summaryrefslogtreecommitdiffstats
path: root/ftp-libs/libtorrent
diff options
context:
space:
mode:
authorMathieu Lonjaret2008-01-31 12:56:30 +0100
committerMathieu Lonjaret2008-01-31 12:56:30 +0100
commitc1a09c88a3e4a3f94be558c0a70421e9a869d812 (patch)
tree02131cc639f6e414832c2a5f30275d3950c4f340 /ftp-libs/libtorrent
parentb3086df053710b40e9699fcfcc57692aa963bce7 (diff)
libtorrent: dht support now built-in
Diffstat (limited to 'ftp-libs/libtorrent')
-rwxr-xr-xftp-libs/libtorrent/CONFIGURE3
-rw-r--r--ftp-libs/libtorrent/HISTORY4
-rwxr-xr-xftp-libs/libtorrent/PRE_BUILD5
-rwxr-xr-xftp-libs/libtorrent/PRE_SUB_DEPENDS6
-rwxr-xr-xftp-libs/libtorrent/SUB_DEPENDS4
-rw-r--r--ftp-libs/libtorrent/dht.diff5112
6 files changed, 4 insertions, 5130 deletions
diff --git a/ftp-libs/libtorrent/CONFIGURE b/ftp-libs/libtorrent/CONFIGURE
deleted file mode 100755
index 9f71ec9299..0000000000
--- a/ftp-libs/libtorrent/CONFIGURE
+++ /dev/null
@@ -1,3 +0,0 @@
-# dht patch disabled for the moment
-#config_query DHT_PATCH "Patch to support dht?" "n"
-
diff --git a/ftp-libs/libtorrent/HISTORY b/ftp-libs/libtorrent/HISTORY
index 1e0d8b5a42..ed914085e6 100644
--- a/ftp-libs/libtorrent/HISTORY
+++ b/ftp-libs/libtorrent/HISTORY
@@ -1,4 +1,8 @@
2008-01-31 Mathieu Lonjaret <lejatorn@sourcemage.org>
+ * PRE_BUILD, rm CONFIGURE, dht.diff, PRE_SUB_DEPENDS, and SUB_DEPENDS
+ because dht support now built-in.
+
+2008-01-31 Mathieu Lonjaret <lejatorn@sourcemage.org>
* DETAILS: update to 0.12.0
2007-10-26 Mathieu Lonjaret <lejatorn@sourcemage.org>
diff --git a/ftp-libs/libtorrent/PRE_BUILD b/ftp-libs/libtorrent/PRE_BUILD
index 30a7a06bfd..8d44878599 100755
--- a/ftp-libs/libtorrent/PRE_BUILD
+++ b/ftp-libs/libtorrent/PRE_BUILD
@@ -2,9 +2,4 @@ default_pre_build &&
cd ${SOURCE_DIRECTORY} &&
# fix problems on amd64 with libtool versions (bug #13943)
./autogen.sh
-# dht patch, commented until new patch comes out for this version
-#if [[ ${DHT_PATCH} != n ]]; then
-# cp ${SPELL_DIRECTORY}/dht.diff ${SOURCE_DIRECTORY}/dht.diff &&
-# patch -p1 < dht.diff
-#fi
diff --git a/ftp-libs/libtorrent/PRE_SUB_DEPENDS b/ftp-libs/libtorrent/PRE_SUB_DEPENDS
deleted file mode 100755
index ccf10cacdb..0000000000
--- a/ftp-libs/libtorrent/PRE_SUB_DEPENDS
+++ /dev/null
@@ -1,6 +0,0 @@
-case "$THIS_SUB_DEPENDS" in
- DHT) if [[ $DHT_PATCH == y ]] ; then return 0
- else return 1
- fi ;;
- *) echo "bogus sub_depends: libtorrent $THIS_SUB_DEPENDS"; return 1;;
-esac
diff --git a/ftp-libs/libtorrent/SUB_DEPENDS b/ftp-libs/libtorrent/SUB_DEPENDS
deleted file mode 100755
index c12b35d4a1..0000000000
--- a/ftp-libs/libtorrent/SUB_DEPENDS
+++ /dev/null
@@ -1,4 +0,0 @@
-case "$THIS_SUB_DEPENDS" in
- DHT) DHT_PATCH="y" ;;
- *) echo "bogus sub_depends: libtorrent $THIS_SUB_DEPENDS"; return 1;;
-esac
diff --git a/ftp-libs/libtorrent/dht.diff b/ftp-libs/libtorrent/dht.diff
deleted file mode 100644
index 12bac8a12e..0000000000
--- a/ftp-libs/libtorrent/dht.diff
+++ /dev/null
@@ -1,5112 +0,0 @@
-Index: libtorrent/scripts/checks.m4
-===================================================================
---- libtorrent/scripts/checks.m4 (revision 975)
-+++ libtorrent/scripts/checks.m4 (working copy)
-@@ -289,7 +289,27 @@
- ])
- ])
-
-+AC_DEFUN([TORRENT_CHECK_TR1], [
-+ AC_LANG_PUSH(C++)
-+ AC_MSG_CHECKING(for TR1 support)
-
-+ AC_COMPILE_IFELSE(
-+ [[#include <tr1/unordered_map>
-+ class Foo;
-+ typedef std::tr1::unordered_map<Foo*, int> Bar;
-+ ]],
-+ [
-+ AC_MSG_RESULT(yes)
-+ AC_DEFINE(HAVE_TR1, 1, Define to 1 if your C++ library supports the extensions from Technical Report 1)
-+ ],
-+ [
-+ AC_MSG_RESULT(no)
-+ ]
-+ )
-+
-+ AC_LANG_POP(C++)
-+])
-+
- AC_DEFUN([TORRENT_WITH_FASTCGI], [
- AC_ARG_WITH(fastcgi,
- [ --with-fastcgi=PATH Enable FastCGI RPC support. (DO NOT USE)],
-Index: libtorrent/scripts/common.m4
-===================================================================
---- libtorrent/scripts/common.m4 (revision 975)
-+++ libtorrent/scripts/common.m4 (working copy)
-@@ -239,3 +239,18 @@
- fi
- ])
- ])
-+
-+AC_DEFUN([TORRENT_ENABLE_TR1], [
-+ AC_ARG_ENABLE(std_tr1,
-+ [ --disable-std_tr1 disable check for support for TR1 [[default=enable]]],
-+ [
-+ if test "$enableval" = "yes"; then
-+ TORRENT_CHECK_TR1()
-+ else
-+ AC_MSG_CHECKING(for TR1 support)
-+ AC_MSG_RESULT(disabled)
-+ fi
-+ ],[
-+ TORRENT_CHECK_TR1()
-+ ])
-+])
-Index: libtorrent/src/dht/dht_tracker.h
-===================================================================
---- libtorrent/src/dht/dht_tracker.h (revision 0)
-+++ libtorrent/src/dht/dht_tracker.h (revision 0)
-@@ -0,0 +1,84 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_DHT_TORRENT_H
-+#define LIBTORRENT_DHT_TORRENT_H
-+
-+#include "globals.h"
-+
-+#include <vector>
-+#include <rak/socket_address.h>
-+
-+#include "download/download_info.h" // for SocketAddressCompact
-+
-+namespace torrent {
-+
-+class Object;
-+
-+// Container for peers tracked in a torrent.
-+
-+class DhtTracker : private std::vector<SocketAddressCompact> {
-+public:
-+ typedef std::vector<SocketAddressCompact> base_type;
-+
-+ // Maximum number of peers we return for a GET_PEERS query (default value).
-+ // Needs to be small enough so that a packet with a payload of num_peers*6 bytes
-+ // does not need fragmentation. Value chosen so that the size is approximately
-+ // equal to a FIND_NODE reply (8*26 bytes).
-+ static const unsigned int max_peers = 32;
-+
-+ // Maximum number of peers we keep track of. For torrents with more peers,
-+ // replace random peers with new announces to avoid excessively large peer
-+ // tables for active torrents.
-+ static const unsigned int max_size = 128;
-+
-+ using base_type::empty;
-+ using base_type::size;
-+
-+ void add_peer(const rak::socket_address* sa);
-+ std::string get_peers(unsigned int maxPeers = max_peers);
-+
-+ // Remove old announces from the tracker that have not reannounced for
-+ // more than the given number of prune calls.
-+ void prune(uint32_t maxAge);
-+
-+private:
-+ std::vector<uint32_t> m_lastSeen;
-+};
-+
-+}
-+
-+#endif
-Index: libtorrent/src/dht/dht_bucket.cc
-===================================================================
---- libtorrent/src/dht/dht_bucket.cc (revision 0)
-+++ libtorrent/src/dht/dht_bucket.cc (revision 0)
-@@ -0,0 +1,191 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+
-+#include "torrent/exceptions.h"
-+
-+#include "dht_bucket.h"
-+#include "dht_node.h"
-+
-+namespace torrent {
-+
-+DhtBucket::DhtBucket(const HashString& begin, const HashString& end) :
-+ m_parent(NULL),
-+ m_child(NULL),
-+
-+ m_lastChanged(cachedTime.seconds()),
-+
-+ m_good(0),
-+ m_bad(0),
-+
-+ m_begin(begin),
-+ m_end(end) {
-+
-+ reserve(num_nodes);
-+}
-+
-+void
-+DhtBucket::add_node(DhtNode* n) {
-+ push_back(n);
-+ touch();
-+
-+ if (n->is_good())
-+ m_good++;
-+ else if (n->is_bad())
-+ m_bad++;
-+}
-+
-+void
-+DhtBucket::remove_node(DhtNode* n) {
-+ iterator itr = std::find_if(begin(), end(), std::bind2nd(std::equal_to<DhtNode*>(), n));
-+ if (itr == end())
-+ throw internal_error("DhtBucket::remove_node called for node not in bucket.");
-+
-+ erase(itr);
-+
-+ if (n->is_good())
-+ m_good--;
-+ else if (n->is_bad())
-+ m_bad--;
-+}
-+
-+void
-+DhtBucket::count() {
-+ m_good = std::count_if(begin(), end(), std::mem_fun(&DhtNode::is_good));
-+ m_bad = std::count_if(begin(), end(), std::mem_fun(&DhtNode::is_bad));
-+}
-+
-+// Called every 15 minutes for housekeeping.
-+void
-+DhtBucket::update() {
-+ // For now we only update the counts after some nodes have become bad
-+ // due to prolonged inactivity.
-+ count();
-+}
-+
-+DhtBucket::iterator
-+DhtBucket::find_replacement_candidate(bool onlyOldest) {
-+ iterator oldest = end();
-+ unsigned int oldestTime = std::numeric_limits<unsigned int>::max();
-+
-+ for (iterator itr = begin(); itr != end(); ++itr) {
-+ if ((*itr)->is_bad() && !onlyOldest)
-+ return itr;
-+
-+ if ((*itr)->last_seen() < oldestTime) {
-+ oldestTime = (*itr)->last_seen();
-+ oldest = itr;
-+ }
-+ }
-+
-+ return oldest;
-+}
-+
-+void
-+DhtBucket::get_mid_point(HashString* middle) const {
-+ *middle = m_end;
-+
-+ for (unsigned int i=0; i<m_begin.size(); i++)
-+ if (m_begin[i] != m_end[i]) {
-+ (*middle)[i] = ((uint8_t)m_begin[i] + (uint8_t)m_end[i]) / 2;
-+ break;
-+ }
-+}
-+
-+void
-+DhtBucket::get_random_id(HashString* rand_id) const {
-+
-+ // Generate a random ID between m_begin and m_end.
-+ // Since m_end - m_begin = 2^n - 1, we can do a bitwise AND operation.
-+ for (unsigned int i=0; i<(*rand_id).size(); i++)
-+ (*rand_id)[i] = m_begin[i] + (random() & (m_end[i] - m_begin[i]));
-+
-+#ifdef USE_EXTRA_DEBUG
-+ if (!is_in_range(*rand_id))
-+ throw internal_error("DhtBucket::get_random_id generated an out-of-range ID.");
-+#endif
-+}
-+
-+DhtBucket*
-+DhtBucket::split(const HashString& id) {
-+ HashString mid_range;
-+ get_mid_point(&mid_range);
-+
-+ DhtBucket* other = new DhtBucket(m_begin, mid_range);
-+
-+ // Set m_begin = mid_range + 1
-+ int carry = 1;
-+ for (unsigned int i = mid_range.size(); i>0; i--) {
-+ unsigned int sum = (uint8_t)mid_range[i-1] + carry;
-+ m_begin[i-1] = (uint8_t)sum;
-+ carry = sum >> 8;
-+ }
-+
-+ // Move nodes over to other bucket if they fall in its range, then
-+ // delete them from this one.
-+ iterator split = std::partition(begin(), end(), std::bind2nd(std::mem_fun(&DhtNode::is_in_range), this));
-+ other->insert(other->end(), split, end());
-+ std::for_each(other->begin(), other->end(), std::bind2nd(std::mem_fun(&DhtNode::set_bucket), other));
-+ erase(split, end());
-+
-+ other->set_time(m_lastChanged);
-+ other->count();
-+
-+ count();
-+
-+ // Maintain child (adjacent narrower bucket) and parent (adjacent wider bucket)
-+ // so that given router ID is in child.
-+ if (other->is_in_range(id)) {
-+ // Make other become our new child.
-+ m_child = other;
-+ other->m_parent = this;
-+
-+ } else {
-+ // We become other's child, other becomes our parent's child.
-+ if (parent()) {
-+ parent()->m_child = other;
-+ other->m_parent = parent();
-+ }
-+
-+ m_parent = other;
-+ other->m_child = this;
-+ }
-+
-+ return other;
-+}
-+
-+}
-Index: libtorrent/src/dht/dht_bucket.h
-===================================================================
---- libtorrent/src/dht/dht_bucket.h (revision 0)
-+++ libtorrent/src/dht/dht_bucket.h (revision 0)
-@@ -0,0 +1,183 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_DHT_BUCKET_H
-+#define LIBTORRENT_DHT_BUCKET_H
-+
-+#include <list>
-+
-+#include "globals.h"
-+
-+#include "torrent/hash_string.h"
-+
-+namespace torrent {
-+
-+class DhtNode;
-+
-+// A container holding a small number of nodes that fall in a given binary
-+// partition of the 160-bit ID space (i.e. the range ID1..ID2 where ID2-ID1+1 is
-+// a power of 2.)
-+class DhtBucket : private std::vector<DhtNode*> {
-+public:
-+ static const unsigned int num_nodes = 8;
-+
-+ typedef std::vector<DhtNode*> base_type;
-+
-+ using base_type::const_iterator;
-+ using base_type::iterator;
-+
-+ using base_type::begin;
-+ using base_type::end;
-+ using base_type::size;
-+ using base_type::empty;
-+
-+ DhtBucket(const HashString& begin, const HashString& end);
-+
-+ // Add new node. Does NOT set node's bucket automatically (to allow adding a
-+ // node to multiple buckets, with only one "main" bucket.)
-+ void add_node(DhtNode* n);
-+
-+ void remove_node(DhtNode* n);
-+
-+ // Bucket's ID range functions.
-+ const HashString& id_range_begin() const { return m_begin; }
-+ HashString& id_range_begin() { return m_begin; }
-+ const HashString& id_range_end() const { return m_end; }
-+ HashString& id_range_end() { return m_end; }
-+
-+ bool is_in_range(const HashString& id) const { return m_begin <= id && id <= m_end; }
-+
-+ // Find middle or random ID in bucket.
-+ void get_mid_point(HashString* middle) const;
-+ void get_random_id(HashString* rand_id) const;
-+
-+ // Node counts and bucket stats.
-+ bool is_full() const { return size() >= num_nodes; }
-+ bool has_space() const { return !is_full() || num_bad() > 0; }
-+ unsigned int num_good() const { return m_good; }
-+ unsigned int num_bad() const { return m_bad; }
-+
-+ unsigned int age() const { return cachedTime.seconds() - m_lastChanged; }
-+ void touch() { m_lastChanged = cachedTime.seconds(); }
-+ void set_time(int time) { m_lastChanged = time; }
-+
-+ // Called every 15 minutes after updating nodes.
-+ void update();
-+
-+ // Return candidate for replacement (a bad node or the oldest node); may
-+ // return end() unless has_space() is true.
-+ iterator find_replacement_candidate(bool onlyOldest = false);
-+
-+ // Split the bucket in two and redistribute nodes. Returned bucket is the
-+ // lower half, "this" bucket keeps the upper half. Sets parent/child so
-+ // that the bucket the given ID falls in is the child.
-+ DhtBucket* split(const HashString& id);
-+
-+ // Parent and child buckets. Parent is the adjacent bucket with double the
-+ // ID width, child the adjacent bucket with half the width (except the very
-+ // last child which has the same width.)
-+ DhtBucket* parent() const { return m_parent; }
-+ DhtBucket* child() const { return m_child; }
-+
-+ // Called by the DhtNode on its bucket to update good/bad node counts.
-+ void node_now_good(bool was_bad);
-+ void node_now_bad(bool was_good);
-+
-+private:
-+ void count();
-+
-+ DhtBucket* m_parent;
-+ DhtBucket* m_child;
-+
-+ unsigned int m_lastChanged;
-+
-+ unsigned int m_good;
-+ unsigned int m_bad;
-+
-+ // These are 40 bytes together, so might as well put them last.
-+ HashString m_begin;
-+ HashString m_end;
-+};
-+
-+// Helper class to recursively follow a chain of buckets. It first recurses
-+// into the bucket's children since they are by definition closer to the bucket,
-+// then continues with the bucket's parents.
-+class DhtBucketChain {
-+public:
-+ DhtBucketChain(const DhtBucket* b) : m_restart(b), m_cur(b) { }
-+
-+ const DhtBucket* bucket() { return m_cur; }
-+ const DhtBucket* next();
-+
-+private:
-+ const DhtBucket* m_restart;
-+ const DhtBucket* m_cur;
-+};
-+
-+inline void
-+DhtBucket::node_now_good(bool was_bad) {
-+ m_bad -= was_bad;
-+ m_good++;
-+}
-+
-+inline void
-+DhtBucket::node_now_bad(bool was_good) {
-+ m_good -= was_good;
-+ m_bad++;
-+}
-+
-+inline const DhtBucket*
-+DhtBucketChain::next() {
-+ // m_restart is clear when we're done recursing into the children and
-+ // follow the parents instead.
-+ if (m_restart == NULL) {
-+ m_cur = m_cur->parent();
-+
-+ } else {
-+ m_cur = m_cur->child();
-+
-+ if (!m_cur) {
-+ m_cur = m_restart->parent();
-+ m_restart = NULL;
-+ }
-+ }
-+
-+ return m_cur;
-+}
-+
-+}
-+
-+#endif
-Index: libtorrent/src/dht/dht_router.cc
-===================================================================
---- libtorrent/src/dht/dht_router.cc (revision 0)
-+++ libtorrent/src/dht/dht_router.cc (revision 0)
-@@ -0,0 +1,646 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+#include "globals.h"
-+
-+#include <sstream>
-+#include <rak/functional.h>
-+#include <rak/string_manip.h>
-+
-+#include "torrent/dht_manager.h"
-+#include "torrent/exceptions.h"
-+#include "utils/sha1.h"
-+#include "manager.h"
-+
-+#include "dht_bucket.h"
-+#include "dht_router.h"
-+#include "dht_tracker.h"
-+#include "dht_transaction.h"
-+
-+namespace torrent {
-+
-+HashString DhtRouter::zero_id;
-+
-+DhtRouter::DhtRouter(const Object& cache, const rak::socket_address* sa) :
-+ DhtNode(zero_id, sa), // actual ID is set later
-+ m_server(this),
-+ m_contacts(NULL),
-+ m_numRefresh(0),
-+ m_curToken(random()),
-+ m_prevToken(random()) {
-+
-+ HashString ones_id;
-+
-+ zero_id.clear();
-+ ones_id.clear(0xFF);
-+
-+ if (cache.has_key("self_id")) {
-+ assign(cache.get_key_string("self_id").c_str());
-+
-+ } else {
-+ long buffer[size_data];
-+
-+ for (long* itr = buffer; itr != buffer + size_data; ++itr)
-+ *itr = random();
-+
-+ Sha1 sha;
-+ sha.init();
-+ sha.update(buffer, sizeof(buffer));
-+ sha.final_c(data());
-+ }
-+
-+ set_bucket(new DhtBucket(zero_id, ones_id));
-+ m_routingTable.insert(std::make_pair(bucket()->id_range_end(), bucket()));
-+
-+ if (cache.has_key("nodes")) {
-+ Object::map_type nodes = cache.get_key_map("nodes");
-+
-+ for (Object::map_type::const_iterator itr = nodes.begin(); itr != nodes.end(); ++itr) {
-+ if (itr->first.length() != HashString::size_data)
-+ throw bencode_error("Loading cache: Invalid node hash.");
-+
-+ add_node_to_bucket(m_nodes.insert(new DhtNode(itr->first, itr->second)));
-+ }
-+ }
-+
-+ if (m_nodes.size() < num_bootstrap_complete)
-+ m_contacts = new std::list<rak::socket_address>;
-+}
-+
-+DhtRouter::~DhtRouter() {
-+ stop();
-+ std::for_each(m_routingTable.begin(), m_routingTable.end(), rak::on(rak::mem_ref(&DhtBucketList::value_type::second), rak::call_delete<DhtBucket>()));
-+ std::for_each(m_trackers.begin(), m_trackers.end(), rak::on(rak::mem_ref(&DhtTrackerList::value_type::second), rak::call_delete<DhtTracker>()));
-+ std::for_each(m_nodes.begin(), m_nodes.end(), rak::on(rak::mem_ref(&DhtNodeList::value_type::second), rak::call_delete<DhtNode>()));
-+}
-+
-+void
-+DhtRouter::start(int port) {
-+ m_server.start(port);
-+
-+ // Set timeout slot and schedule it to be called immediately for initial bootstrapping if necessary.
-+ m_taskTimeout.set_slot(rak::mem_fn(this, &DhtRouter::receive_timeout_bootstrap));
-+ priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(1)).round_seconds());
-+}
-+
-+void
-+DhtRouter::stop() {
-+ priority_queue_erase(&taskScheduler, &m_taskTimeout);
-+ m_server.stop();
-+}
-+
-+// Start a DHT get_peers and announce_peer request.
-+void
-+DhtRouter::announce(DownloadInfo* info, TrackerDht* tracker) {
-+ m_server.announce(*find_bucket(info->hash())->second, info->hash(), tracker);
-+}
-+
-+// Cancel any running requests from the given tracker.
-+// If info is not NULL, only cancel those requests.
-+void
-+DhtRouter::cancel_announce(DownloadInfo* info, const TrackerDht* tracker) {
-+ m_server.cancel_announce(info, tracker);
-+}
-+
-+DhtTracker*
-+DhtRouter::get_tracker(const HashString& hash, bool create) {
-+ DhtTrackerList::accessor itr = m_trackers.find(hash);
-+
-+ if (itr == m_trackers.end()) {
-+ if (!create)
-+ return NULL;
-+
-+ std::pair<DhtTrackerList::accessor, bool> res = m_trackers.insert(std::make_pair(hash, new DhtTracker()));
-+
-+ if (!res.second)
-+ throw internal_error("DhtRouter::get_tracker did not actually insert tracker.");
-+
-+ return res.first.tracker();
-+ }
-+
-+ return itr.tracker();
-+}
-+
-+bool
-+DhtRouter::want_node(const HashString& id) {
-+ // We don't want to add ourself. Also, too many broken implementations
-+ // advertise an ID of 0, which causes collisions, so reject that.
-+ if (id == this->id() || id == zero_id)
-+ return false;
-+
-+ // We are always interested in more nodes for our own bucket (causing it
-+ // to be split if full); in other buckets only if there's space.
-+ DhtBucket* b = find_bucket(id)->second;
-+ return b == bucket() || b->has_space();
-+}
-+
-+DhtNode*
-+DhtRouter::get_node(const HashString& id) {
-+ DhtNodeList::accessor itr = m_nodes.find(&id);
-+
-+ if (itr == m_nodes.end()) {
-+ if (id == this->id())
-+ return this;
-+ else
-+ return NULL;
-+ }
-+
-+ return itr.node();
-+}
-+
-+DhtRouter::DhtBucketList::iterator
-+DhtRouter::find_bucket(const HashString& id) {
-+ DhtBucketList::iterator itr = m_routingTable.upper_bound(id);
-+
-+#ifdef USE_EXTRA_DEBUG
-+ if (itr == m_routingTable.end())
-+ throw internal_error("DHT Buckets not covering entire ID space.");
-+
-+ if (!itr->second->is_in_range(id))
-+ throw internal_error("DhtRouter::find_bucket, m_routingTable.upper_bound did not find correct bucket.");
-+#endif
-+
-+ return itr;
-+}
-+
-+void
-+DhtRouter::contact(const rak::socket_address* sa, bool external) {
-+ // For non-external nodes, e.g. those from an explict add_node command
-+ // or found via the BT PORT message, contact them immediately if possible.
-+ if (!external) {
-+ if (is_active())
-+ m_server.ping(zero_id, sa);
-+
-+ } else {
-+ // Externally obtained nodes are added to the contact list, but only if
-+ // we're still bootstrapping. We don't contact external nodes after that.
-+ if (m_contacts != NULL)
-+ m_contacts->push_back(*sa);
-+ }
-+}
-+
-+// Received a query from the given node. If it has previously replied
-+// to one of our queries, consider it alive and update the bucket mtime,
-+// otherwise if we could use it in a bucket, try contacting it.
-+DhtNode*
-+DhtRouter::node_queried(const HashString& id, const rak::socket_address* sa) {
-+ DhtNode* node = get_node(id);
-+
-+ if (node == NULL) {
-+ if (want_node(id))
-+ m_server.ping(id, sa);
-+
-+ return NULL;
-+ }
-+
-+ // If we know the ID but the address is different, don't set the original node
-+ // active, but neither use this new address to prevent rogue nodes from polluting
-+ // our routing table with fake source addresses.
-+ if (node->address()->sa_inet()->address_n() != sa->sa_inet()->address_n())
-+ return NULL;
-+
-+ node->queried();
-+ if (node->is_good())
-+ node->bucket()->touch();
-+
-+ return node;
-+}
-+
-+// Received a reply from a node we queried.
-+// Check that it matches the information we have, set that it has replied
-+// and update the bucket mtime.
-+DhtNode*
-+DhtRouter::node_replied(const HashString& id, const rak::socket_address* sa) {
-+ DhtNode* node = get_node(id);
-+
-+ if (node == NULL) {
-+ // New node, create it. It's a good node (it replied!) so add it to a bucket.
-+ if (id == this->id() || id == zero_id)
-+ return NULL;
-+
-+ node = m_nodes.insert(new DhtNode(id, sa));
-+
-+ if (!add_node_to_bucket(node)) // deletes the node if it fails
-+ return NULL;
-+ }
-+
-+ if (node->address()->sa_inet()->address_n() != sa->sa_inet()->address_n())
-+ return NULL;
-+
-+ node->replied();
-+ node->bucket()->touch();
-+
-+ return node;
-+}
-+
-+// A node has not replied to one of our queries.
-+DhtNode*
-+DhtRouter::node_inactive(const HashString& id, const rak::socket_address* sa) {
-+ DhtNodeList::accessor itr = m_nodes.find(&id);
-+
-+ // If not found add it to some blacklist so we won't try contacting it again immediately?
-+ if (itr == m_nodes.end())
-+ return NULL;
-+
-+ // Check source address. Normally node_inactive is called if we DON'T receive a reply,
-+ // however it can also be called if a node replied with an malformed response packet,
-+ // so check that the address matches so that a rogue node cannot cause other nodes
-+ // to be considered bad by sending malformed packets.
-+ if (itr.node()->address()->sa_inet()->address_n() != sa->sa_inet()->address_n())
-+ return NULL;
-+
-+ itr.node()->inactive();
-+
-+ // Old node age normally implies no replies for many consecutive queries, however
-+ // after loading the node cache after a day or more we want to give each node a few
-+ // chances to reply again instead of removing all nodes instantly.
-+ if (itr.node()->is_bad() && itr.node()->age() >= timeout_remove_node) {
-+ delete_node(itr);
-+ return NULL;
-+ }
-+
-+ return itr.node();
-+}
-+
-+// We sent a query to the given node ID, but received a reply from a different
-+// node ID, that means the address of the original ID is invalid now.
-+void
-+DhtRouter::node_invalid(const HashString& id) {
-+ DhtNode* node = get_node(id);
-+
-+ if (node == NULL || node == this)
-+ return;
-+
-+ delete_node(m_nodes.find(&node->id()));
-+}
-+
-+char*
-+DhtRouter::store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd) {
-+ DhtBucketChain chain(find_bucket(id)->second);
-+
-+ do {
-+ for (DhtBucket::const_iterator itr = chain.bucket()->begin(); itr != chain.bucket()->end(); ++itr) {
-+ if (!(*itr)->is_bad()) {
-+ buffer = (*itr)->store_compact(buffer);
-+
-+ if (buffer > bufferEnd)
-+ throw internal_error("DhtRouter::store_closest_nodes wrote past buffer end.");
-+
-+ if (buffer == bufferEnd)
-+ break;
-+ }
-+ }
-+ } while (buffer != bufferEnd && chain.next());
-+
-+ return buffer;
-+}
-+
-+Object*
-+DhtRouter::store_cache(Object* container) const {
-+ container->insert_key("self_id", str());
-+
-+ // Insert all nodes.
-+ Object& nodes = container->insert_key("nodes", Object(Object::TYPE_MAP));
-+ for (DhtNodeList::const_accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr) {
-+ if (!itr.node()->is_bad())
-+ itr.node()->store_cache(&nodes.insert_key(itr.id().str(), Object(Object::TYPE_MAP)));
-+ }
-+
-+ return container;
-+}
-+
-+DhtManager::statistics_type
-+DhtRouter::get_statistics() const {
-+ DhtManager::statistics_type stats(*m_server.upload_throttle()->rate(), *m_server.download_throttle()->rate());
-+
-+ if (!m_server.is_active())
-+ stats.cycle = 0;
-+ else if (m_numRefresh < 2) // still bootstrapping
-+ stats.cycle = 1;
-+ else
-+ stats.cycle = m_numRefresh;
-+
-+ stats.queries_received = m_server.queries_received();
-+ stats.queries_sent = m_server.queries_sent();
-+ stats.replies_received = m_server.replies_received();
-+
-+ stats.num_nodes = m_nodes.size();
-+ stats.num_buckets = m_routingTable.size();
-+
-+ stats.num_peers = 0;
-+ stats.max_peers = 0;
-+ stats.num_trackers = m_trackers.size();
-+
-+ for (DhtTrackerList::const_accessor itr = m_trackers.begin(); itr != m_trackers.end(); ++itr) {
-+ unsigned int peers = itr.tracker()->size();
-+ stats.num_peers += peers;
-+ stats.max_peers = std::max(peers, stats.max_peers);
-+ }
-+
-+ return stats;
-+}
-+
-+void
-+DhtRouter::receive_timeout_bootstrap() {
-+ // If we're still bootstrapping, restart the process every 60 seconds until
-+ // we have enough nodes in our routing table. After we have 32 nodes, we switch
-+ // to a less aggressive non-bootstrap mode of collecting nodes that contact us
-+ // and through doing normal torrent announces.
-+ if (m_nodes.size() < num_bootstrap_complete) {
-+ if (m_contacts == NULL)
-+ throw internal_error("DhtRouter::receive_timeout_bootstrap called without contact list.");
-+
-+ if (!m_nodes.empty() || !m_contacts->empty())
-+ bootstrap();
-+
-+ // Retry in 60 seconds.
-+ priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(timeout_bootstrap_retry)).round_seconds());
-+ m_numRefresh = 1; // still bootstrapping
-+
-+ } else {
-+ // We won't be needing external contacts after this.
-+ if (m_contacts != NULL) {
-+ delete m_contacts;
-+ m_contacts = NULL;
-+ }
-+
-+ m_taskTimeout.set_slot(rak::mem_fn(this, &DhtRouter::receive_timeout));
-+
-+ if (!m_numRefresh) {
-+ // If we're still in the startup, do the usual refreshing too.
-+ receive_timeout();
-+
-+ } else {
-+ // Otherwise just set the 15 minute timer.
-+ priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(timeout_update)).round_seconds());
-+ }
-+
-+ m_numRefresh = 2;
-+ }
-+}
-+
-+void
-+DhtRouter::receive_timeout() {
-+ priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(timeout_update)).round_seconds());
-+
-+ m_prevToken = m_curToken;
-+ m_curToken = random();
-+
-+ // Do some periodic accounting, refreshing buckets and marking
-+ // bad nodes.
-+
-+ // Update nodes.
-+ for (DhtNodeList::accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr) {
-+ if (!itr.node()->bucket())
-+ throw internal_error("DhtRouter::receive_timeout has node without bucket.");
-+
-+ itr.node()->update();
-+
-+ // Try contacting nodes we haven't received anything from for a while.
-+ // Don't contact repeatedly unresponsive nodes; we keep them in case they
-+ // do send a query, until we find a better node. However, give it a last
-+ // chance just before deleting it.
-+ if (itr.node()->is_questionable() && (!itr.node()->is_bad() || itr.node()->age() >= timeout_remove_node))
-+ m_server.ping(itr.node()->id(), itr.node()->address());
-+ }
-+
-+ // If bucket isn't full yet or hasn't received replies/queries from
-+ // its nodes for a while, try to find new nodes now.
-+ for (DhtBucketList::const_iterator itr = m_routingTable.begin(); itr != m_routingTable.end(); ++itr) {
-+ itr->second->update();
-+
-+ if (!itr->second->is_full() || itr->second->age() > timeout_bucket_bootstrap)
-+ bootstrap_bucket(itr->second);
-+ }
-+
-+ // Remove old peers and empty torrents from the tracker.
-+ for (DhtTrackerList::accessor itr = m_trackers.begin(); itr != m_trackers.end(); ) {
-+ itr.tracker()->prune(timeout_peer_announce);
-+
-+ if (itr.tracker()->empty()) {
-+ delete itr.tracker();
-+ m_trackers.erase(itr++);
-+
-+ } else {
-+ ++itr;
-+ }
-+ }
-+
-+ m_server.update();
-+
-+ m_numRefresh++;
-+}
-+
-+void
-+DhtRouter::generate_token(const rak::socket_address* sa, int token, char buffer[20]) {
-+ Sha1 sha;
-+ uint32_t key = sa->sa_inet()->address_n();
-+
-+ sha.init();
-+ sha.update(&token, sizeof(token));
-+ sha.update(&key, 4);
-+ sha.final_c(buffer);
-+}
-+
-+std::string
-+DhtRouter::make_token(const rak::socket_address* sa) {
-+ char token[20];
-+
-+ generate_token(sa, m_curToken, token);
-+
-+ return std::string(token, size_token);
-+}
-+
-+bool
-+DhtRouter::token_valid(const std::string& token, const rak::socket_address* sa) {
-+ if (token.length() != size_token)
-+ return false;
-+
-+ // Compare given token to the reference token.
-+ char reference[20];
-+
-+ // First try current token.
-+ generate_token(sa, m_curToken, reference);
-+
-+ if (std::memcmp(reference, token.c_str(), size_token) == 0)
-+ return true;
-+
-+ // If token recently changed, some clients may be using the older one.
-+ // That way a token is valid for 15-30 minutes, instead of 0-15.
-+ generate_token(sa, m_prevToken, reference);
-+
-+ return std::memcmp(reference, token.c_str(), size_token) == 0;
-+}
-+
-+DhtNode*
-+DhtRouter::find_node(const rak::socket_address* sa) {
-+ for (DhtNodeList::accessor itr = m_nodes.begin(); itr != m_nodes.end(); ++itr)
-+ if (itr.node()->address()->sa_inet()->address_n() == sa->sa_inet()->address_n())
-+ return itr.node();
-+
-+ return NULL;
-+}
-+
-+DhtRouter::DhtBucketList::iterator
-+DhtRouter::split_bucket(const DhtBucketList::iterator& itr, DhtNode* node) {
-+ // Split bucket. Current bucket keeps the upper half thus keeping the
-+ // map key valid, new bucket is the lower half of the original bucket.
-+ DhtBucket* newBucket = itr->second->split(id());
-+
-+ // If our bucket has a child now (the new bucket), move ourself into it.
-+ if (bucket()->child() != NULL)
-+ set_bucket(bucket()->child());
-+
-+ if (!bucket()->is_in_range(id()))
-+ throw internal_error("DhtRouter::split_bucket router ID ended up in wrong bucket.");
-+
-+ // Insert new bucket with iterator hint = just before current bucket.
-+ DhtBucketList::iterator other = m_routingTable.insert(itr, std::make_pair(newBucket->id_range_end(), newBucket));
-+
-+ // Check that the bucket we're not adding the node to isn't empty.
-+ if (other->second->is_in_range(node->id())) {
-+ if (itr->second->empty())
-+ bootstrap_bucket(itr->second);
-+
-+ } else {
-+ if (other->second->empty())
-+ bootstrap_bucket(other->second);
-+
-+ other = itr;
-+ }
-+
-+ return other;
-+}
-+
-+bool
-+DhtRouter::add_node_to_bucket(DhtNode* node) {
-+ DhtBucketList::iterator itr = find_bucket(node->id());
-+
-+ while (itr->second->is_full()) {
-+ // Bucket is full. If there are any bad nodes, remove the oldest.
-+ DhtBucket::iterator nodeItr = itr->second->find_replacement_candidate();
-+ if (nodeItr == itr->second->end())
-+ throw internal_error("DhtBucket::find_candidate returned no node.");
-+
-+ if ((*nodeItr)->is_bad()) {
-+ delete_node(m_nodes.find(&(*nodeItr)->id()));
-+
-+ } else {
-+ // Bucket is full of good nodes; if our own ID falls in
-+ // range then split the bucket else discard new node.
-+ if (itr->second != bucket()) {
-+ delete_node(m_nodes.find(&node->id()));
-+ return false;
-+ }
-+
-+ itr = split_bucket(itr, node);
-+ }
-+ }
-+
-+ itr->second->add_node(node);
-+ node->set_bucket(itr->second);
-+ return true;
-+}
-+
-+void
-+DhtRouter::delete_node(const DhtNodeList::accessor& itr) {
-+ if (itr == m_nodes.end())
-+ throw internal_error("DhtRouter::delete_node called with invalid iterator.");
-+
-+ if (itr.node()->bucket() != NULL)
-+ itr.node()->bucket()->remove_node(itr.node());
-+
-+ delete itr.node();
-+
-+ m_nodes.erase(itr);
-+}
-+
-+void
-+DhtRouter::bootstrap() {
-+ // Contact up to 8 nodes from the contact list.
-+ for (int count = 0; count < 8 && !m_contacts->empty(); count++) {
-+ if (!find_node(&m_contacts->back()))
-+ m_server.ping(zero_id, &m_contacts->back());
-+
-+ m_contacts->pop_back();
-+ }
-+
-+ // Abort unless we already found some nodes for a search.
-+ if (m_nodes.empty())
-+ return;
-+
-+ bootstrap_bucket(bucket());
-+
-+ // Aggressively ping all questionable nodes in our own bucket to weed
-+ // out bad nodes as early as possible and make room for fresh nodes.
-+ for (DhtBucket::iterator itr = bucket()->begin(); itr != bucket()->end(); ++itr)
-+ if (!(*itr)->is_good())
-+ m_server.ping((*itr)->id(), (*itr)->address());
-+
-+ // Also bootstrap a random bucket, if there are others.
-+ if (m_routingTable.size() < 2)
-+ return;
-+
-+ DhtBucketList::iterator itr = m_routingTable.begin();
-+ std::advance(itr, random() % m_routingTable.size());
-+
-+ if (itr->second != bucket() && itr != m_routingTable.end())
-+ bootstrap_bucket(itr->second);
-+}
-+
-+void
-+DhtRouter::bootstrap_bucket(const DhtBucket* bucket) {
-+ if (!m_server.is_active())
-+ return;
-+
-+ // Do a search for a random ID, or the ID adjacent to our
-+ // own when bootstrapping our own bucket. We don't search for
-+ // our own exact ID to avoid receiving only our own node info
-+ // instead of closest nodes, from nodes that know us already.
-+ HashString contactId;
-+
-+ if (bucket == this->bucket()) {
-+ contactId = id();
-+ contactId[contactId.size() - 1] ^= 1;
-+ } else {
-+ bucket->get_random_id(&contactId);
-+ }
-+
-+ m_server.find_node(*bucket, contactId);
-+}
-+
-+}
-Index: libtorrent/src/dht/dht_router.h
-===================================================================
---- libtorrent/src/dht/dht_router.h (revision 0)
-+++ libtorrent/src/dht/dht_router.h (revision 0)
-@@ -0,0 +1,174 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_DHT_ROUTER_H
-+#define LIBTORRENT_DHT_ROUTER_H
-+
-+#include <rak/priority_queue_default.h>
-+#include <rak/socket_address.h>
-+
-+#include "torrent/dht_manager.h"
-+#include "torrent/hash_string.h"
-+#include "torrent/object.h"
-+
-+#include "dht_node.h"
-+#include "dht_hash_map.h"
-+#include "dht_server.h"
-+
-+namespace torrent {
-+
-+class DhtBucket;
-+class DhtTracker;
-+class TrackerDht;
-+
-+// Main DHT class, maintains the routing table of known nodes and talks to the
-+// DhtServer object that handles the actual communication.
-+
-+class DhtRouter : public DhtNode {
-+public:
-+ // How many bytes to return and verify from the 20-byte SHA token.
-+ static const unsigned int size_token = 8;
-+
-+ static const unsigned int timeout_bootstrap_retry = 60; // Retry initial bootstrapping every minute.
-+ static const unsigned int timeout_update = 15 * 60; // Regular housekeeping updates every 15 minutes.
-+ static const unsigned int timeout_bucket_bootstrap = 15 * 60; // Bootstrap idle buckets after 15 minutes.
-+ static const unsigned int timeout_remove_node = 4 * 60 * 60; // Remove unresponsive nodes after 4 hours.
-+ static const unsigned int timeout_peer_announce = 30 * 60; // Remove peers which haven't reannounced for 30 minutes.
-+
-+ // A node ID of all zero.
-+ static HashString zero_id;
-+
-+ DhtRouter(const Object& cache, const rak::socket_address* sa);
-+ ~DhtRouter();
-+
-+ // Start and stop the router. This starts/stops the UDP server as well.
-+ void start(int port);
-+ void stop();
-+
-+ bool is_active() { return m_server.is_active(); }
-+
-+ // Find peers for given download and announce ourselves.
-+ void announce(DownloadInfo* info, TrackerDht* tracker);
-+
-+ // Cancel any pending transactions related to the given download (or all if NULL).
-+ void cancel_announce(DownloadInfo* info, const TrackerDht* tracker);
-+
-+ // Retrieve tracked torrent for the hash.
-+ // Returns NULL if not tracking the torrent unless create is true.
-+ DhtTracker* get_tracker(const HashString& hash, bool create);
-+
-+ // Check if we are interested in inserting a new node of the given ID
-+ // into our table (i.e. if we have space or bad nodes in the corresponding bucket).
-+ bool want_node(const HashString& id);
-+
-+ // Contact the given node, or add it to the list of potential contacts
-+ // if it was obtained externally from a .torrent file for example.
-+ void contact(const rak::socket_address* sa, bool external);
-+
-+ // Retrieve node of given ID in constant time. Return NULL if not found, unless
-+ // it's our own ID in which case it returns the DhtRouter object.
-+ DhtNode* get_node(const HashString& id);
-+
-+ // Search for node with given address in O(n), disregarding the port.
-+ DhtNode* find_node(const rak::socket_address* sa);
-+
-+ // Whenever a node queries us, replies, or is confirmed inactive (no reply) or
-+ // invalid (reply with wrong ID), we need to update its status.
-+ DhtNode* node_queried(const HashString& id, const rak::socket_address* sa);
-+ DhtNode* node_replied(const HashString& id, const rak::socket_address* sa);
-+ DhtNode* node_inactive(const HashString& id, const rak::socket_address* sa);
-+ void node_invalid(const HashString& id);
-+
-+ // Store compact node information (26 bytes) for nodes closest to the
-+ // given ID in the given buffer, return new buffer end.
-+ char* store_closest_nodes(const HashString& id, char* buffer, char* bufferEnd);
-+
-+ // Store DHT cache in the given container.
-+ Object* store_cache(Object* container) const;
-+
-+ // Create and verify a token. Tokens are valid between 15-30 minutes from creation.
-+ std::string make_token(const rak::socket_address* sa);
-+ bool token_valid(const std::string& token, const rak::socket_address* sa);
-+
-+ DhtManager::statistics_type get_statistics() const;
-+ void reset_statistics() { m_server.reset_statistics(); }
-+
-+ DhtServer* server() { return &m_server; }
-+ DhtTrackerList* trackers() { return &m_trackers; }
-+
-+private:
-+ // Number of nodes we need to consider the bootstrap process complete.
-+ static const unsigned int num_bootstrap_complete = 32;
-+
-+ typedef std::map<const HashString, DhtBucket*> DhtBucketList;
-+
-+ DhtBucketList::iterator find_bucket(const HashString& id);
-+
-+ bool add_node_to_bucket(DhtNode* node);
-+ void delete_node(const DhtNodeList::accessor& itr);
-+
-+ DhtBucketList::iterator split_bucket(const DhtBucketList::iterator& itr, DhtNode* node);
-+
-+ void bootstrap();
-+ void bootstrap_bucket(const DhtBucket* bucket);
-+
-+ void receive_timeout();
-+ void receive_timeout_bootstrap();
-+
-+ // buffer needs to hold an SHA1 hash (20 bytes), not just the token (8 bytes)
-+ void generate_token(const rak::socket_address* sa, int token, char buffer[20]);
-+
-+ rak::priority_item m_taskTimeout;
-+
-+ DhtServer m_server;
-+ DhtNodeList m_nodes;
-+ DhtBucketList m_routingTable;
-+ DhtTrackerList m_trackers;
-+
-+ std::list<rak::socket_address>* m_contacts;
-+
-+ int m_numRefresh;
-+
-+ bool m_networkUp;
-+
-+ // Secret keys used for generating announce tokens.
-+ int m_curToken;
-+ int m_prevToken;
-+};
-+
-+}
-+
-+#endif
-Index: libtorrent/src/dht/dht_hash_map.h
-===================================================================
---- libtorrent/src/dht/dht_hash_map.h (revision 0)
-+++ libtorrent/src/dht/dht_hash_map.h (revision 0)
-@@ -0,0 +1,168 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_DHT_HASH_MAP_H
-+#define LIBTORRENT_DHT_HASH_MAP_H
-+
-+#include "config.h"
-+
-+#if HAVE_TR1
-+#include <tr1/unordered_map>
-+#else
-+#include <map>
-+#endif
-+
-+#include "torrent/hash_string.h"
-+
-+#include "dht_node.h"
-+#include "dht_tracker.h"
-+
-+namespace torrent {
-+
-+#if HAVE_TR1
-+// Hash functions for HashString keys, and dereferencing HashString pointers.
-+// We use the last n bits of the 160-bit ID hash, since in sub-buckets the
-+// first few bits are all identical.
-+struct hashstring_ptr_hash : public std::unary_function<const HashString*, size_t> {
-+ size_t operator () (const HashString* n) const
-+ { return *(size_t*)(n->data() + n->size() - sizeof(size_t)); }
-+};
-+
-+struct hashstring_hash : public std::unary_function<HashString, size_t> {
-+ size_t operator () (const HashString& n) const
-+ { return *(size_t*)(n.data() + n.size() - sizeof(size_t)); }
-+};
-+
-+// Compare HashString pointers by dereferencing them.
-+struct hashstring_ptr_equal : public std::binary_function<const HashString*, const HashString*, bool> {
-+ size_t operator () (const HashString* one, const HashString* two) const
-+ { return *one == *two; }
-+};
-+
-+class DhtNodeList : public std::tr1::unordered_map<const HashString*, DhtNode*, hashstring_ptr_hash, hashstring_ptr_equal> {
-+public:
-+ typedef std::tr1::unordered_map<const HashString*, DhtNode*, hashstring_ptr_hash, hashstring_ptr_equal> base_type;
-+
-+ // Define accessor iterator with more convenient access to the key and
-+ // element values. Allows changing the map definition more easily if needed.
-+ template<typename T>
-+ struct accessor_wrapper : public T {
-+ accessor_wrapper(const T& itr) : T(itr) { }
-+
-+ const HashString& id() const { return *(**this).first; }
-+ DhtNode* node() const { return (**this).second; }
-+ };
-+
-+ typedef accessor_wrapper<const_iterator> const_accessor;
-+ typedef accessor_wrapper<iterator> accessor;
-+
-+ DhtNode* insert(DhtNode* n);
-+
-+};
-+
-+class DhtTrackerList : public std::tr1::unordered_map<HashString, DhtTracker*, hashstring_hash> {
-+public:
-+ typedef std::tr1::unordered_map<HashString, DhtTracker*, hashstring_hash> base_type;
-+
-+ template<typename T>
-+ struct accessor_wrapper : public T {
-+ accessor_wrapper(const T& itr) : T(itr) { }
-+
-+ const HashString& id() const { return (**this).first; }
-+ DhtTracker* tracker() const { return (**this).second; }
-+ };
-+
-+ typedef accessor_wrapper<const_iterator> const_accessor;
-+ typedef accessor_wrapper<iterator> accessor;
-+
-+};
-+
-+#else
-+
-+// Compare HashString pointers by dereferencing them.
-+struct hashstring_ptr_less : public std::binary_function<const HashString*, const HashString*, bool> {
-+ size_t operator () (const HashString* one, const HashString* two) const
-+ { return *one < *two; }
-+};
-+
-+class DhtNodeList : public std::map<const HashString*, DhtNode*, hashstring_ptr_less> {
-+public:
-+ typedef std::map<const HashString*, DhtNode*, hashstring_ptr_less> base_type;
-+
-+ // Define accessor iterator with more convenient access to the key and
-+ // element values. Allows changing the map definition more easily if needed.
-+ template<typename T>
-+ struct accessor_wrapper : public T {
-+ accessor_wrapper(const T& itr) : T(itr) { }
-+
-+ const HashString& id() const { return *(**this).first; }
-+ DhtNode* node() const { return (**this).second; }
-+ };
-+
-+ typedef accessor_wrapper<const_iterator> const_accessor;
-+ typedef accessor_wrapper<iterator> accessor;
-+
-+ DhtNode* insert(DhtNode* n);
-+
-+};
-+
-+class DhtTrackerList : public std::map<HashString, DhtTracker*> {
-+public:
-+ typedef std::map<HashString, DhtTracker*> base_type;
-+
-+ template<typename T>
-+ struct accessor_wrapper : public T {
-+ accessor_wrapper(const T& itr) : T(itr) { }
-+
-+ const HashString& id() const { return (**this).first; }
-+ DhtTracker* tracker() const { return (**this).second; }
-+ };
-+
-+ typedef accessor_wrapper<const_iterator> const_accessor;
-+ typedef accessor_wrapper<iterator> accessor;
-+
-+};
-+#endif // HAVE_TR1
-+
-+inline
-+DhtNode* DhtNodeList::insert(DhtNode* n) {
-+ base_type::insert(std::make_pair<const HashString*, DhtNode*>(n, n));
-+ return n;
-+}
-+
-+}
-+
-+#endif
-Index: libtorrent/src/dht/dht_transaction.cc
-===================================================================
---- libtorrent/src/dht/dht_transaction.cc (revision 0)
-+++ libtorrent/src/dht/dht_transaction.cc (revision 0)
-@@ -0,0 +1,287 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+
-+#include "torrent/exceptions.h"
-+#include "torrent/object_stream.h"
-+#include "tracker/tracker_dht.h"
-+
-+#include "dht_bucket.h"
-+#include "dht_transaction.h"
-+
-+namespace torrent {
-+
-+DhtSearch::DhtSearch(const HashString& target, const DhtBucket& contacts)
-+ : base_type(dht_compare_closer(target)),
-+ m_pending(0),
-+ m_contacted(0),
-+ m_replied(0),
-+ m_concurrency(3),
-+ m_restart(false),
-+ m_started(false),
-+ m_next(end()) {
-+
-+ add_contacts(contacts);
-+}
-+
-+DhtSearch::~DhtSearch() {
-+ // Make sure transactions were destructed first. Since it is the destruction
-+ // of a transaction that triggers this destructor, that should always be the
-+ // case.
-+ if (m_pending)
-+ throw internal_error("DhtSearch::~DhtSearch called with pending transactions.");
-+
-+ if (m_concurrency != 3)
-+ throw internal_error("DhtSearch::~DhtSearch with invalid concurrency limit.");
-+
-+ for (accessor itr = begin(); itr != end(); ++itr)
-+ delete itr.node();
-+}
-+
-+bool
-+DhtSearch::add_contact(const HashString& id, const rak::socket_address* sa) {
-+ DhtNode* n = new DhtNode(id, sa);
-+ bool added = insert(std::make_pair(n, this)).second;
-+
-+ if (!added)
-+ delete n;
-+ else
-+ m_restart = true;
-+
-+ return added;
-+}
-+
-+void
-+DhtSearch::add_contacts(const DhtBucket& contacts) {
-+ DhtBucketChain chain(&contacts);
-+
-+ // Add max_contacts=18 closest nodes, and fill up so we also have at least 8 good nodes.
-+ int needClosest = max_contacts - size();
-+ int needGood = DhtBucket::num_nodes;
-+
-+ for (DhtBucket::const_iterator itr = chain.bucket()->begin(); needClosest > 0 || needGood > 0; ++itr) {
-+ while (itr == chain.bucket()->end()) {
-+ if (!chain.next())
-+ return;
-+
-+ itr = chain.bucket()->begin();
-+ }
-+
-+ if ((!(*itr)->is_bad() || needClosest > 0) && add_contact((*itr)->id(), (*itr)->address())) {
-+ needGood -= !(*itr)->is_bad();
-+ needClosest--;
-+ }
-+ }
-+}
-+
-+// Check if a node has been contacted yet. This is the case if it is not currently
-+// being contacted, nor has it been found to be good or bad.
-+bool
-+DhtSearch::node_uncontacted(const DhtNode* node) const {
-+ return !node->is_active() && !node->is_good() && !node->is_bad();
-+}
-+
-+// After more contacts have been added, discard least closest nodes
-+// except if node has a transaction pending.
-+void
-+DhtSearch::trim(bool final) {
-+
-+ // We keep:
-+ // - the max_contacts=18 closest good or unknown nodes and all nodes closer
-+ // than them (to see if further searches find closer ones)
-+ // - for announces, also the 8 closest good nodes (i.e. nodes that have
-+ // replied) to have at least that many for the actual announce
-+ // - any node that currently has transactions pending
-+ //
-+ // However, after exhausting all search nodes, we only keep good nodes.
-+ //
-+ // For our purposes, the node status is as follows:
-+ // node is bad (contacted but hasn't replied) if is_bad()
-+ // node is good (contacted and replied) if is_good()
-+ // node is currently being contacted if is_active()
-+ // node is new and unknown otherwise
-+
-+ int needClosest = final ? 0 : max_contacts;
-+ int needGood = is_announce() ? DhtBucket::num_nodes : 0;
-+
-+ // We're done if we can't find any more nodes to contact.
-+ m_next = end();
-+
-+ accessor itr = base_type::begin();
-+ while (itr != end()) {
-+ // If we have all we need, delete current node unless it is
-+ // currently being contacted.
-+ if (!itr.node()->is_active() && needClosest <= 0 && (!itr.node()->is_good() || needGood <= 0)) {
-+ delete itr.node();
-+ erase(itr++);
-+ continue;
-+ }
-+
-+ // Otherwise adjust needed counts appropriately.
-+ needClosest--;
-+ needGood -= itr.node()->is_good();
-+
-+ // Remember the first uncontacted node as the closest one to contact next.
-+ if (m_next == end() && node_uncontacted(itr.node()))
-+ m_next = const_accessor(itr);
-+
-+ ++itr;
-+ }
-+
-+ m_restart = false;
-+}
-+
-+DhtSearch::const_accessor
-+DhtSearch::get_contact() {
-+ if (m_pending >= m_concurrency)
-+ return end();
-+
-+ if (m_restart)
-+ trim(false);
-+
-+ const_accessor ret = m_next;
-+ if (ret == end())
-+ return ret;
-+
-+ set_node_active(ret, true);
-+ m_pending++;
-+ m_contacted++;
-+
-+ // Find next node to contact: any node we haven't contacted yet.
-+ while (++m_next != end()) {
-+ if (node_uncontacted(m_next.node()))
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+void
-+DhtSearch::node_status(const_accessor& n, bool success) {
-+ if (n == end() || !n.node()->is_active())
-+ throw internal_error("DhtSearch::node_status called for invalid/inactive node.");
-+
-+ if (success) {
-+ n.node()->set_good();
-+ m_replied++;
-+
-+ } else {
-+ n.node()->set_bad();
-+ }
-+
-+ m_pending--;
-+ set_node_active(n, false);
-+}
-+
-+DhtSearch::const_accessor
-+DhtAnnounce::start_announce() {
-+ trim(true);
-+
-+ if (empty())
-+ return end();
-+
-+ if (!complete() || m_next != end() || size() > DhtBucket::num_nodes)
-+ throw internal_error("DhtSearch::start_announce called in inconsistent state.");
-+
-+ m_contacted = m_pending = size();
-+ m_replied = 0;
-+
-+ for (const_accessor itr(begin()); itr != end(); ++itr)
-+ set_node_active(itr, true);
-+
-+ return const_accessor(begin());
-+}
-+
-+void
-+DhtTransactionPacket::build_buffer(const Object& data) {
-+ char buffer[1500]; // If the message would exceed an Ethernet frame, something went very wrong.
-+ object_buffer_t result = object_write_bencode_c(object_write_to_buffer, NULL, std::make_pair(buffer, buffer + sizeof(buffer)), &data);
-+
-+ m_length = result.second - buffer;
-+ m_data = new char[m_length];
-+ memcpy(m_data, buffer, m_length);
-+}
-+
-+DhtTransaction::DhtTransaction(int quick_timeout, int timeout, const HashString& id, const rak::socket_address* sa)
-+ : m_hasQuickTimeout(quick_timeout > 0),
-+ m_id(id),
-+ m_sa(*sa),
-+ m_timeout(cachedTime.seconds() + timeout),
-+ m_quickTimeout(cachedTime.seconds() + quick_timeout),
-+ m_retry(3),
-+ m_packet(NULL) {
-+
-+}
-+
-+DhtTransaction::~DhtTransaction() {
-+ if (m_packet != NULL)
-+ m_packet->set_failed();
-+}
-+
-+void
-+DhtTransactionSearch::set_stalled() {
-+ if (!m_hasQuickTimeout)
-+ throw internal_error("DhtTransactionSearch::set_stalled called on already stalled transaction.");
-+
-+ m_hasQuickTimeout = false;
-+ m_search->m_concurrency++;
-+}
-+
-+void
-+DhtTransactionSearch::complete(bool success) {
-+ if (m_node == m_search->end())
-+ throw internal_error("DhtTransactionSearch::complete called multiple times.");
-+
-+ if (m_node.search() != m_search)
-+ throw internal_error("DhtTransactionSearch::complete called for node from wrong search.");
-+
-+ if (!m_hasQuickTimeout)
-+ m_search->m_concurrency--;
-+
-+ m_search->node_status(m_node, success);
-+ m_node = m_search->end();
-+}
-+
-+DhtTransactionSearch::~DhtTransactionSearch() {
-+ if (m_node != m_search->end())
-+ complete(false);
-+
-+ if (m_search->complete())
-+ delete m_search;
-+}
-+
-+}
-Index: libtorrent/src/dht/dht_node.cc
-===================================================================
---- libtorrent/src/dht/dht_node.cc (revision 0)
-+++ libtorrent/src/dht/dht_node.cc (revision 0)
-@@ -0,0 +1,89 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+#include "globals.h"
-+
-+#include "torrent/exceptions.h"
-+#include "torrent/object.h"
-+
-+#include "dht_node.h"
-+
-+namespace torrent {
-+
-+DhtNode::DhtNode(const HashString& id, const rak::socket_address* sa) :
-+ HashString(id),
-+ m_socketAddress(*sa),
-+ m_lastSeen(0),
-+ m_recentlyActive(false),
-+ m_recentlyInactive(0),
-+ m_bucket(NULL) {
-+
-+ if (sa->family() != rak::socket_address::af_inet)
-+ throw resource_error("Address not af_inet");
-+}
-+
-+DhtNode::DhtNode(const std::string& id, const Object& cache) :
-+ HashString(*HashString::cast_from(id.c_str())),
-+ m_recentlyActive(false),
-+ m_recentlyInactive(0),
-+ m_bucket(NULL) {
-+
-+ rak::socket_address_inet* sa = m_socketAddress.sa_inet();
-+ sa->set_family();
-+ sa->set_address_h(cache.get_key_value("i"));
-+ sa->set_port(cache.get_key_value("p"));
-+ m_lastSeen = cache.get_key_value("t");
-+ update();
-+}
-+
-+char*
-+DhtNode::store_compact(char* buffer) const {
-+ HashString::cast_from(buffer)->assign(data());
-+ *(uint32_t*) (buffer+20) = address()->sa_inet()->address_n();
-+ *(uint16_t*) (buffer+24) = address()->sa_inet()->port_n();
-+ return buffer+26;
-+}
-+
-+Object*
-+DhtNode::store_cache(Object* container) const {
-+ container->insert_key("i", m_socketAddress.sa_inet()->address_h());
-+ container->insert_key("p", m_socketAddress.sa_inet()->port());
-+ container->insert_key("t", m_lastSeen);
-+ return container;
-+}
-+
-+}
-Index: libtorrent/src/dht/dht_server.cc
-===================================================================
---- libtorrent/src/dht/dht_server.cc (revision 0)
-+++ libtorrent/src/dht/dht_server.cc (revision 0)
-@@ -0,0 +1,912 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+#include "globals.h"
-+
-+#include <algorithm>
-+#include <sstream>
-+#include <rak/error_number.h>
-+#include <rak/string_manip.h>
-+
-+#include "net/throttle_manager.h"
-+#include "torrent/exceptions.h"
-+#include "torrent/connection_manager.h"
-+#include "torrent/object.h"
-+#include "torrent/object_stream.h"
-+#include "torrent/poll.h"
-+#include "tracker/tracker_dht.h"
-+
-+#include "dht_bucket.h"
-+#include "dht_router.h"
-+#include "dht_transaction.h"
-+
-+#include "manager.h"
-+
-+namespace torrent {
-+
-+const char* DhtServer::queries[] = {
-+ "ping",
-+ "find_node",
-+ "get_peers",
-+ "announce_peer",
-+};
-+
-+// Error in DHT protocol, avoids std::string ctor from communication_error
-+class dht_error : public network_error {
-+public:
-+ dht_error(int code, const char* message) : m_message(message), m_code(code) {}
-+
-+ virtual int code() const throw() { return m_code; }
-+ virtual const char* what() const throw() { return m_message; }
-+
-+private:
-+ const char* m_message;
-+ int m_code;
-+};
-+
-+DhtServer::DhtServer(DhtRouter* router) :
-+ m_router(router),
-+
-+ m_uploadThrottle(60),
-+ m_downloadThrottle(60),
-+
-+ m_networkUp(false) {
-+
-+ get_fd().clear();
-+ reset_statistics();
-+
-+ // Reserve a socket for the DHT server, even though we don't
-+ // actually open it until the server is started, which may not
-+ // happen until the first non-private torrent is started.
-+ manager->connection_manager()->inc_socket_count();
-+}
-+
-+DhtServer::~DhtServer() {
-+ stop();
-+
-+ std::for_each(m_highQueue.begin(), m_highQueue.end(), rak::call_delete<DhtTransactionPacket>());
-+ std::for_each(m_lowQueue.begin(), m_lowQueue.end(), rak::call_delete<DhtTransactionPacket>());
-+
-+ manager->connection_manager()->dec_socket_count();
-+}
-+
-+void
-+DhtServer::start(int port) {
-+ try {
-+ if (!get_fd().open_datagram() || !get_fd().set_nonblock())
-+ throw resource_error("Could not allocate datagram socket.");
-+
-+ if (!get_fd().set_reuse_address(true))
-+ throw resource_error("Could not set listening port to reuse address.");
-+
-+ rak::socket_address sa = *m_router->address();
-+ sa.set_port(port);
-+
-+ if (!get_fd().bind(sa))
-+ throw resource_error("Could not bind datagram socket.");
-+
-+ } catch (...) {
-+ get_fd().close();
-+ get_fd().clear();
-+ throw;
-+ }
-+
-+ m_taskTimeout.set_slot(rak::mem_fn(this, &DhtServer::receive_timeout));
-+
-+ m_uploadThrottle.set_list_iterator(manager->upload_throttle()->throttle_list()->end());
-+ m_uploadThrottle.slot_activate(rak::make_mem_fun(reinterpret_cast<SocketBase*>(this), &SocketBase::receive_throttle_up_activate));
-+
-+ m_downloadThrottle.set_list_iterator(manager->download_throttle()->throttle_list()->end());
-+ manager->download_throttle()->throttle_list()->insert(&m_downloadThrottle);
-+
-+ manager->poll()->open(this);
-+ manager->poll()->insert_read(this);
-+ manager->poll()->insert_error(this);
-+}
-+
-+void
-+DhtServer::stop() {
-+ if (!is_active())
-+ return;
-+
-+ clear_transactions();
-+
-+ priority_queue_erase(&taskScheduler, &m_taskTimeout);
-+
-+ manager->upload_throttle()->throttle_list()->erase(&m_uploadThrottle);
-+ manager->download_throttle()->throttle_list()->erase(&m_downloadThrottle);
-+
-+ manager->poll()->remove_read(this);
-+ manager->poll()->remove_write(this);
-+ manager->poll()->remove_error(this);
-+ manager->poll()->close(this);
-+
-+ get_fd().close();
-+ get_fd().clear();
-+
-+ m_networkUp = false;
-+}
-+
-+void
-+DhtServer::reset_statistics() {
-+ m_queriesReceived = 0;
-+ m_queriesSent = 0;
-+ m_repliesReceived = 0;
-+
-+ m_uploadThrottle.rate()->set_total(0);
-+ m_downloadThrottle.rate()->set_total(0);
-+}
-+
-+// Ping a node whose ID we know.
-+void
-+DhtServer::ping(const HashString& id, const rak::socket_address* sa) {
-+ // No point pinging a node that we're already contacting otherwise.
-+ transaction_itr itr = m_transactions.lower_bound(DhtTransaction::key(sa, 0));
-+ if (itr == m_transactions.end() || (itr->first >> 32 != DhtTransaction::key(sa, 0) >> 32))
-+ add_transaction(new DhtTransactionPing(id, sa), packet_prio_low);
-+}
-+
-+// Contact nodes in given bucket and ask for their nodes closest to target.
-+void
-+DhtServer::find_node(const DhtBucket& contacts, const HashString& target) {
-+ DhtSearch* search = new DhtSearch(target, contacts);
-+
-+ DhtSearch::const_accessor n;
-+ while ((n = search->get_contact()) != search->end())
-+ add_transaction(new DhtTransactionFindNode(n), packet_prio_low);
-+
-+ // This shouldn't happen, it means we had no contactable nodes at all.
-+ if (!search->start()) {
-+ delete search;
-+ return;
-+ }
-+
-+}
-+
-+void
-+DhtServer::announce(const DhtBucket& contacts, const HashString& infoHash, TrackerDht* tracker) {
-+ DhtAnnounce* announce = new DhtAnnounce(infoHash, tracker, contacts);
-+
-+ DhtSearch::const_accessor n;
-+ while ((n = announce->get_contact()) != announce->end())
-+ add_transaction(new DhtTransactionFindNode(n), packet_prio_high);
-+
-+ // This can only happen if all nodes we know are bad.
-+ if (!announce->start()) {
-+ tracker->receive_failed("No DHT nodes available for peer search.");
-+ delete announce;
-+ return;
-+ }
-+
-+ tracker->receive_status(announce->num_replied(), announce->num_contacted(), "Searching");
-+}
-+
-+void
-+DhtServer::cancel_announce(DownloadInfo* info, const TrackerDht* tracker) {
-+ transaction_itr itr = m_transactions.begin();
-+
-+ while (itr != m_transactions.end()) {
-+ if (itr->second->is_search() && itr->second->as_search()->search()->is_announce()) {
-+ DhtAnnounce* announce = reinterpret_cast<DhtAnnounce*>(itr->second->as_search()->search());
-+
-+ if ((!info || announce->target() == info->hash()) && (!tracker || announce->tracker() == tracker)) {
-+ itr = cancel_transaction(itr);
-+ continue; // itr has been incremented already
-+ }
-+ }
-+
-+ ++itr;
-+ }
-+}
-+
-+void
-+DhtServer::update() {
-+ // Reset this every 15 minutes. It'll get set back to true if we receive
-+ // any valid packets. This allows detecting when the entire network goes
-+ // down, and prevents all nodes from getting removed as unresponsive.
-+ m_networkUp = false;
-+}
-+
-+void
-+DhtServer::process_query(const Object& transactionId, const HashString& id, const rak::socket_address* sa, Object& request) {
-+ m_queriesReceived++;
-+ m_networkUp = true;
-+
-+ std::string& query = request.get_key_string("q");
-+
-+ Object& arg = request.get_key("a");
-+
-+ // Construct reply.
-+ Object reply(Object::TYPE_MAP);
-+
-+ if (query == "find_node")
-+ create_find_node_response(arg, reply);
-+
-+ else if (query == "get_peers")
-+ create_get_peers_response(arg, sa, reply);
-+
-+ else if (query == "announce_peer")
-+ create_announce_peer_response(arg, sa, reply);
-+
-+ else if (query != "ping")
-+ throw dht_error(dht_error_bad_method, "Unknown query type.");
-+
-+ m_router->node_queried(id, sa);
-+
-+ create_response(transactionId, sa, reply);
-+}
-+
-+void
-+DhtServer::create_find_node_response(const Object& arg, Object& reply) {
-+ const std::string& target = arg.get_key_string("target");
-+
-+ if (target.length() < HashString::size_data)
-+ throw dht_error(dht_error_protocol, "target string too short");
-+
-+ char compact[26*8];
-+ char* end = m_router->store_closest_nodes(*HashString::cast_from(target), compact, compact + 26*8);
-+
-+ if (end == compact)
-+ throw dht_error(dht_error_generic, "No nodes");
-+
-+ reply.insert_key("nodes", std::string(compact, end));
-+}
-+
-+void
-+DhtServer::create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply) {
-+ reply.insert_key("token", m_router->make_token(sa));
-+
-+ const std::string& info_hash_str = arg.get_key_string("info_hash");
-+
-+ if (info_hash_str.length() < HashString::size_data)
-+ throw dht_error(dht_error_protocol, "info hash too short");
-+
-+ const HashString* info_hash = HashString::cast_from(info_hash_str);
-+
-+ DhtTracker* tracker = m_router->get_tracker(*info_hash, false);
-+
-+ // If we're not tracking or have no peers, send closest nodes.
-+ if (!tracker || tracker->empty()) {
-+ char compact[26*8];
-+ char* end = m_router->store_closest_nodes(*info_hash, compact, compact + 26*8);
-+
-+ if (end == compact)
-+ throw dht_error(dht_error_generic, "No peers nor nodes");
-+
-+ reply.insert_key("nodes", std::string(compact, end));
-+
-+ } else {
-+ Object& values = reply.insert_key("values", Object(Object::TYPE_LIST));
-+ values.insert_back(tracker->get_peers());
-+ }
-+}
-+
-+void
-+DhtServer::create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply) {
-+ const std::string& info_hash_str = arg.get_key_string("info_hash");
-+
-+ if (info_hash_str.length() < HashString::size_data)
-+ throw dht_error(dht_error_protocol, "info hash too short");
-+
-+ const HashString* info_hash = HashString::cast_from(info_hash_str);
-+
-+ if (!m_router->token_valid(arg.get_key_string("token"), sa))
-+ throw dht_error(dht_error_protocol, "Token invalid.");
-+
-+ DhtTracker* tracker = m_router->get_tracker(*info_hash, true);
-+ rak::socket_address peer = *sa;
-+ peer.set_port(arg.get_key_value("port"));
-+ tracker->add_peer(&peer);
-+}
-+
-+void
-+DhtServer::process_response(int transactionId, const HashString& id, const rak::socket_address* sa, Object& request) {
-+ transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId));
-+
-+ // Response to a transaction we don't have in our table. At this point it's
-+ // impossible to tell whether it used to be a valid transaction but timed out
-+ // or the node did not return the ID we sent it. Best we can do is ignore the
-+ // reply.
-+ if (itr == m_transactions.end())
-+ return;
-+
-+ m_repliesReceived++;
-+ m_networkUp = true;
-+
-+ const Object& response = request.get_key("r");
-+
-+ // If we contact a node but the reply ID or key (address) don't match, ignore the reply
-+ // to prevent interference from rogue nodes.
-+ // (But ignore the port for comparison purposes since it may be changed by NAT.)
-+ DhtTransaction* transaction = itr->second;
-+ if (DhtTransaction::key(sa, transactionId) != transaction->key(transactionId) || (id != transaction->id() && transaction->id() != *m_router && transaction->id() != m_router->zero_id))
-+ return;
-+
-+ switch (transaction->type()) {
-+ case DhtTransaction::DHT_FIND_NODE:
-+ parse_find_node_reply(transaction->as_find_node(), response.get_key_string("nodes"));
-+ break;
-+
-+ case DhtTransaction::DHT_GET_PEERS:
-+ parse_get_peers_reply(transaction->as_get_peers(), response);
-+ break;
-+
-+ // Nothing to do for DHT_PING and DHT_ANNOUNCE_PEER
-+ default:
-+ break;
-+ }
-+
-+ // Mark node responsive only if all processing was successful, without errors.
-+ m_router->node_replied(id, sa);
-+
-+ delete itr->second;
-+ m_transactions.erase(itr);
-+}
-+
-+void
-+DhtServer::process_error(int transactionId, const rak::socket_address* sa, Object& request) {
-+ transaction_itr itr = m_transactions.find(DhtTransaction::key(sa, transactionId));
-+
-+ if (itr == m_transactions.end())
-+ return;
-+
-+ m_repliesReceived++;
-+ m_networkUp = true;
-+
-+ // Don't mark node as good (because it replied) or bad (because it returned an error).
-+ // If it consistently returns errors for valid queries it's probably broken. But a
-+ // few error messages are acceptable. So we do nothing and pretend the query never happened.
-+
-+ cancel_transaction(itr);
-+}
-+
-+void
-+DhtServer::parse_node_info(node_info_list& l, const std::string& n) {
-+ if (sizeof(const compact_node_info) != 26)
-+ throw internal_error("DhtServer::parse_node_info(...) bad struct size.");
-+
-+ std::copy(reinterpret_cast<const compact_node_info*>(n.c_str()),
-+ reinterpret_cast<const compact_node_info*>(n.c_str() + n.size() - n.size() % sizeof(compact_node_info)),
-+ std::back_inserter(l));
-+}
-+
-+void
-+DhtServer::parse_find_node_reply(DhtTransactionSearch* transaction, const std::string& nodes) {
-+ DhtSearch* search = transaction->search();
-+
-+ transaction->complete(true);
-+
-+ node_info_list l;
-+ parse_node_info(l, nodes);
-+
-+ for (node_info_list::iterator itr = l.begin(); itr != l.end(); ++itr) {
-+ if (itr->id() != m_router->id()) {
-+ rak::socket_address sa = itr->address();
-+ search->add_contact(itr->id(), &sa);
-+ }
-+ }
-+
-+ find_node_next(transaction);
-+}
-+
-+void
-+DhtServer::parse_get_peers_reply(DhtTransactionGetPeers* transaction, const Object& response) {
-+ DhtAnnounce* announce = transaction->announce();
-+
-+ transaction->complete(true);
-+
-+ if (response.has_key_list("values"))
-+ announce->tracker()->receive_peers((*response.get_key_list("values").begin()).as_string());
-+
-+ if (response.has_key_string("token"))
-+ add_transaction(new DhtTransactionAnnounce(transaction->id(), transaction->address(), announce->target(), response.get_key_string("token")), packet_prio_low);
-+
-+ announce->tracker()->receive_status(announce->num_replied(), announce->num_contacted(), "Announcing");
-+
-+ if (!announce->complete())
-+ return;
-+
-+ DhtTracker* torrent = m_router->get_tracker(transaction->announce()->target(), false);
-+ if (torrent != NULL)
-+ announce->tracker()->receive_peers(torrent->get_peers());
-+
-+ announce->tracker()->receive_success();
-+}
-+
-+void
-+DhtServer::find_node_next(DhtTransactionSearch* transaction) {
-+ DhtSearch::const_accessor node;
-+ while ((node = transaction->search()->get_contact()) != transaction->search()->end()) {
-+ int prio = packet_prio_low;
-+ if (transaction->search()->is_announce())
-+ prio = packet_prio_high;
-+
-+ add_transaction(new DhtTransactionFindNode(node), prio);
-+ }
-+
-+ if (transaction->search()->is_announce())
-+ reinterpret_cast<DhtAnnounce*>(transaction->search())->tracker()->receive_status(transaction->search()->num_replied(), transaction->search()->num_contacted(), "Searching");
-+
-+ if (!transaction->search()->complete())
-+ return;
-+
-+ if (!transaction->search()->is_announce())
-+ return;
-+
-+ // We have found the 8 closest nodes to the info hash. Retrieve peers
-+ // from them and announce to them.
-+ DhtAnnounce* announce = transaction->as_get_peers()->announce();
-+
-+ for (node = announce->start_announce(); node != announce->end(); ++node)
-+ add_transaction(new DhtTransactionGetPeers(node), packet_prio_high);
-+
-+ if (announce->complete())
-+ return announce->tracker()->receive_failed("DHT search unsuccessful.");
-+
-+ announce->tracker()->receive_status(announce->num_replied(), announce->num_contacted(), "Announcing");
-+}
-+
-+void
-+DhtServer::add_packet(DhtTransactionPacket* packet, int priority) {
-+ switch (priority) {
-+ // High priority packets are for important queries, and quite small.
-+ // They're added to front of high priority queue and thus will be the
-+ // next packets sent.
-+ case packet_prio_high:
-+ m_highQueue.push_front(packet);
-+ break;
-+
-+ // Low priority query packets are added to the back of the high priority
-+ // queue and will be sent when all high priority packets have been transmitted.
-+ case packet_prio_low:
-+ m_highQueue.push_back(packet);
-+ break;
-+
-+ // Reply packets will be processed after all of our own packets have been send.
-+ case packet_prio_reply:
-+ m_lowQueue.push_back(packet);
-+ break;
-+
-+ default:
-+ throw internal_error("DhtServer::add_packet called with invalid priority.");
-+ }
-+}
-+
-+void
-+DhtServer::create_query(transaction_itr itr, int tID, const rak::socket_address* sa, int priority) {
-+ if (itr->second->id() == m_router->id()) {
-+ delete itr->second;
-+ m_transactions.erase(itr);
-+ return; // Don't send to ourself.
-+ }
-+
-+ Object query(Object::TYPE_MAP);
-+
-+ Object q(Object::TYPE_MAP);
-+ q.insert_key("id", m_router->str());
-+
-+ DhtTransaction* transaction = itr->second;
-+ switch (transaction->type()) {
-+ case DhtTransaction::DHT_PING:
-+ // nothing to do
-+ break;
-+
-+ case DhtTransaction::DHT_FIND_NODE:
-+ q.insert_key("target", transaction->as_find_node()->search()->target().str());
-+ break;
-+
-+ case DhtTransaction::DHT_GET_PEERS:
-+ q.insert_key("info_hash", transaction->as_get_peers()->announce()->target().str());
-+ break;
-+
-+ case DhtTransaction::DHT_ANNOUNCE_PEER:
-+ q.insert_key("info_hash", transaction->as_announce()->info_hash().str());
-+ q.insert_key("port", manager->connection_manager()->listen_port());
-+ q.insert_key("token", transaction->as_announce()->token());
-+ break;
-+ }
-+
-+ char trans_id = tID;
-+ query.insert_key("t", std::string(&trans_id, 1));
-+ query.insert_key("y", "q");
-+ query.insert_key("q", queries[transaction->type()]);
-+ query.insert_key("a", q);
-+ query.insert_key("v", PEER_VERSION);
-+
-+ DhtTransactionPacket* packet = new DhtTransactionPacket(transaction->address(), query, tID, transaction);
-+ transaction->set_packet(packet);
-+ add_packet(packet, priority);
-+
-+ m_queriesSent++;
-+}
-+
-+void
-+DhtServer::create_response(const Object& transactionId, const rak::socket_address* sa, Object& r) {
-+ Object reply(Object::TYPE_MAP);
-+ r.insert_key("id", m_router->str());
-+
-+ reply.insert_key("t", transactionId);
-+ reply.insert_key("y", "r");
-+ reply.insert_key("r", r);
-+ reply.insert_key("v", PEER_VERSION);
-+
-+ add_packet(new DhtTransactionPacket(sa, reply), packet_prio_reply);
-+}
-+
-+void
-+DhtServer::create_error(const Object& transactionId, const rak::socket_address* sa, int num, const std::string& msg) {
-+ Object error(Object::TYPE_MAP);
-+
-+ error.insert_key("t", transactionId);
-+ error.insert_key("y", "e");
-+ error.insert_key("v", PEER_VERSION);
-+
-+ Object& e = error.insert_key("e", Object(Object::TYPE_LIST));
-+ e.insert_back(num);
-+ e.insert_back(msg);
-+
-+ add_packet(new DhtTransactionPacket(sa, error), packet_prio_reply);
-+}
-+
-+int
-+DhtServer::add_transaction(DhtTransaction* transaction, int priority) {
-+ // Try random transaction ID. This is to make it less likely that we reuse
-+ // a transaction ID from an earlier transaction which timed out and we forgot
-+ // about it, so that if the node replies after the timeout it's less likely
-+ // that we match the reply to the wrong transaction.
-+ //
-+ // If there's an existing transaction with the random ID we search for the next
-+ // unused one. Since normally only one or two transactions will be active per
-+ // node, a collision is extremely unlikely, and a linear search for the first
-+ // open one is the most efficient.
-+ unsigned int rnd = (uint8_t)random();
-+ unsigned int id = rnd;
-+
-+ transaction_itr insertItr = m_transactions.lower_bound(transaction->key(rnd));
-+
-+ // If key matches, keep trying successive IDs.
-+ while (insertItr != m_transactions.end() && insertItr->first == transaction->key(id)) {
-+ ++insertItr;
-+ id = (uint8_t)(id + 1);
-+
-+ // Give up after trying all possible IDs. This should never happen.
-+ if (id == rnd) {
-+ delete transaction;
-+ return -1;
-+ }
-+
-+ // Transaction ID wrapped around, reset iterator.
-+ if (id == 0)
-+ insertItr = m_transactions.lower_bound(transaction->key(id));
-+ }
-+
-+ // We know where to insert it, so pass that as hint.
-+ insertItr = m_transactions.insert(insertItr, std::make_pair(transaction->key(id), transaction));
-+
-+ create_query(insertItr, id, transaction->address(), priority);
-+
-+ start_write();
-+
-+ return id;
-+}
-+
-+// Transaction received no reply and timed out. Mark node as bad and remove
-+// transaction (except if it was only the quick timeout).
-+DhtServer::transaction_itr
-+DhtServer::failed_transaction(transaction_itr itr, bool quick) {
-+ DhtTransaction* transaction = itr->second;
-+
-+ // If it was a known node, remember that it didn't reply, unless the transaction
-+ // is only stalled (had quick timeout, but not full timeout). Also if the
-+ // transaction still has an associated packet, the packet never got sent due to
-+ // throttling, so don't blame the remote node for not replying.
-+ // Finally, if we haven't received anything whatsoever so far, assume the entire
-+ // network is down and so we can't blame the node either.
-+ if (!quick && m_networkUp && transaction->packet() == NULL && transaction->id() != *m_router)
-+ m_router->node_inactive(transaction->id(), transaction->address());
-+
-+ switch (transaction->type()) {
-+ case DhtTransaction::DHT_FIND_NODE:
-+ if (quick)
-+ transaction->as_find_node()->set_stalled();
-+ else
-+ transaction->as_find_node()->complete(false);
-+
-+ find_node_next(transaction->as_find_node());
-+ break;
-+
-+ case DhtTransaction::DHT_GET_PEERS:
-+ DhtAnnounce* announce;
-+ announce = transaction->as_get_peers()->announce();
-+ transaction->as_get_peers()->complete(false);
-+ if (announce->complete())
-+ announce->tracker()->receive_success();
-+ break;
-+
-+ // Nothing to for DHT_PING and DHT_ANNOUNCE_PEER
-+ default:
-+ break;
-+ }
-+
-+ if (quick) {
-+ return ++itr; // don't actually delete the transaction until the final timeout
-+
-+ } else {
-+ delete itr->second;
-+ m_transactions.erase(itr++);
-+ return itr;
-+ }
-+}
-+
-+// Cancel transaction before the timeout (i.e. we aren't sure if the node is bad yet).
-+DhtServer::transaction_itr
-+DhtServer::cancel_transaction(transaction_itr itr) {
-+ DhtTransaction* transaction = itr->second;
-+
-+ // If this was the last transaction of an announce search, notify tracker.
-+ if (transaction->is_search() && transaction->as_search()->search()->is_announce()) {
-+ transaction->as_search()->complete(false);
-+
-+ DhtAnnounce* announce = reinterpret_cast<DhtAnnounce*>(transaction->as_search()->search());
-+ if (announce->complete()) {
-+ // If we got no peers, it's a failure if the announces all failed as well.
-+ // We don't want no peers to be a failure in general since it may just be
-+ // a dead torrent.
-+ if (announce->num_replied() == 0 && !announce->tracker()->has_peers())
-+ announce->tracker()->receive_failed("Announce failed");
-+ else
-+ announce->tracker()->receive_success();
-+ }
-+ }
-+
-+ delete itr->second;
-+ m_transactions.erase(itr++);
-+ return itr;
-+}
-+
-+void
-+DhtServer::clear_transactions() {
-+ std::for_each(m_transactions.begin(), m_transactions.end(),
-+ rak::on(rak::mem_ref(&transaction_map::value_type::second),
-+ rak::call_delete<DhtTransaction>()));
-+ m_transactions.clear();
-+}
-+
-+void
-+DhtServer::event_read() {
-+ uint32_t total = 0;
-+ std::istringstream sstream;
-+
-+ sstream.imbue(std::locale::classic());
-+
-+ while (true) {
-+ rak::socket_address sa;
-+ int type = '?';
-+ const Object* transactionId = NULL;
-+ const HashString* nodeId = NULL;
-+
-+ try {
-+ char buffer[2048];
-+ int32_t read = read_datagram(buffer, sizeof(buffer), &sa);
-+
-+ if (read < 0)
-+ break;
-+
-+ total += read;
-+ sstream.str(std::string(buffer, read));
-+
-+ Object request;
-+ sstream >> request;
-+
-+ // Could throw dht_error with more descriptive error messages but
-+ // we can't return an error unless the packet is valid and we have the
-+ // transaction ID anyway. Searching the routing table for the source
-+ // address to find the node is too expensive, so just ignore the packet.
-+ if (sstream.fail() || !request.is_map() || !request.has_key("t") || !request.has_key_string("y"))
-+ continue;
-+
-+ // Read the items required for proper error handling first.
-+ transactionId = &request.get_key("t");
-+ if (request.get_key_string("y").length() == 1)
-+ type = request.get_key_string("y")[0];
-+
-+ // Queries and replies have node ID in different dictionaries.
-+ if (type == 'r' || type == 'q') {
-+ const std::string& nodeIdStr = request.get_key(type == 'q' ? "a" : "r").get_key_string("id");
-+
-+ if (nodeIdStr.length() < HashString::size_data)
-+ throw dht_error(dht_error_protocol, "id value too short");
-+
-+ nodeId = HashString::cast_from(nodeIdStr);
-+ }
-+
-+ // Sanity check the returned transaction ID.
-+ if ((type == 'r' || type == 'e') &&
-+ (!transactionId->is_string() || transactionId->as_string().length() != 1))
-+ throw dht_error(dht_error_protocol, "Invalid transaction ID type/length.");
-+
-+ switch (type) {
-+ case 'q':
-+ process_query(*transactionId, *nodeId, &sa, request);
-+ break;
-+
-+ case 'r':
-+ process_response(((unsigned char*)transactionId->as_string().c_str())[0], *nodeId, &sa, request);
-+ break;
-+
-+ case 'e':
-+ process_error(((unsigned char*)transactionId->as_string().c_str())[0], &sa, request);
-+ break;
-+
-+ default:
-+ throw dht_error(dht_error_bad_method, "Unknown message type.");
-+ }
-+
-+ } catch (bencode_error& e) {
-+ if (type == 'q' && transactionId != NULL)
-+ create_error(*transactionId, &sa, dht_error_protocol, std::string("Malformed packet: ") + e.what());
-+ else if ((type == 'r' || type == 'e') && nodeId != NULL)
-+ m_router->node_inactive(*nodeId, &sa);
-+
-+ } catch (dht_error& e) {
-+ // If node was querying us, reply with error packet, otherwise mark the node as "query failed",
-+ // so that if it repeatedly sends malformed replies we will drop it instead of propagating it
-+ // to other nodes.
-+ if (type == 'q' && transactionId != NULL)
-+ create_error(*transactionId, &sa, e.code(), e.what());
-+ else if ((type == 'r' || type == 'e') && nodeId != NULL)
-+ m_router->node_inactive(*nodeId, &sa);
-+
-+ } catch (network_error& e) {
-+
-+ }
-+ }
-+
-+ manager->download_throttle()->throttle_list()->node_used_unthrottled(total);
-+ start_write();
-+}
-+
-+bool
-+DhtServer::process_queue(packet_queue& queue, uint32_t* quota) {
-+ uint32_t used = 0;
-+
-+ while (!queue.empty()) {
-+ DhtTransactionPacket* packet = queue.front();
-+
-+ // Make sure its transaction hasn't timed out yet, if it has/had one
-+ // and don't bother sending non-transaction packets after more than
-+ // 15 seconds in the queue.
-+ if (packet->has_failed() || packet->age() > 15) {
-+ delete packet;
-+ queue.pop_front();
-+ continue;
-+ }
-+
-+ if (packet->length() > *quota) {
-+ manager->upload_throttle()->throttle_list()->node_used(&m_uploadThrottle, used);
-+ return false;
-+ }
-+
-+ queue.pop_front();
-+
-+ try {
-+ int written = write_datagram(packet->c_str(), packet->length(), packet->address());
-+
-+ if (written == -1)
-+ throw network_error();
-+
-+ used += written;
-+ *quota -= written;
-+
-+ if ((unsigned int)written != packet->length())
-+ throw network_error();
-+
-+ } catch (network_error& e) {
-+ // Couldn't write packet, maybe something wrong with node address or routing, so mark node as bad.
-+ if (packet->has_transaction()) {
-+ transaction_itr itr = m_transactions.find(packet->transaction()->key(packet->id()));
-+ if (itr == m_transactions.end())
-+ throw internal_error("DhtServer::process_queue could not find transaction.");
-+
-+ failed_transaction(itr, false);
-+ }
-+ }
-+
-+ if (packet->has_transaction())
-+ packet->transaction()->set_packet(NULL);
-+
-+ delete packet;
-+ }
-+
-+ manager->upload_throttle()->throttle_list()->node_used(&m_uploadThrottle, used);
-+ return true;
-+}
-+
-+void
-+DhtServer::event_write() {
-+ if (m_highQueue.empty() && m_lowQueue.empty())
-+ throw internal_error("DhtServer::event_write called but both write queues are empty.");
-+
-+ if (!manager->upload_throttle()->throttle_list()->is_throttled(&m_uploadThrottle))
-+ throw internal_error("DhtServer::event_write called while not in throttle list.");
-+
-+ uint32_t quota = manager->upload_throttle()->throttle_list()->node_quota(&m_uploadThrottle);
-+
-+ if (quota == 0 || !process_queue(m_highQueue, &quota) || !process_queue(m_lowQueue, &quota)) {
-+ manager->poll()->remove_write(this);
-+ manager->upload_throttle()->throttle_list()->node_deactivate(&m_uploadThrottle);
-+
-+ } else if (m_highQueue.empty() && m_lowQueue.empty()) {
-+ manager->poll()->remove_write(this);
-+ manager->upload_throttle()->throttle_list()->erase(&m_uploadThrottle);
-+ }
-+}
-+
-+void
-+DhtServer::event_error() {
-+}
-+
-+void
-+DhtServer::start_write() {
-+ if ((!m_highQueue.empty() || !m_lowQueue.empty()) && !manager->upload_throttle()->throttle_list()->is_throttled(&m_uploadThrottle)) {
-+ manager->upload_throttle()->throttle_list()->insert(&m_uploadThrottle);
-+ manager->poll()->insert_write(this);
-+ }
-+
-+ if (!m_taskTimeout.is_queued() && !m_transactions.empty())
-+ priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(5)).round_seconds());
-+}
-+
-+void
-+DhtServer::receive_timeout() {
-+ transaction_itr itr = m_transactions.begin();
-+ while (itr != m_transactions.end()) {
-+ if (itr->second->has_quick_timeout() && itr->second->quick_timeout() < cachedTime.seconds()) {
-+ itr = failed_transaction(itr, true);
-+
-+ } else if (itr->second->timeout() < cachedTime.seconds()) {
-+ itr = failed_transaction(itr, false);
-+
-+ } else {
-+ ++itr;
-+ }
-+ }
-+
-+ start_write();
-+}
-+
-+}
-Index: libtorrent/src/dht/Makefile.am
-===================================================================
---- libtorrent/src/dht/Makefile.am (revision 0)
-+++ libtorrent/src/dht/Makefile.am (revision 0)
-@@ -0,0 +1,19 @@
-+noinst_LTLIBRARIES = libsub_dht.la
-+
-+libsub_dht_la_SOURCES = \
-+ dht_bucket.cc \
-+ dht_bucket.h \
-+ dht_hash_map.h \
-+ dht_node.cc \
-+ dht_node.h \
-+ dht_router.cc \
-+ dht_router.h \
-+ dht_server.cc \
-+ dht_server.h \
-+ dht_size_list.h \
-+ dht_tracker.cc \
-+ dht_tracker.h \
-+ dht_transaction.cc \
-+ dht_transaction.h
-+
-+INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
-Index: libtorrent/src/dht/dht_transaction.h
-===================================================================
---- libtorrent/src/dht/dht_transaction.h (revision 0)
-+++ libtorrent/src/dht/dht_transaction.h (revision 0)
-@@ -0,0 +1,363 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_DHT_TRANSACTION_H
-+#define LIBTORRENT_DHT_TRANSACTION_H
-+
-+#include <map>
-+
-+#include <rak/socket_address.h>
-+
-+#include "dht/dht_node.h"
-+#include "torrent/hash_string.h"
-+
-+namespace torrent {
-+
-+class DhtBucket;
-+class TrackerDht;
-+
-+class DhtSearch;
-+class DhtTransactionSearch;
-+
-+class DhtTransaction;
-+class DhtTransactionPing;
-+class DhtTransactionFindNode;
-+class DhtTransactionGetPeers;
-+class DhtTransactionAnnounce;
-+
-+// DhtSearch implements the DHT search algorithm and holds search data
-+// that needs to be persistent across multiple find_node transactions.
-+//
-+// DhtAnnounce is a derived class used for searches that will eventually
-+// lead to an announce to the closest nodes.
-+
-+
-+// Compare predicate for ID closeness.
-+struct dht_compare_closer : public std::binary_function<const DhtNode*, const DhtNode*, bool> {
-+ dht_compare_closer(const HashString& target) : m_target(target) { }
-+
-+ bool operator () (const DhtNode* one, const DhtNode* two) const;
-+
-+ const HashString& target() const { return m_target; }
-+
-+ HashString m_target;
-+};
-+
-+// DhtSearch contains a list of nodes sorted by closeness to the given target,
-+// and returns what nodes to contact with up to three concurrent transactions pending.
-+// The map element is the DhtSearch object itself to allow the returned accessors
-+// to know which search a given node belongs to.
-+class DhtSearch : protected std::map<DhtNode*, DhtSearch*, dht_compare_closer> {
-+ friend class DhtTransactionSearch;
-+
-+public:
-+ typedef std::map<DhtNode*, DhtSearch*, dht_compare_closer> base_type;
-+
-+ // Number of closest potential contact nodes to keep.
-+ static const unsigned int max_contacts = 18;
-+
-+ DhtSearch(const HashString& target, const DhtBucket& contacts);
-+ virtual ~DhtSearch();
-+
-+ // Wrapper for iterators, allowing more convenient access to the key
-+ // and element values, which also makes it easier to change the container
-+ // without having to modify much code using iterators.
-+ template <typename T>
-+ struct accessor_wrapper : public T {
-+ accessor_wrapper() { }
-+ accessor_wrapper(const T& itr) : T(itr) { }
-+
-+ DhtNode* node() const { return (**this).first; }
-+ DhtSearch* search() const { return (**this).second; }
-+ };
-+
-+ typedef accessor_wrapper<base_type::const_iterator> const_accessor;
-+ typedef accessor_wrapper<base_type::iterator> accessor;
-+
-+ // Add a potential node to contact for the search.
-+ bool add_contact(const HashString& id, const rak::socket_address* sa);
-+ void add_contacts(const DhtBucket& contacts);
-+
-+ // Return next node to contact. Up to concurrent_searches nodes are returned,
-+ // and end() after that. Don't advance the accessor to get further contacts!
-+ const_accessor get_contact();
-+
-+ // Search statistics.
-+ int num_contacted() { return m_contacted; }
-+ int num_replied() { return m_replied; }
-+
-+ bool start() { m_started = true; return m_pending; }
-+ bool complete() const { return m_started && !m_pending; }
-+
-+ const HashString& target() const { return key_comp().target(); }
-+
-+ virtual bool is_announce() const { return false; }
-+
-+ // Expose the otherwise private end() function but return an accessor,
-+ // to allow code checking whether get_contact returned a valid accessor.
-+ const_accessor end() const { return base_type::end(); }
-+
-+ // Used by the sorting/comparison predicate to see which node is closer.
-+ static bool is_closer(const HashString& one, const HashString& two, const HashString& target);
-+
-+protected:
-+ void trim(bool final);
-+ void node_status(const_accessor& n, bool success);
-+ void set_node_active(const_accessor& n, bool active);
-+
-+ // Statistics about contacted nodes.
-+ unsigned int m_pending;
-+ unsigned int m_contacted;
-+ unsigned int m_replied;
-+ unsigned int m_concurrency;
-+
-+ bool m_restart; // If true, trim nodes and reset m_next on the following get_contact call.
-+ bool m_started;
-+
-+ // Next node to return in get_contact, is end() if we have no more contactable nodes.
-+ const_accessor m_next;
-+
-+private:
-+ DhtSearch(const DhtSearch& s);
-+
-+ bool node_uncontacted(const DhtNode* node) const;
-+};
-+
-+class DhtAnnounce : public DhtSearch {
-+public:
-+ DhtAnnounce(const HashString& infoHash, TrackerDht* tracker, const DhtBucket& contacts)
-+ : DhtSearch(infoHash, contacts),
-+ m_tracker(tracker) { }
-+
-+ TrackerDht* tracker() const { return m_tracker; }
-+
-+ // Start announce and return final set of nodes in get_contact() calls.
-+ // This resets DhtSearch's completed() function, which now
-+ // counts announces instead.
-+ const_accessor start_announce();
-+
-+ virtual bool is_announce() const { return true; }
-+
-+private:
-+ TrackerDht* m_tracker;
-+};
-+
-+// Class holding transaction data to be transmitted.
-+class DhtTransactionPacket {
-+public:
-+ // transaction packet
-+ DhtTransactionPacket(const rak::socket_address* s, const Object& d, unsigned int id, DhtTransaction* t)
-+ : m_sa(*s), m_id(id), m_transaction(t) { build_buffer(d); };
-+
-+ // non-transaction packet
-+ DhtTransactionPacket(const rak::socket_address* s, const Object& d)
-+ : m_sa(*s), m_id(-cachedTime.seconds()), m_transaction(NULL) { build_buffer(d); };
-+
-+ ~DhtTransactionPacket() { delete[] m_data; }
-+
-+ bool has_transaction() const { return m_id >= -1; }
-+ bool has_failed() const { return m_id == -1; }
-+ void set_failed() { m_id = -1; }
-+
-+ const rak::socket_address* address() const { return &m_sa; }
-+ rak::socket_address* address() { return &m_sa; }
-+
-+ const char* c_str() const { return m_data; }
-+ size_t length() const { return m_length; }
-+
-+ int id() const { return m_id; }
-+ int age() const { return has_transaction() ? 0 : cachedTime.seconds() + m_id; }
-+ const DhtTransaction* transaction() const { return m_transaction; }
-+ DhtTransaction* transaction() { return m_transaction; }
-+
-+private:
-+ void build_buffer(const Object& data);
-+
-+ rak::socket_address m_sa;
-+ char* m_data;
-+ size_t m_length;
-+ int m_id;
-+ DhtTransaction* m_transaction;
-+};
-+
-+// DHT Transaction classes. DhtTransaction and DhtTransactionSearch
-+// are not directly usable with no public constructor, since type()
-+// is a pure virtual function.
-+class DhtTransaction {
-+public:
-+ virtual ~DhtTransaction();
-+
-+ typedef enum {
-+ DHT_PING,
-+ DHT_FIND_NODE,
-+ DHT_GET_PEERS,
-+ DHT_ANNOUNCE_PEER,
-+ } transaction_type;
-+
-+ virtual transaction_type type() = 0;
-+ virtual bool is_search() { return false; }
-+
-+ // Key to uniquely identify a transaction with given per-node transaction id.
-+ uint64_t key(int id) const { return key(&m_sa, id); }
-+ static uint64_t key(const rak::socket_address* sa, int id) { return ((uint64_t)sa->sa_inet()->address_n() << 32) + id; }
-+
-+ // Node ID and address.
-+ const HashString& id() { return m_id; }
-+ const rak::socket_address* address() { return &m_sa; }
-+
-+ int timeout() { return m_timeout; }
-+ int quick_timeout() { return m_quickTimeout; }
-+ bool has_quick_timeout() { return m_hasQuickTimeout; }
-+
-+ int dec_retry() { return m_retry--; }
-+ int retry() { return m_retry; }
-+
-+ DhtTransactionPacket* packet() { return m_packet; }
-+ void set_packet(DhtTransactionPacket* p) { m_packet = p; }
-+
-+ // These could (should?) check that the type matches. Or use dynamic cast.
-+ DhtTransactionSearch* as_search() { return reinterpret_cast<DhtTransactionSearch*>(this); }
-+ DhtTransactionPing* as_ping() { return reinterpret_cast<DhtTransactionPing*>(this); }
-+ DhtTransactionFindNode* as_find_node() { return reinterpret_cast<DhtTransactionFindNode*>(this); }
-+ DhtTransactionGetPeers* as_get_peers() { return reinterpret_cast<DhtTransactionGetPeers*>(this); }
-+ DhtTransactionAnnounce* as_announce() { return reinterpret_cast<DhtTransactionAnnounce*>(this); }
-+
-+protected:
-+ DhtTransaction(int quick_timeout, int timeout, const HashString& id, const rak::socket_address* sa);
-+
-+ bool m_hasQuickTimeout;
-+
-+private:
-+ DhtTransaction(const DhtTransaction& t);
-+
-+ HashString m_id;
-+ rak::socket_address m_sa;
-+ int m_timeout;
-+ int m_quickTimeout;
-+ int m_retry;
-+ DhtTransactionPacket* m_packet;
-+};
-+
-+class DhtTransactionSearch : public DhtTransaction {
-+public:
-+ virtual ~DhtTransactionSearch();
-+
-+ virtual bool is_search() { return true; }
-+
-+ DhtSearch::const_accessor node() { return m_node; }
-+ DhtSearch* search() { return m_search; }
-+
-+ void set_stalled();
-+
-+ void complete(bool success);
-+
-+protected:
-+ DhtTransactionSearch(int quick_timeout, int timeout, DhtSearch::const_accessor& node)
-+ : DhtTransaction(quick_timeout, timeout, node.node()->id(), node.node()->address()),
-+ m_node(node),
-+ m_search(node.search()) { if (!m_hasQuickTimeout) m_search->m_concurrency++; }
-+
-+private:
-+ DhtSearch::const_accessor m_node;
-+ DhtSearch* m_search;
-+};
-+
-+// Actual transaction classes.
-+class DhtTransactionPing : public DhtTransaction {
-+public:
-+ DhtTransactionPing(const HashString& id, const rak::socket_address* sa)
-+ : DhtTransaction(-1, 30, id, sa) { }
-+
-+ virtual transaction_type type() { return DHT_PING; }
-+};
-+
-+class DhtTransactionFindNode : public DhtTransactionSearch {
-+public:
-+ DhtTransactionFindNode(DhtSearch::const_accessor& node)
-+ : DhtTransactionSearch(4, 30, node) { }
-+
-+ virtual transaction_type type() { return DHT_FIND_NODE; }
-+};
-+
-+class DhtTransactionGetPeers : public DhtTransactionSearch {
-+public:
-+ DhtTransactionGetPeers(DhtSearch::const_accessor& node)
-+ : DhtTransactionSearch(-1, 30, node) { }
-+
-+ virtual transaction_type type() { return DHT_GET_PEERS; }
-+
-+ DhtAnnounce* announce() { return reinterpret_cast<DhtAnnounce*>(search()); }
-+};
-+
-+class DhtTransactionAnnounce : public DhtTransaction {
-+public:
-+ DhtTransactionAnnounce(const HashString& id, const rak::socket_address* sa, const HashString& infoHash, const std::string& token)
-+ : DhtTransaction(-1, 30, id, sa),
-+ m_infoHash(infoHash),
-+ m_token(token) { }
-+
-+ virtual transaction_type type() { return DHT_ANNOUNCE_PEER; }
-+
-+ const HashString& info_hash() { return m_infoHash; }
-+ const std::string& token() { return m_token; }
-+
-+private:
-+ HashString m_infoHash;
-+ std::string m_token;
-+};
-+
-+inline bool
-+DhtSearch::is_closer(const HashString& one, const HashString& two, const HashString& target) {
-+ for (unsigned int i=0; i<one.size(); i++)
-+ if (one[i] != two[i])
-+ return (uint8_t)(one[i] ^ target[i]) < (uint8_t)(two[i] ^ target[i]);
-+
-+ return false;
-+}
-+
-+inline void
-+DhtSearch::set_node_active(const_accessor& n, bool active) {
-+ n.node()->m_lastSeen = active;
-+}
-+
-+inline bool
-+dht_compare_closer::operator () (const DhtNode* one, const DhtNode* two) const {
-+ return DhtSearch::is_closer(*one, *two, m_target);
-+}
-+
-+}
-+
-+#endif
-Index: libtorrent/src/dht/dht_node.h
-===================================================================
---- libtorrent/src/dht/dht_node.h (revision 0)
-+++ libtorrent/src/dht/dht_node.h (revision 0)
-@@ -0,0 +1,136 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_DHT_NODE_H
-+#define LIBTORRENT_DHT_NODE_H
-+
-+#include "globals.h"
-+
-+#include <rak/socket_address.h>
-+
-+#include "torrent/hash_string.h"
-+
-+#include "dht_bucket.h"
-+
-+namespace torrent {
-+
-+class DhtBucket;
-+
-+class DhtNode : public HashString {
-+ friend class DhtSearch;
-+
-+public:
-+ // A node is considered bad if it failed to reply to this many queries.
-+ static const unsigned int max_failed_replies = 5;
-+
-+ DhtNode(const HashString& id, const rak::socket_address* sa);
-+ DhtNode(const std::string& id, const Object& cache);
-+
-+ const HashString& id() const { return *this; }
-+ const rak::socket_address* address() const { return &m_socketAddress; }
-+ void set_address(const rak::socket_address* sa) { m_socketAddress = *sa; }
-+
-+ // For determining node quality.
-+ unsigned int last_seen() const { return m_lastSeen; }
-+ unsigned int age() const { return cachedTime.seconds() - m_lastSeen; }
-+ bool is_good() const { return m_recentlyActive; }
-+ bool is_questionable() const { return !m_recentlyActive; }
-+ bool is_bad() const { return m_recentlyInactive >= max_failed_replies; };
-+ bool is_active() const { return m_lastSeen; }
-+
-+ // Update is called once every 15 minutes.
-+ void update() { m_recentlyActive = age() < 15 * 60; }
-+
-+ // Called when node replies to us, queries us, or fails to reply.
-+ void replied() { set_good(); }
-+ void queried() { if (m_lastSeen) set_good(); }
-+ void inactive();
-+
-+ DhtBucket* bucket() const { return m_bucket; }
-+ DhtBucket* set_bucket(DhtBucket* b) { m_bucket = b; return b; }
-+
-+ bool is_in_range(const DhtBucket* b) { return b->is_in_range(*this); }
-+
-+ // Store compact node information (26 bytes address, port and ID) in the given
-+ // buffer and return pointer to end of stored information.
-+ char* store_compact(char* buffer) const;
-+
-+ // Store node cache in the given container object and return it.
-+ Object* store_cache(Object* container) const;
-+
-+private:
-+ DhtNode();
-+
-+ void set_good();
-+ void set_bad();
-+
-+ rak::socket_address m_socketAddress;
-+ unsigned int m_lastSeen;
-+ bool m_recentlyActive;
-+ unsigned int m_recentlyInactive;
-+ DhtBucket* m_bucket;
-+};
-+
-+inline void
-+DhtNode::set_good() {
-+ if (m_bucket != NULL && !is_good())
-+ m_bucket->node_now_good(is_bad());
-+
-+ m_lastSeen = cachedTime.seconds();
-+ m_recentlyInactive = 0;
-+ m_recentlyActive = true;
-+}
-+
-+inline void
-+DhtNode::set_bad() {
-+ if (m_bucket != NULL && !is_bad())
-+ m_bucket->node_now_bad(is_good());
-+
-+ m_recentlyInactive = max_failed_replies;
-+ m_recentlyActive = false;
-+}
-+
-+inline void
-+DhtNode::inactive() {
-+ if (m_recentlyInactive + 1 == max_failed_replies)
-+ set_bad();
-+ else
-+ m_recentlyInactive++;
-+}
-+
-+}
-+
-+#endif
-Index: libtorrent/src/dht/dht_server.h
-===================================================================
---- libtorrent/src/dht/dht_server.h (revision 0)
-+++ libtorrent/src/dht/dht_server.h (revision 0)
-@@ -0,0 +1,186 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_DHT_SERVER_H
-+#define LIBTORRENT_DHT_SERVER_H
-+
-+#include <map>
-+#include <deque>
-+#include <rak/priority_queue_default.h>
-+#include <rak/socket_address.h>
-+
-+#include "net/socket_datagram.h"
-+#include "net/throttle_node.h"
-+#include "download/download_info.h" // for SocketAddressCompact
-+#include "torrent/hash_string.h"
-+
-+namespace torrent {
-+
-+class DhtBucket;
-+class DhtNode;
-+class DhtRouter;
-+
-+class DownloadInfo;
-+class TrackerDht;
-+
-+class DhtTransaction;
-+class DhtTransactionPacket;
-+class DhtTransactionSearch;
-+class DhtTransactionGetPeers;
-+
-+// UDP server that handles the DHT node communications.
-+
-+class DhtServer : public SocketDatagram {
-+public:
-+ DhtServer(DhtRouter* self);
-+ ~DhtServer();
-+
-+ void start(int port);
-+ void stop();
-+ bool is_active() const { return get_fd().is_valid(); }
-+
-+ unsigned int queries_received() const { return m_queriesReceived; }
-+ unsigned int queries_sent() const { return m_queriesSent; }
-+ unsigned int replies_received() const { return m_repliesReceived; }
-+ void reset_statistics();
-+
-+ // Contact a node to see if it replies. Set id=0 if unknown.
-+ void ping(const HashString& id, const rak::socket_address* sa);
-+
-+ // Do a find_node search with the given contacts as starting point for the
-+ // search.
-+ void find_node(const DhtBucket& contacts, const HashString& target);
-+
-+ // Do DHT announce, starting with the given contacts.
-+ void announce(const DhtBucket& contacts, const HashString& infoHash, TrackerDht* tracker);
-+
-+ // Cancel given announce for given tracker, or all matching announces if info/tracker NULL.
-+ void cancel_announce(DownloadInfo* info, const TrackerDht* tracker);
-+
-+ // Called every 15 minutes.
-+ void update();
-+
-+ ThrottleNode* upload_throttle() { return &m_uploadThrottle; }
-+ const ThrottleNode* upload_throttle() const { return &m_uploadThrottle; }
-+ ThrottleNode* download_throttle() { return &m_downloadThrottle; }
-+ const ThrottleNode* download_throttle() const { return &m_downloadThrottle; }
-+
-+ virtual void event_read();
-+ virtual void event_write();
-+ virtual void event_error();
-+
-+private:
-+ // DHT error codes.
-+ static const int dht_error_generic = 201;
-+ static const int dht_error_server = 202;
-+ static const int dht_error_protocol = 203;
-+ static const int dht_error_bad_method = 204;
-+
-+ typedef std::deque<DhtTransactionPacket*> packet_queue;
-+
-+ struct compact_node_info {
-+ char _id[20];
-+ SocketAddressCompact _addr;
-+
-+ HashString& id() { return *HashString::cast_from(_id); }
-+ rak::socket_address address() { return rak::socket_address(_addr); }
-+ } __attribute__ ((packed));
-+ typedef std::list<compact_node_info> node_info_list;
-+
-+ // Pending transactions (using DhtTransaction::key as key).
-+ typedef std::map<uint64_t, DhtTransaction*> transaction_map;
-+ typedef transaction_map::iterator transaction_itr;
-+
-+ // DHT transaction names for given transaction type.
-+ static const char* queries[];
-+
-+ // Priorities for the outgoing packets.
-+ static const int packet_prio_high = 2; // For important queries we send (announces).
-+ static const int packet_prio_low = 1; // For (relatively) unimportant queries we send.
-+ static const int packet_prio_reply = 0; // For replies to peer queries.
-+
-+ void start_write();
-+
-+ void process_query(const Object& transaction, const HashString& id, const rak::socket_address* sa, Object& req);
-+ void process_response(int transaction, const HashString& id, const rak::socket_address* sa, Object& req);
-+ void process_error(int transaction, const rak::socket_address* sa, Object& req);
-+
-+ void parse_node_info(node_info_list& l, const std::string& n);
-+ void parse_find_node_reply(DhtTransactionSearch* t, const std::string& nodes);
-+ void parse_get_peers_reply(DhtTransactionGetPeers* t, const Object& res);
-+
-+ void find_node_next(DhtTransactionSearch* t);
-+
-+ void add_packet(DhtTransactionPacket* packet, int priority);
-+ void create_query(transaction_itr itr, int tID, const rak::socket_address* sa, int priority);
-+ void create_response(const Object& transaction, const rak::socket_address* sa, Object& r);
-+ void create_error(const Object& transaction, const rak::socket_address* sa, int num, const std::string& msg);
-+
-+ void create_find_node_response(const Object& arg, Object& reply);
-+ void create_get_peers_response(const Object& arg, const rak::socket_address* sa, Object& reply);
-+ void create_announce_peer_response(const Object& arg, const rak::socket_address* sa, Object& reply);
-+
-+ int add_transaction(DhtTransaction* t, int priority);
-+
-+ // These methods return the iterator after the given one or end()
-+ transaction_itr failed_transaction(transaction_itr itr, bool quick);
-+ transaction_itr cancel_transaction(transaction_itr itr);
-+
-+ void clear_transactions();
-+
-+ bool process_queue(packet_queue& queue, uint32_t* quota);
-+ void receive_timeout();
-+
-+ DhtRouter* m_router;
-+ packet_queue m_highQueue;
-+ packet_queue m_lowQueue;
-+ transaction_map m_transactions;
-+
-+ rak::priority_item m_taskTimeout;
-+
-+ ThrottleNode m_uploadThrottle;
-+ ThrottleNode m_downloadThrottle;
-+
-+ unsigned int m_queriesReceived;
-+ unsigned int m_queriesSent;
-+ unsigned int m_repliesReceived;
-+
-+ bool m_networkUp;
-+};
-+
-+}
-+
-+#endif
-Index: libtorrent/src/dht/dht_tracker.cc
-===================================================================
---- libtorrent/src/dht/dht_tracker.cc (revision 0)
-+++ libtorrent/src/dht/dht_tracker.cc (revision 0)
-@@ -0,0 +1,129 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+
-+#include "torrent/object.h"
-+
-+#include "dht_tracker.h"
-+
-+namespace torrent {
-+
-+void
-+DhtTracker::add_peer(const rak::socket_address* sa) {
-+ // Should check that sa is an INET address.
-+ SocketAddressCompact compact(sa->sa_inet()->address_n(), sa->sa_inet()->port_n());
-+
-+ unsigned int oldest = 0;
-+ uint32_t minSeen = ~uint32_t();
-+
-+ // Check if peer exists. If not, find oldest peer.
-+ for (unsigned int i = 0; i < size(); i++) {
-+ if ((*this)[i].addr == compact.addr) {
-+ (*this)[i].port = compact.port;
-+ m_lastSeen[i] = cachedTime.seconds();
-+ return;
-+
-+ } else if (m_lastSeen[i] < minSeen) {
-+ minSeen = m_lastSeen[i];
-+ oldest = i;
-+ }
-+ }
-+
-+ // If peer doesn't exist, append to list if the table is not full.
-+ if (size() < max_size) {
-+ push_back(compact);
-+ m_lastSeen.push_back(cachedTime.seconds());
-+
-+ // Peer doesn't exist and table is full: replace oldest peer.
-+ } else {
-+ (*this)[oldest] = compact;
-+ m_lastSeen[oldest] = cachedTime.seconds();
-+ }
-+}
-+
-+// Return compact info (6 bytes) for up to 30 peers, returning different
-+// peers for each call if there are more.
-+std::string
-+DhtTracker::get_peers(unsigned int maxPeers) {
-+ iterator first;
-+ iterator last;
-+
-+ // If we have more than max_peers, randomly return block of peers.
-+ // The peers in overlapping blocks get picked twice as often, but
-+ // that's better than returning fewer peers.
-+ if (size() > maxPeers) {
-+ unsigned int blocks = (size() + maxPeers - 1) / maxPeers;
-+
-+ first = begin() + (random() % blocks) * (size() - maxPeers) / (blocks - 1);
-+ last = first + maxPeers;
-+
-+ } else {
-+ first = begin();
-+ last = end();
-+ }
-+
-+ return std::string(first->c_str(), last->c_str());
-+}
-+
-+// Remove old announces.
-+void
-+DhtTracker::prune(uint32_t maxAge) {
-+ uint32_t minSeen = cachedTime.seconds() - maxAge;
-+ unsigned int i;
-+
-+ for (i = 0; i < size(); i++)
-+ if (m_lastSeen[i] <= minSeen)
-+ break;
-+
-+ if (i == size())
-+ return; // No old peers found.
-+
-+ unsigned int index = i;
-+
-+ for (i++; i < size(); i++) {
-+ if (m_lastSeen[i] > minSeen) {
-+ m_lastSeen[index] = m_lastSeen[i];
-+ (*this)[index] = (*this)[i];
-+ index++;
-+ }
-+ }
-+
-+ resize(index);
-+ m_lastSeen.resize(index);
-+}
-+
-+}
-Index: libtorrent/src/net/throttle_node.h
-===================================================================
---- libtorrent/src/net/throttle_node.h (revision 975)
-+++ libtorrent/src/net/throttle_node.h (working copy)
-@@ -45,13 +45,13 @@
-
- namespace torrent {
-
--class PeerConnectionBase;
-+class SocketBase;
-
- class ThrottleNode {
- public:
- typedef ThrottleList::iterator iterator;
- typedef ThrottleList::const_iterator const_iterator;
-- typedef rak::mem_fun0<PeerConnectionBase, void> SlotActivate;
-+ typedef rak::mem_fun0<SocketBase, void> SlotActivate;
-
- ThrottleNode(uint32_t rateSpan) : m_rate(rateSpan) { clear_quota(); }
-
-Index: libtorrent/src/net/socket_base.cc
-===================================================================
---- libtorrent/src/net/socket_base.cc (revision 975)
-+++ libtorrent/src/net/socket_base.cc (working copy)
-@@ -41,6 +41,8 @@
- #include <sys/socket.h>
-
- #include "torrent/exceptions.h"
-+#include "torrent/poll.h"
-+#include "manager.h"
- #include "socket_base.h"
-
- namespace torrent {
-@@ -72,4 +74,14 @@
- return r == 1;
- }
-
-+void
-+SocketBase::receive_throttle_down_activate() {
-+ manager->poll()->insert_read(this);
-+}
-+
-+void
-+SocketBase::receive_throttle_up_activate() {
-+ manager->poll()->insert_write(this);
-+}
-+
- } // namespace torrent
-Index: libtorrent/src/net/socket_base.h
-===================================================================
---- libtorrent/src/net/socket_base.h (revision 975)
-+++ libtorrent/src/net/socket_base.h (working copy)
-@@ -60,6 +60,9 @@
- bool read_oob(void* buffer);
- bool write_oob(const void* buffer);
-
-+ void receive_throttle_down_activate();
-+ void receive_throttle_up_activate();
-+
- protected:
- // Disable copying
- SocketBase(const SocketBase&);
-Index: libtorrent/src/tracker/tracker_control.cc
-===================================================================
---- libtorrent/src/tracker/tracker_control.cc (revision 975)
-+++ libtorrent/src/tracker/tracker_control.cc (working copy)
-@@ -43,6 +43,7 @@
- #include "net/address_list.h"
- #include "torrent/exceptions.h"
- #include "tracker_control.h"
-+#include "tracker_dht.h"
- #include "tracker_http.h"
- #include "tracker_udp.h"
-
-@@ -73,6 +74,9 @@
- else if (std::strncmp("udp://", url.c_str(), 6) == 0)
- t = new TrackerUdp(m_info, url);
-
-+ else if (std::strncmp("dht://", url.c_str(), 6) == 0)
-+ t = new TrackerDht(m_info, url);
-+
- else
- // TODO: Error message here?... not really...
- return;
-@@ -101,7 +105,7 @@
- m_tries = -1;
- m_state = s;
-
-- m_itr = m_list.find_enabled(m_itr);
-+ m_itr = m_list.find_usable(m_itr);
-
- if (m_itr != m_list.end())
- m_itr->second->send_state(m_state,
-Index: libtorrent/src/tracker/tracker_dht.h
-===================================================================
---- libtorrent/src/tracker/tracker_dht.h (revision 0)
-+++ libtorrent/src/tracker/tracker_dht.h (revision 0)
-@@ -0,0 +1,72 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#ifndef LIBTORRENT_TRACKER_TRACKER_DHT_H
-+#define LIBTORRENT_TRACKER_TRACKER_DHT_H
-+
-+#include "net/address_list.h"
-+#include "torrent/object.h"
-+#include "tracker_base.h"
-+
-+namespace torrent {
-+
-+class TrackerDht : public TrackerBase {
-+public:
-+ TrackerDht(DownloadInfo* info, const std::string& url);
-+ ~TrackerDht();
-+
-+ virtual bool is_busy() const;
-+ virtual bool is_usable() const;
-+
-+ virtual void send_state(DownloadInfo::State state, uint64_t down, uint64_t up, uint64_t left);
-+ virtual void close();
-+
-+ virtual Type type() const;
-+
-+ bool has_peers() const { return !m_peers.empty(); }
-+
-+ void receive_peers(const std::string& peers);
-+ void receive_success();
-+ void receive_failed(const std::string& msg);
-+ void receive_status(int replied, int contacted, const char* stage);
-+
-+private:
-+ AddressList m_peers;
-+};
-+
-+}
-+
-+#endif
-Index: libtorrent/src/tracker/tracker_container.cc
-===================================================================
---- libtorrent/src/tracker/tracker_container.cc (revision 975)
-+++ libtorrent/src/tracker/tracker_container.cc (working copy)
-@@ -47,10 +47,13 @@
-
- namespace torrent {
-
-+struct tracker_usable_t : public std::unary_function<TrackerContainer::value_type, bool> {
-+ bool operator () (const TrackerContainer::value_type& value) const { return value.second->is_usable(); }
-+};
-+
- bool
--TrackerContainer::has_enabled() const {
-- return std::find_if(begin(), end(),
-- rak::on(rak::mem_ref(&value_type::second), std::mem_fun(&TrackerBase::is_enabled)))
-+TrackerContainer::has_usable() const {
-+ return std::find_if(begin(), end(), tracker_usable_t())
- != end();
- }
-
-@@ -99,8 +102,8 @@
- }
-
- TrackerContainer::iterator
--TrackerContainer::find_enabled(iterator itr) {
-- while (itr != end() && !itr->second->is_enabled())
-+TrackerContainer::find_usable(iterator itr) {
-+ while (itr != end() && !tracker_usable_t()(*itr))
- ++itr;
-
- return itr;
-Index: libtorrent/src/tracker/tracker_container.h
-===================================================================
---- libtorrent/src/tracker/tracker_container.h (revision 975)
-+++ libtorrent/src/tracker/tracker_container.h (working copy)
-@@ -72,7 +72,7 @@
-
- ~TrackerContainer() { clear(); }
-
-- bool has_enabled() const;
-+ bool has_usable() const;
-
- void randomize();
- void clear();
-@@ -82,7 +82,7 @@
- iterator promote(iterator itr);
-
- iterator find(TrackerBase* tb);
-- iterator find_enabled(iterator itr);
-+ iterator find_usable(iterator itr);
- const_iterator find_enabled(const_iterator itr) const;
-
- iterator begin_group(int group);
-Index: libtorrent/src/tracker/tracker_udp.cc
-===================================================================
---- libtorrent/src/tracker/tracker_udp.cc (revision 975)
-+++ libtorrent/src/tracker/tracker_udp.cc (working copy)
-@@ -36,9 +36,6 @@
-
- #include "config.h"
-
--#include <sigc++/bind.h>
--#include <torrent/connection_manager.h>
--
- #include "net/address_list.h"
- #include "torrent/exceptions.h"
- #include "torrent/connection_manager.h"
-@@ -64,7 +61,7 @@
-
- close();
- }
--
-+
- bool
- TrackerUdp::is_busy() const {
- return get_fd().is_valid();
-@@ -91,7 +88,7 @@
- m_sendUp = up;
- m_sendLeft = left;
-
-- m_slotResolver = manager->connection_manager()->resolver()(hostname, PF_INET, SOCK_DGRAM,
-+ m_slotResolver = manager->connection_manager()->resolver()(hostname, (int)rak::socket_address::pf_inet, SOCK_DGRAM,
- sigc::mem_fun(this, &TrackerUdp::start_announce));
- }
-
-Index: libtorrent/src/tracker/Makefile.am
-===================================================================
---- libtorrent/src/tracker/Makefile.am (revision 975)
-+++ libtorrent/src/tracker/Makefile.am (working copy)
-@@ -4,6 +4,8 @@
- tracker_base.h \
- tracker_control.cc \
- tracker_control.h \
-+ tracker_dht.cc \
-+ tracker_dht.h \
- tracker_http.cc \
- tracker_http.h \
- tracker_container.cc \
-Index: libtorrent/src/tracker/tracker_base.h
-===================================================================
---- libtorrent/src/tracker/tracker_base.h (revision 975)
-+++ libtorrent/src/tracker/tracker_base.h (working copy)
-@@ -58,7 +58,8 @@
- typedef enum {
- TRACKER_NONE,
- TRACKER_HTTP,
-- TRACKER_UDP
-+ TRACKER_UDP,
-+ TRACKER_DHT
- } Type;
-
- TrackerBase(DownloadInfo* info, const std::string& url) :
-@@ -69,6 +70,7 @@
-
- virtual bool is_busy() const = 0;
- bool is_enabled() const { return m_enabled; }
-+ virtual bool is_usable() const { return m_enabled; }
-
- void enable(bool state) { m_enabled = state; }
-
-@@ -81,6 +83,8 @@
- const std::string& url() const { return m_url; }
- void set_url(const std::string& url) { m_url = url; }
-
-+ const std::string& status() const { return m_status; }
-+
- const std::string& tracker_id() const { return m_trackerId; }
- void set_tracker_id(const std::string& id) { m_trackerId = id; }
-
-@@ -106,6 +110,7 @@
-
- DownloadInfo* m_info;
- std::string m_url;
-+ std::string m_status;
-
- std::string m_trackerId;
-
-Index: libtorrent/src/tracker/tracker_dht.cc
-===================================================================
---- libtorrent/src/tracker/tracker_dht.cc (revision 0)
-+++ libtorrent/src/tracker/tracker_dht.cc (revision 0)
-@@ -0,0 +1,151 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+
-+#include <sstream>
-+
-+#include "dht/dht_router.h"
-+#include "torrent/connection_manager.h"
-+#include "torrent/dht_manager.h"
-+#include "torrent/exceptions.h"
-+
-+#include "tracker_dht.h"
-+#include "tracker_http.h"
-+
-+#include "globals.h"
-+#include "manager.h"
-+
-+namespace torrent {
-+
-+TrackerDht::TrackerDht(DownloadInfo* info, const std::string& url) :
-+ TrackerBase(info, url) {
-+
-+ if (!manager->dht_manager()->is_valid())
-+ throw internal_error("Trying to add DHT tracker with no DHT manager.");
-+}
-+
-+TrackerDht::~TrackerDht() {
-+ if (is_busy())
-+ manager->dht_manager()->router()->cancel_announce(NULL, this);
-+}
-+
-+bool
-+TrackerDht::is_busy() const {
-+ return !m_status.empty();
-+}
-+
-+bool
-+TrackerDht::is_usable() const {
-+ return m_enabled && manager->dht_manager()->is_active();
-+}
-+
-+void
-+TrackerDht::send_state(DownloadInfo::State state, uint64_t down, uint64_t up, uint64_t left) {
-+ if (m_info == NULL)
-+ throw internal_error("TrackerDht::send_state(...) does not have a valid m_info.");
-+
-+ if (is_busy()) {
-+ manager->dht_manager()->router()->cancel_announce(m_info, this);
-+
-+ if (is_busy())
-+ throw internal_error("TrackerDht::send_state cancel_announce did not cancel announce.");
-+ }
-+
-+ if (state == DownloadInfo::STOPPED)
-+ return;
-+
-+ m_status = "[Initializing]";
-+
-+ if (!manager->dht_manager()->is_active())
-+ return receive_failed("DHT server not active.");
-+
-+ manager->dht_manager()->router()->announce(m_info, this);
-+
-+ set_normal_interval(20 * 60);
-+ set_min_interval(0);
-+}
-+
-+void
-+TrackerDht::close() {
-+ if (is_busy())
-+ manager->dht_manager()->router()->cancel_announce(m_info, this);
-+}
-+
-+TrackerDht::Type
-+TrackerDht::type() const {
-+ return TRACKER_DHT;
-+}
-+
-+void
-+TrackerDht::receive_peers(const std::string& peers) {
-+ if (!is_busy())
-+ throw internal_error("TrackerDht::receive_peers called while not busy.");
-+
-+ m_peers.parse_address_compact(peers);
-+}
-+
-+void
-+TrackerDht::receive_success() {
-+ if (!is_busy())
-+ throw internal_error("TrackerDht::receive_success called while not busy.");
-+
-+ m_status.clear();
-+ m_slotSuccess(this, &m_peers);
-+ m_peers.clear();
-+}
-+
-+void
-+TrackerDht::receive_failed(const std::string& msg) {
-+ if (!is_busy())
-+ throw internal_error("TrackerDht::receive_failed called while not busy.");
-+
-+ m_status.clear();
-+ m_slotFailed(this, msg);
-+ m_peers.clear();
-+}
-+
-+void
-+TrackerDht::receive_status(int replied, int contacted, const char* stage) {
-+ if (!is_busy())
-+ throw internal_error("TrackerDht::receive_status called while not busy.");
-+
-+ std::stringstream s;
-+ s << "[" << stage << ": " << replied << "/" << contacted << " nodes replied]";
-+ m_status = s.str();
-+}
-+
-+}
-Index: libtorrent/src/torrent/tracker.cc
-===================================================================
---- libtorrent/src/torrent/tracker.cc (revision 975)
-+++ libtorrent/src/torrent/tracker.cc (working copy)
-@@ -46,6 +46,11 @@
- return m_tracker.second->is_enabled();
- }
-
-+bool
-+Tracker::is_usable() const {
-+ return m_tracker.second->is_usable();
-+}
-+
- void
- Tracker::enable() {
- m_tracker.second->enable(true);
-@@ -67,6 +72,11 @@
- }
-
- const std::string&
-+Tracker::status() const {
-+ return m_tracker.second->status();
-+}
-+
-+const std::string&
- Tracker::tracker_id() const {
- return m_tracker.second->tracker_id();
- }
-Index: libtorrent/src/torrent/torrent.cc
-===================================================================
---- libtorrent/src/torrent/torrent.cc (revision 975)
-+++ libtorrent/src/torrent/torrent.cc (working copy)
-@@ -165,6 +165,11 @@
- return manager->connection_manager();
- }
-
-+DhtManager*
-+dht_manager() {
-+ return manager->dht_manager();
-+}
-+
- uint32_t
- total_handshakes() {
- return manager->handshake_manager()->size();
-Index: libtorrent/src/torrent/tracker.h
-===================================================================
---- libtorrent/src/torrent/tracker.h (revision 975)
-+++ libtorrent/src/torrent/tracker.h (working copy)
-@@ -58,6 +58,7 @@
- Tracker(value_type v) : m_tracker(v) {}
-
- bool is_enabled() const;
-+ bool is_usable() const;
- bool is_open() const;
-
- void enable();
-@@ -65,6 +66,7 @@
-
- uint32_t group() const { return m_tracker.first; }
- const std::string& url() const;
-+ const std::string& status() const;
-
- // The "tracker id" string returned by the tracker.
- const std::string& tracker_id() const;
-Index: libtorrent/src/torrent/torrent.h
-===================================================================
---- libtorrent/src/torrent/torrent.h (revision 975)
-+++ libtorrent/src/torrent/torrent.h (working copy)
-@@ -65,6 +65,7 @@
- ChunkManager* chunk_manager() LIBTORRENT_EXPORT;
- ClientList* client_list() LIBTORRENT_EXPORT;
- ConnectionManager* connection_manager() LIBTORRENT_EXPORT;
-+DhtManager* dht_manager() LIBTORRENT_EXPORT;
-
- uint32_t total_handshakes() LIBTORRENT_EXPORT;
-
-Index: libtorrent/src/torrent/peer/peer_info.h
-===================================================================
---- libtorrent/src/torrent/peer/peer_info.h (revision 975)
-+++ libtorrent/src/torrent/peer/peer_info.h (working copy)
-@@ -80,6 +80,7 @@
- uint32_t last_connection() const { return m_lastConnection; }
- void set_last_connection(uint32_t tvsec) { m_lastConnection = tvsec; }
-
-+ bool supports_dht() const { return m_options[7] & 0x01; }
- bool supports_extensions() const { return m_options[5] & 0x10; }
-
- // Internal to libTorrent:
-Index: libtorrent/src/torrent/dht_manager.cc
-===================================================================
---- libtorrent/src/torrent/dht_manager.cc (revision 0)
-+++ libtorrent/src/torrent/dht_manager.cc (revision 0)
-@@ -0,0 +1,119 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+#include "config.h"
-+
-+#include <rak/address_info.h>
-+#include <rak/socket_address.h>
-+
-+#include <torrent/exceptions.h>
-+
-+#include "manager.h"
-+
-+#include "dht/dht_router.h"
-+#include "dht/dht_server.h"
-+#include "dht_manager.h"
-+#include "net/throttle_manager.h"
-+
-+namespace torrent {
-+
-+DhtManager::~DhtManager() {
-+ stop();
-+ delete m_router;
-+}
-+
-+void
-+DhtManager::initialize(const Object& dhtCache) {
-+ if (m_router != NULL)
-+ throw internal_error("DhtManager::initialize called with DHT already active.");
-+
-+ m_router = new DhtRouter(dhtCache, rak::socket_address::cast_from(manager->connection_manager()->bind_address()));
-+}
-+
-+void
-+DhtManager::start(port_type port) {
-+ if (m_router == NULL)
-+ throw internal_error("DhtManager::start called without initializing first.");
-+
-+ m_router->start(port);
-+}
-+
-+
-+void
-+DhtManager::stop() {
-+ if (m_router != NULL)
-+ m_router->stop();
-+}
-+
-+bool
-+DhtManager::is_active() const {
-+ return m_router != NULL && m_router->is_active();
-+}
-+
-+void
-+DhtManager::add_node(const sockaddr* addr, int port, bool external) {
-+ if (m_router == NULL)
-+ return;
-+
-+ rak::socket_address sa = *rak::socket_address::cast_from(addr);
-+
-+ if (sa.family() != rak::socket_address::af_inet)
-+ return;
-+
-+
-+ sa.set_port(port);
-+ m_router->contact(&sa, external);
-+}
-+
-+Object*
-+DhtManager::store_cache(Object* container) const {
-+ if (m_router == NULL)
-+ throw internal_error("DhtManager::store_cache called but DHT not initialized.");
-+
-+ return m_router->store_cache(container);
-+}
-+
-+DhtManager::statistics_type
-+DhtManager::get_statistics() const {
-+ return m_router->get_statistics();
-+}
-+
-+void
-+DhtManager::reset_statistics() {
-+ m_router->reset_statistics();
-+}
-+
-+}
-Index: libtorrent/src/torrent/dht_manager.h
-===================================================================
---- libtorrent/src/torrent/dht_manager.h (revision 0)
-+++ libtorrent/src/torrent/dht_manager.h (revision 0)
-@@ -0,0 +1,125 @@
-+// libTorrent - BitTorrent library
-+// Copyright (C) 2005-2007, Jari Sundell
-+//
-+// This program is free software; you can redistribute it and/or modify
-+// it under the terms of the GNU General Public License as published by
-+// the Free Software Foundation; either version 2 of the License, or
-+// (at your option) any later version.
-+//
-+// This program is distributed in the hope that it will be useful,
-+// but WITHOUT ANY WARRANTY; without even the implied warranty of
-+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+// GNU General Public License for more details.
-+//
-+// You should have received a copy of the GNU General Public License
-+// along with this program; if not, write to the Free Software
-+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+//
-+// In addition, as a special exception, the copyright holders give
-+// permission to link the code of portions of this program with the
-+// OpenSSL library under certain conditions as described in each
-+// individual source file, and distribute linked combinations
-+// including the two.
-+//
-+// You must obey the GNU General Public License in all respects for
-+// all of the code used other than OpenSSL. If you modify file(s)
-+// with this exception, you may extend this exception to your version
-+// of the file(s), but you are not obligated to do so. If you do not
-+// wish to do so, delete this exception statement from your version.
-+// If you delete this exception statement from all source files in the
-+// program, then also delete it here.
-+//
-+// Contact: Jari Sundell <jaris@ifi.uio.no>
-+//
-+// Skomakerveien 33
-+// 3185 Skoppum, NORWAY
-+
-+// Add some helpfull words here.
-+
-+#ifndef LIBTORRENT_DHT_MANAGER_H
-+#define LIBTORRENT_DHT_MANAGER_H
-+
-+#include <torrent/common.h>
-+#include <torrent/connection_manager.h>
-+
-+namespace torrent {
-+
-+class ThrottleList;
-+
-+class LIBTORRENT_EXPORT DhtManager {
-+public:
-+ typedef ConnectionManager::port_type port_type;
-+
-+ struct statistics_type {
-+ // Cycle; 0=inactive, 1=initial bootstrapping, 2 and up=normal operation
-+ unsigned int cycle;
-+
-+ // UDP transfer rates.
-+ const Rate& up_rate;
-+ const Rate& down_rate;
-+
-+ // DHT query statistics.
-+ unsigned int queries_received;
-+ unsigned int queries_sent;
-+ unsigned int replies_received;
-+
-+ // DHT node info.
-+ unsigned int num_nodes;
-+ unsigned int num_buckets;
-+
-+ // DHT tracker info.
-+ unsigned int num_peers;
-+ unsigned int max_peers;
-+ unsigned int num_trackers;
-+
-+ statistics_type(const Rate& up, const Rate& down) : up_rate(up), down_rate(down) { }
-+ };
-+
-+ DhtManager() : m_router(NULL), m_portSent(0), m_canReceive(true) { };
-+ ~DhtManager();
-+
-+ void initialize(const Object& dhtCache);
-+
-+ void start(port_type port);
-+ void stop();
-+
-+ // Store DHT cache in the given container and return the container.
-+ Object* store_cache(Object* container) const;
-+
-+ bool is_valid() const { return m_router; }
-+ bool is_active() const;
-+
-+ port_type port() const { return m_port; }
-+
-+ bool can_receive_queries() const { return m_canReceive; }
-+
-+ // Call this after sending the port to a client, so the router knows
-+ // that it should be getting requests.
-+ void port_sent() { m_portSent++; }
-+
-+ // Add a node by address, e.g. from a torrent file (external = true), or from
-+ // explicit add_node commands or the BT PORT message (external = false).
-+ void add_node(const sockaddr* addr, int port, bool external);
-+
-+ statistics_type get_statistics() const;
-+ void reset_statistics();
-+
-+ // To be called if upon examining the statistics, the client decides that
-+ // we can't receive outside requests and therefore shouldn't advertise our
-+ // UDP port after the BT handshake.
-+ void set_can_receive(bool can) { m_canReceive = can; }
-+
-+ // Internal libTorrent use only
-+ DhtRouter* router() { return m_router; }
-+
-+private:
-+ DhtRouter* m_router;
-+ port_type m_port;
-+
-+ int m_portSent;
-+ bool m_canReceive;
-+};
-+
-+}
-+
-+#endif
-Index: libtorrent/src/torrent/hash_string.h
-===================================================================
---- libtorrent/src/torrent/hash_string.h (revision 975)
-+++ libtorrent/src/torrent/hash_string.h (working copy)
-@@ -83,6 +83,10 @@
-
- const value_type* c_str() const { return m_data; }
-
-+ std::string str() const { return std::string(m_data, size_data); }
-+
-+ void clear(int v = 0) { std::memset(data(), v, size()); }
-+
- void assign(const value_type* src) { std::memcpy(data(), src, size()); }
-
- bool equal_to(const char* hash) const { return std::memcmp(m_data, hash, size()) == 0; }
-@@ -93,6 +97,8 @@
- static const HashString* cast_from(const char* src) { return (const HashString*)src; }
- static const HashString* cast_from(const std::string& src) { return (const HashString*)src.c_str(); }
-
-+ static HashString* cast_from(char* src) { return (HashString*)src; }
-+
- private:
- char m_data[size_data];
- };
-@@ -103,10 +109,20 @@
- }
-
- inline bool
-+operator != (const HashString& one, const HashString& two) {
-+ return std::memcmp(one.begin(), two.begin(), HashString::size_data) != 0;
-+}
-+
-+inline bool
- operator < (const HashString& one, const HashString& two) {
- return std::memcmp(one.begin(), two.begin(), HashString::size_data) < 0;
- }
-
-+inline bool
-+operator <= (const HashString& one, const HashString& two) {
-+ return std::memcmp(one.begin(), two.begin(), HashString::size_data) <= 0;
- }
-
-+}
-+
- #endif
-Index: libtorrent/src/torrent/Makefile.am
-===================================================================
---- libtorrent/src/torrent/Makefile.am (revision 975)
-+++ libtorrent/src/torrent/Makefile.am (working copy)
-@@ -12,6 +12,8 @@
- common.h \
- connection_manager.cc \
- connection_manager.h \
-+ dht_manager.cc \
-+ dht_manager.h \
- download.cc \
- download.h \
- error.cc \
-@@ -54,6 +56,7 @@
- chunk_manager.h \
- common.h \
- connection_manager.h \
-+ dht_manager.h \
- download.h \
- error.h \
- exceptions.h \
-Index: libtorrent/src/torrent/common.h
-===================================================================
---- libtorrent/src/torrent/common.h (revision 975)
-+++ libtorrent/src/torrent/common.h (working copy)
-@@ -67,6 +67,8 @@
- class ClientList;
- class ConnectionList;
- class ConnectionManager;
-+class DhtManager;
-+class DhtRouter;
- class Download;
- class DownloadMain;
- class DownloadWrapper;
-Index: libtorrent/src/manager.cc
-===================================================================
---- libtorrent/src/manager.cc (revision 975)
-+++ libtorrent/src/manager.cc (working copy)
-@@ -50,6 +50,7 @@
-
- #include "torrent/chunk_manager.h"
- #include "torrent/connection_manager.h"
-+#include "torrent/dht_manager.h"
- #include "torrent/data/file_manager.h"
- #include "torrent/peer/client_list.h"
-
-@@ -70,6 +71,7 @@
- m_chunkManager(new ChunkManager),
- m_clientList(new ClientList),
- m_connectionManager(new ConnectionManager),
-+ m_dhtManager(new DhtManager),
-
- m_poll(NULL),
-
-@@ -99,6 +101,7 @@
- delete m_hashQueue;
-
- delete m_resourceManager;
-+ delete m_dhtManager;
- delete m_connectionManager;
- delete m_chunkManager;
-
-Index: libtorrent/src/manager.h
-===================================================================
---- libtorrent/src/manager.h (revision 975)
-+++ libtorrent/src/manager.h (working copy)
-@@ -58,6 +58,7 @@
- class ChunkManager;
- class ConnectionManager;
- class ThrottleManager;
-+class DhtManager;
-
- typedef std::list<std::string> EncodingList;
-
-@@ -75,6 +76,7 @@
- ChunkManager* chunk_manager() { return m_chunkManager; }
- ClientList* client_list() { return m_clientList; }
- ConnectionManager* connection_manager() { return m_connectionManager; }
-+ DhtManager* dht_manager() { return m_dhtManager; }
-
- Poll* poll() { return m_poll; }
- void set_poll(Poll* p) { m_poll = p; }
-@@ -99,6 +101,7 @@
- ChunkManager* m_chunkManager;
- ClientList* m_clientList;
- ConnectionManager* m_connectionManager;
-+ DhtManager* m_dhtManager;
- Poll* m_poll;
-
- EncodingList m_encodingList;
-Index: libtorrent/src/protocol/protocol_base.h
-===================================================================
---- libtorrent/src/protocol/protocol_base.h (revision 975)
-+++ libtorrent/src/protocol/protocol_base.h (working copy)
-@@ -62,6 +62,7 @@
- REQUEST,
- PIECE,
- CANCEL,
-+ PORT,
-
- EXTENSION_PROTOCOL = 20,
-
-@@ -108,6 +109,7 @@
- void write_request(const Piece& p);
- void write_cancel(const Piece& p);
- void write_piece(const Piece& p);
-+ void write_port(uint16_t port);
- void write_extension(uint8_t id, uint32_t length);
-
- static const size_type sizeof_keepalive = 4;
-@@ -122,6 +124,8 @@
- static const size_type sizeof_cancel_body = 12;
- static const size_type sizeof_piece = 13;
- static const size_type sizeof_piece_body = 8;
-+ static const size_type sizeof_port = 7;
-+ static const size_type sizeof_port_body = 2;
- static const size_type sizeof_extension = 6;
- static const size_type sizeof_extension_body=1;
-
-@@ -133,12 +137,14 @@
- bool can_write_request() const { return m_buffer.reserved_left() >= sizeof_request; }
- bool can_write_cancel() const { return m_buffer.reserved_left() >= sizeof_cancel; }
- bool can_write_piece() const { return m_buffer.reserved_left() >= sizeof_piece; }
-+ bool can_write_port() const { return m_buffer.reserved_left() >= sizeof_port; }
- bool can_write_extension() const { return m_buffer.reserved_left() >= sizeof_extension; }
-
- bool can_read_have_body() const { return m_buffer.remaining() >= sizeof_have_body; }
- bool can_read_request_body() const { return m_buffer.remaining() >= sizeof_request_body; }
- bool can_read_cancel_body() const { return m_buffer.remaining() >= sizeof_request_body; }
- bool can_read_piece_body() const { return m_buffer.remaining() >= sizeof_piece_body; }
-+ bool can_read_port_body() const { return m_buffer.remaining() >= sizeof_port_body; }
- bool can_read_extension_body() const { return m_buffer.remaining() >= sizeof_extension_body; }
-
- protected:
-@@ -223,6 +229,13 @@
- }
-
- inline void
-+ProtocolBase::write_port(uint16_t port) {
-+ m_buffer.write_32(3);
-+ write_command(PORT);
-+ m_buffer.write_16(port);
-+}
-+
-+inline void
- ProtocolBase::write_extension(uint8_t id, uint32_t length) {
- m_buffer.write_32(2 + length);
- write_command(EXTENSION_PROTOCOL);
-Index: libtorrent/src/protocol/peer_connection_base.cc
-===================================================================
---- libtorrent/src/protocol/peer_connection_base.cc (revision 975)
-+++ libtorrent/src/protocol/peer_connection_base.cc (working copy)
-@@ -51,6 +51,7 @@
- #include "download/download_main.h"
- #include "net/socket_base.h"
- #include "torrent/connection_manager.h"
-+#include "torrent/dht_manager.h"
- #include "torrent/peer/peer_info.h"
-
- #include "extensions.h"
-@@ -118,10 +119,10 @@
- m_peerChunks.bitfield()->swap(*bitfield);
-
- m_peerChunks.upload_throttle()->set_list_iterator(m_download->upload_throttle()->end());
-- m_peerChunks.upload_throttle()->slot_activate(rak::make_mem_fun(this, &PeerConnectionBase::receive_throttle_up_activate));
-+ m_peerChunks.upload_throttle()->slot_activate(rak::make_mem_fun(static_cast<SocketBase*>(this), &SocketBase::receive_throttle_up_activate));
-
- m_peerChunks.download_throttle()->set_list_iterator(m_download->download_throttle()->end());
-- m_peerChunks.download_throttle()->slot_activate(rak::make_mem_fun(this, &PeerConnectionBase::receive_throttle_down_activate));
-+ m_peerChunks.download_throttle()->slot_activate(rak::make_mem_fun(static_cast<SocketBase*>(this), &SocketBase::receive_throttle_down_activate));
-
- download_queue()->set_delegator(m_download->delegator());
- download_queue()->set_peer_chunks(&m_peerChunks);
-@@ -323,16 +324,6 @@
- }
-
- void
--PeerConnectionBase::receive_throttle_down_activate() {
-- manager->poll()->insert_read(this);
--}
--
--void
--PeerConnectionBase::receive_throttle_up_activate() {
-- manager->poll()->insert_write(this);
--}
--
--void
- PeerConnectionBase::event_error() {
- m_download->connection_list()->erase(this, 0);
- }
-Index: libtorrent/src/protocol/peer_connection_base.h
-===================================================================
---- libtorrent/src/protocol/peer_connection_base.h (revision 975)
-+++ libtorrent/src/protocol/peer_connection_base.h (working copy)
-@@ -140,9 +140,6 @@
-
- void load_up_chunk();
-
-- void receive_throttle_down_activate();
-- void receive_throttle_up_activate();
--
- void read_request_piece(const Piece& p);
- void read_cancel_piece(const Piece& p);
-
-Index: libtorrent/src/protocol/peer_connection_leech.cc
-===================================================================
---- libtorrent/src/protocol/peer_connection_leech.cc (revision 975)
-+++ libtorrent/src/protocol/peer_connection_leech.cc (working copy)
-@@ -45,6 +45,8 @@
- #include "download/chunk_statistics.h"
- #include "download/download_info.h"
- #include "download/download_main.h"
-+#include "torrent/dht_manager.h"
-+#include "torrent/peer/peer_info.h"
-
- #include "extensions.h"
- #include "peer_connection_leech.h"
-@@ -275,6 +277,13 @@
- read_cancel_piece(m_down->read_request());
- return true;
-
-+ case ProtocolBase::PORT:
-+ if (!m_down->can_read_port_body())
-+ break;
-+
-+ manager->dht_manager()->add_node(m_peerInfo->socket_address(), m_down->buffer()->read_16(), false);
-+ return true;
-+
- case ProtocolBase::EXTENSION_PROTOCOL:
- if (!m_down->can_read_extension_body())
- break;
-Index: libtorrent/src/protocol/peer_connection_seed.cc
-===================================================================
---- libtorrent/src/protocol/peer_connection_seed.cc (revision 975)
-+++ libtorrent/src/protocol/peer_connection_seed.cc (working copy)
-@@ -43,6 +43,8 @@
- #include "download/chunk_statistics.h"
- #include "download/download_info.h"
- #include "download/download_main.h"
-+#include "torrent/dht_manager.h"
-+#include "torrent/peer/peer_info.h"
-
- #include "extensions.h"
- #include "peer_connection_seed.h"
-@@ -175,6 +177,13 @@
- read_cancel_piece(m_down->read_request());
- return true;
-
-+ case ProtocolBase::PORT:
-+ if (!m_down->can_read_port_body())
-+ break;
-+
-+ manager->dht_manager()->add_node(m_peerInfo->socket_address(), m_down->buffer()->read_16(), false);
-+ return true;
-+
- case ProtocolBase::EXTENSION_PROTOCOL:
- if (!m_down->can_read_extension_body())
- break;
-Index: libtorrent/src/protocol/handshake.cc
-===================================================================
---- libtorrent/src/protocol/handshake.cc (revision 975)
-+++ libtorrent/src/protocol/handshake.cc (working copy)
-@@ -38,6 +38,7 @@
-
- #include "download/download_info.h"
- #include "download/download_main.h"
-+#include "torrent/dht_manager.h"
- #include "net/throttle_list.h"
- #include "net/throttle_manager.h"
- #include "torrent/exceptions.h"
-@@ -522,18 +523,19 @@
-
- // The download is just starting so we're not sending any
- // bitfield.
-- if (m_download->file_list()->bitfield()->is_all_unset())
-- prepare_keepalive();
-- else
-+ if (m_download->file_list()->bitfield()->is_all_unset()) {
-+ prepare_post_handshake(true);
-+
-+ } else {
- prepare_bitfield();
-
-- m_state = BITFIELD;
-+ // Give some extra time for reading/writing the bitfield.
-+ priority_queue_erase(&taskScheduler, &m_taskTimeout);
-+ priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(120)).round_seconds());
-+ }
-+
- manager->poll()->insert_write(this);
-
-- // Give some extra time for reading/writing the bitfield.
-- priority_queue_erase(&taskScheduler, &m_taskTimeout);
-- priority_queue_insert(&taskScheduler, &m_taskTimeout, (cachedTime + rak::timer::from_seconds(120)).round_seconds());
--
- return false;
- }
-
-@@ -731,6 +733,7 @@
- goto restart;
-
- case BITFIELD:
-+ case POST_HANDSHAKE:
- read_bitfield();
- break;
-
-@@ -864,8 +867,12 @@
- if (!m_writeBuffer.remaining())
- throw internal_error("event_write called with empty write buffer.");
-
-- if (m_writeBuffer.consume(write_unthrottled(m_writeBuffer.position(), m_writeBuffer.remaining())))
-- manager->poll()->remove_write(this);
-+ if (m_writeBuffer.consume(write_stream_throws(m_writeBuffer.position(), m_writeBuffer.remaining()))) {
-+ if (m_state == POST_HANDSHAKE)
-+ write_done();
-+ else
-+ manager->poll()->remove_write(this);
-+ }
-
- } catch (handshake_succeeded& e) {
- m_manager->receive_succeeded(this);
-@@ -948,6 +955,8 @@
- m_writeBuffer.write_range(m_protocol, m_protocol + 19);
-
- std::memset(m_writeBuffer.end(), 0, 8);
-+ if (manager->dht_manager()->is_active())
-+ *(m_writeBuffer.end()+7) |= 0x01; // DHT support, enable PORT message
- m_writeBuffer.move_end(8);
-
- m_writeBuffer.write_range(m_download->info()->hash().c_str(), m_download->info()->hash().c_str() + 20);
-@@ -990,20 +999,50 @@
- m_encryption.info()->encrypt(m_writeBuffer.end() - 5, 5);
-
- m_writePos = 0;
-+
-+ m_state = BITFIELD;
- }
-
- void
--Handshake::prepare_keepalive() {
-- m_writeBuffer.write_32(0);
-+Handshake::prepare_post_handshake(bool must_write) {
-+ m_state = POST_HANDSHAKE;
-
-- if (m_encryption.info()->is_encrypted())
-- m_encryption.info()->encrypt(m_writeBuffer.end() - 4, 4);
-+ // Send PORT message for DHT if enabled and peer supports it.
-+ if (m_peerInfo->supports_dht() && manager->dht_manager()->is_active() && manager->dht_manager()->can_receive_queries()) {
-+ m_writeBuffer.write_32(3);
-+ m_writeBuffer.write_8(protocol_port);
-+ m_writeBuffer.write_16(manager->dht_manager()->port());
-+ manager->dht_manager()->port_sent();
-+ if (m_encryption.info()->is_encrypted())
-+ m_encryption.info()->encrypt(m_writeBuffer.end() - 7, 7);
-
-- // Skip writting the bitfield.
-- m_writePos = m_download->file_list()->bitfield()->size_bytes();
-+ must_write = false;
-+ }
-+
-+ // Send a keep-alive if we still must send something.
-+ if (must_write) {
-+ m_writeBuffer.write_32(0);
-+
-+ if (m_encryption.info()->is_encrypted())
-+ m_encryption.info()->encrypt(m_writeBuffer.end() - 4, 4);
-+ }
-+
-+ if (!m_writeBuffer.remaining())
-+ write_done();
- }
-
- void
-+Handshake::write_done() {
-+ m_writeDone = true;
-+ manager->poll()->remove_write(this);
-+
-+ // Ok to just check m_readDone as the call in event_read() won't
-+ // set it before the call.
-+ if (m_readDone)
-+ throw handshake_succeeded();
-+}
-+
-+void
- Handshake::write_extension_handshake() {
- if (m_extensions->is_default())
- m_extensions = new ProtocolExtension;
-@@ -1059,15 +1098,8 @@
- }
- }
-
-- if (m_writePos == bitfield->size_bytes()) {
-- m_writeDone = true;
-- manager->poll()->remove_write(this);
--
-- // Ok to just check m_readDone as the call in event_read() won't
-- // set it before the call.
-- if (m_readDone)
-- throw handshake_succeeded();
-- }
-+ if (m_writePos == bitfield->size_bytes())
-+ prepare_post_handshake(false);
- }
-
- void
-Index: libtorrent/src/protocol/handshake.h
-===================================================================
---- libtorrent/src/protocol/handshake.h (revision 975)
-+++ libtorrent/src/protocol/handshake.h (working copy)
-@@ -74,6 +74,7 @@
- INACTIVE,
- CONNECTING,
- BITFIELD,
-+ POST_HANDSHAKE,
-
- PROXY_CONNECT,
- PROXY_DONE,
-@@ -130,6 +131,7 @@
- void operator = (const Handshake&);
-
- void read_done();
-+ void write_done();
-
- bool fill_read_buffer(int size);
-
-@@ -151,7 +153,7 @@
- void prepare_handshake();
- void prepare_peer_info();
- void prepare_bitfield();
-- void prepare_keepalive();
-+ void prepare_post_handshake(bool must_write);
-
- void write_extension_handshake();
- void write_bitfield();
-Index: libtorrent/src/download/download_constructor.cc
-===================================================================
---- libtorrent/src/download/download_constructor.cc (revision 975)
-+++ libtorrent/src/download/download_constructor.cc (working copy)
-@@ -42,6 +42,7 @@
- #include <rak/string_manip.h>
-
- #include "download/download_wrapper.h"
-+#include "torrent/dht_manager.h"
- #include "torrent/exceptions.h"
- #include "torrent/object.h"
- #include "torrent/data/file.h"
-@@ -50,6 +51,8 @@
-
- #include "download_constructor.h"
-
-+#include "manager.h"
-+
- namespace torrent {
-
- struct download_constructor_is_single_path {
-@@ -167,9 +170,16 @@
- else if (b.has_key("announce"))
- add_tracker_single(b.get_key("announce"), 0);
-
-- else
-+ else if (!manager->dht_manager()->is_valid() || m_download->info()->is_private())
- throw bencode_error("Could not find any trackers");
-
-+ if (manager->dht_manager()->is_valid() && !m_download->info()->is_private())
-+ tracker->insert(tracker->group_size(), "dht://");
-+
-+ if (manager->dht_manager()->is_valid() && b.has_key_list("nodes"))
-+ std::for_each(b.get_key_list("nodes").begin(), b.get_key_list("nodes").end(),
-+ rak::make_mem_fun(this, &DownloadConstructor::add_dht_node));
-+
- tracker->randomize();
- }
-
-@@ -191,6 +201,35 @@
- m_download->main()->tracker_manager()->insert(group, rak::trim_classic(b.as_string()));
- }
-
-+struct call_add_node_t {
-+ call_add_node_t(int port) : m_port(port) { }
-+
-+ void operator() (const sockaddr* sa, int err) {
-+ if (sa != NULL)
-+ manager->dht_manager()->add_node(sa, m_port, true);
-+ }
-+
-+ int m_port;
-+};
-+
-+void
-+DownloadConstructor::add_dht_node(const Object& b) {
-+ if (!b.is_list() || b.as_list().size() < 2)
-+ return;
-+
-+ Object::list_type::const_iterator el = b.as_list().begin();
-+
-+ if (!el->is_string())
-+ return;
-+
-+ const std::string& host = el->as_string();
-+
-+ if (!(++el)->is_value())
-+ return;
-+
-+ manager->connection_manager()->resolver()(host.c_str(), (int)rak::socket_address::pf_inet, SOCK_DGRAM, call_add_node_t(el->as_value()));
-+}
-+
- bool
- DownloadConstructor::is_valid_path_element(const Object& b) {
- return
-Index: libtorrent/src/download/download_constructor.h
-===================================================================
---- libtorrent/src/download/download_constructor.h (revision 975)
-+++ libtorrent/src/download/download_constructor.h (working copy)
-@@ -67,6 +67,7 @@
-
- void add_tracker_group(const Object& b);
- void add_tracker_single(const Object& b, int group);
-+ void add_dht_node(const Object& b);
-
- static bool is_valid_path_element(const Object& b);
- static bool is_invalid_path_element(const Object& b) { return !is_valid_path_element(b); }
-Index: libtorrent/src/Makefile.am
-===================================================================
---- libtorrent/src/Makefile.am (revision 975)
-+++ libtorrent/src/Makefile.am (working copy)
-@@ -1,6 +1,7 @@
- SUBDIRS = \
- torrent \
- data \
-+ dht \
- download \
- net \
- protocol \
-@@ -15,6 +16,7 @@
- $(top_srcdir)/src/torrent/data/libsub_torrentdata.la \
- $(top_srcdir)/src/torrent/peer/libsub_torrentpeer.la \
- $(top_srcdir)/src/data/libsub_data.la \
-+ $(top_srcdir)/src/dht/libsub_dht.la \
- $(top_srcdir)/src/download/libsub_download.la \
- $(top_srcdir)/src/net/libsub_net.la \
- $(top_srcdir)/src/protocol/libsub_protocol.la \
-Index: libtorrent/configure.ac
-===================================================================
---- libtorrent/configure.ac (revision 975)
-+++ libtorrent/configure.ac (working copy)
-@@ -2,6 +2,7 @@
-
- dnl Find a better way to do this
- AC_DEFINE(PEER_NAME, "-lt0B80-", Identifier that is part of the default peer id)
-+AC_DEFINE(PEER_VERSION, "lt\x0B\x80", 4 byte client and version identifier for DHT)
-
- LIBTORRENT_CURRENT=10
- LIBTORRENT_REVISION=8
-@@ -32,6 +33,7 @@
- TORRENT_ENABLE_DEBUG
- TORRENT_ENABLE_EXTRA_DEBUG
- TORRENT_ENABLE_WERROR
-+TORRENT_ENABLE_TR1
-
- TORRENT_DISABLE_IPV6
-
-@@ -104,6 +106,7 @@
- src/torrent/peer/Makefile
- src/torrent/data/Makefile
- src/data/Makefile
-+ src/dht/Makefile
- src/download/Makefile
- src/net/Makefile
- src/protocol/Makefile