first version with DHT support. Limited pipelining used by url-seeds. Fixed one configuration problem on FreeBSD.

This commit is contained in:
Arvid Norberg 2006-08-01 15:27:08 +00:00
parent f1548fd668
commit 5ef57265bf
52 changed files with 5211 additions and 212 deletions

View File

@ -1,11 +1,18 @@
* added support for trackerless torrents (with kademlia DHT).
* support for torrents with the private flag set.
* support for torrents containing bootstrap nodes for the
DHT network.
* fixed problem with the configure script on FreeBSD.
* limits the pipelining used on url-seeds.
* fixed problem where the shutdown always would delay for
session_settings::stop_tracker_timeout seconds.
* session::listen_on() won't reopen the socket in case the port and
interface is the same as is already open.
* Added http proxy support for web seeds.
* Fixed problem where upload and download stats could become incorrect
interface is the same as the one currently in use.
* added http proxy support for web seeds.
* fixed problem where upload and download stats could become incorrect
in case of high cpu load.
* Added more clients to the identifiable list.
* Fixed fingerprint parser to cope with latest Mainline versions.
* added more clients to the identifiable list.
* fixed fingerprint parser to cope with latest Mainline versions.
release 0.10

12
Jamfile
View File

@ -27,6 +27,7 @@ project torrent
<include>./zlib
<include>$(BOOST_ROOT)
<variant>release:<define>NDEBUG
<variant>debug:<define>TORRENT_DHT_VERBOSE_LOGGING
<define>BOOST_ALL_NO_LIB
<define>_FILE_OFFSET_BITS=64
<define>BOOST_THREAD_USE_LIB
@ -54,6 +55,7 @@ project torrent
<include>./include
<include>$(BOOST_ROOT)
<variant>release:<define>NDEBUG
<variant>debug:<define>TORRENT_DHT_VERBOSE_LOGGING
<define>BOOST_ALL_NO_LIB
<link>shared:<define>TORRENT_LINKING_SHARED
@ -82,6 +84,16 @@ SOURCES =
http_tracker_connection.cpp
udp_tracker_connection.cpp
sha1.cpp
kademlia/closest_nodes.cpp
kademlia/dht_tracker.cpp
kademlia/node.cpp
kademlia/refresh.cpp
kademlia/rpc_manager.cpp
kademlia/find_data.cpp
kademlia/node_id.cpp
kademlia/routing_table.cpp
kademlia/traversal_algorithm.cpp
;
ZLIB_SOURCES =

View File

@ -4,8 +4,10 @@ docs/extension_protocol.html docs/udp_tracker_protocol.rst \
docs/projects.rst docs/projects.html \
docs/arctic_thumb.png \
docs/bitbuddy_thumb.jpg \
docs/bitscast_thumb.png \
docs/bitslug_thumb.png \
docs/btg_thumb.jpg \
docs/electric_sheep_thumb.jpg \
docs/moopolice_thumb.gif \
docs/qbittorrent_thumb.jpg \
docs/ziptorrent_thumb.gif \

View File

@ -12,11 +12,12 @@
<h1 class="title">libtorrent</h1>
<table border="1" class="menu docutils">
<colgroup>
<col width="26%" />
<col width="19%" />
<col width="19%" />
<col width="14%" />
<col width="14%" />
<col width="11%" />
<col width="15%" />
<col width="21%" />
<col width="26%" />
</colgroup>
<tbody valign="top">
<tr><td><a class="reference" href="http://www.sourceforge.net/projects/libtorrent">sourceforge page</a></td>
@ -24,6 +25,7 @@
<td><a class="reference" href="http://sourceforge.net/tracker/?group_id=79942&amp;atid=558250">report bugs</a></td>
<td><a class="reference" href="client_test.png">screenshot</a></td>
<td><a class="reference" href="http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss">mailing list</a></td>
<td><a class="reference" href="projects.html">who's using libtorrent?</a></td>
</tr>
</tbody>
</table>

View File

@ -5,15 +5,16 @@ libtorrent
.. class:: menu
=================== ============== ============== =========== ===============
`sourceforge page`_ documentation_ `report bugs`_ screenshot_ `mailing list`_
=================== ============== ============== =========== ===============
=================== ============== ============== =========== =============== ==========================
`sourceforge page`_ documentation_ `report bugs`_ screenshot_ `mailing list`_ `who's using libtorrent?`_
=================== ============== ============== =========== =============== ==========================
.. _sourceforge page: http://www.sourceforge.net/projects/libtorrent
.. _documentation: manual.html
.. _`report bugs`: http://sourceforge.net/tracker/?group_id=79942&atid=558250
.. _screenshot: client_test.png
.. _mailing list: http://lists.sourceforge.net/lists/listinfo/libtorrent-discuss
.. _`who's using libtorrent?`: projects.html
libtorrent is a C++ library that aims to be a good alternative to all the
other bittorrent implementations around. It is a

View File

@ -46,127 +46,131 @@
<li><a class="reference" href="#status" id="id43" name="id43">status()</a></li>
<li><a class="reference" href="#is-listening-listen-port-listen-on" id="id44" name="id44">is_listening() listen_port() listen_on()</a></li>
<li><a class="reference" href="#pop-alert-set-severity-level" id="id45" name="id45">pop_alert() set_severity_level()</a></li>
<li><a class="reference" href="#start-dht-stop-dht-set-dht-settings-dht-state" id="id46" name="id46">start_dht() stop_dht() set_dht_settings() dht_state()</a></li>
</ul>
</li>
<li><a class="reference" href="#entry" id="id46" name="id46">entry</a><ul>
<li><a class="reference" href="#integer-string-list-dict-type" id="id47" name="id47">integer() string() list() dict() type()</a></li>
<li><a class="reference" href="#operator" id="id48" name="id48">operator[]</a></li>
<li><a class="reference" href="#find-key" id="id49" name="id49">find_key()</a></li>
<li><a class="reference" href="#entry" id="id47" name="id47">entry</a><ul>
<li><a class="reference" href="#integer-string-list-dict-type" id="id48" name="id48">integer() string() list() dict() type()</a></li>
<li><a class="reference" href="#operator" id="id49" name="id49">operator[]</a></li>
<li><a class="reference" href="#find-key" id="id50" name="id50">find_key()</a></li>
</ul>
</li>
<li><a class="reference" href="#torrent-info" id="id50" name="id50">torrent_info</a><ul>
<li><a class="reference" href="#id12" id="id51" name="id51">torrent_info()</a></li>
<li><a class="reference" href="#set-comment-set-piece-size-set-creator-set-hash-add-tracker-add-file" id="id52" name="id52">set_comment() set_piece_size() set_creator() set_hash() add_tracker() add_file()</a></li>
<li><a class="reference" href="#create-torrent" id="id53" name="id53">create_torrent()</a></li>
<li><a class="reference" href="#begin-files-end-files-rbegin-files-rend-files" id="id54" name="id54">begin_files() end_files() rbegin_files() rend_files()</a></li>
<li><a class="reference" href="#num-files-file-at" id="id55" name="id55">num_files() file_at()</a></li>
<li><a class="reference" href="#map-block" id="id56" name="id56">map_block()</a></li>
<li><a class="reference" href="#map-file" id="id57" name="id57">map_file()</a></li>
<li><a class="reference" href="#url-seeds" id="id58" name="id58">url_seeds()</a></li>
<li><a class="reference" href="#print" id="id59" name="id59">print()</a></li>
<li><a class="reference" href="#trackers" id="id60" name="id60">trackers()</a></li>
<li><a class="reference" href="#total-size-piece-length-piece-size-num-pieces" id="id61" name="id61">total_size() piece_length() piece_size() num_pieces()</a></li>
<li><a class="reference" href="#hash-for-piece-info-hash" id="id62" name="id62">hash_for_piece() info_hash()</a></li>
<li><a class="reference" href="#name-comment-creation-date-creator" id="id63" name="id63">name() comment() creation_date() creator()</a></li>
<li><a class="reference" href="#torrent-info" id="id51" name="id51">torrent_info</a><ul>
<li><a class="reference" href="#id12" id="id52" name="id52">torrent_info()</a></li>
<li><a class="reference" href="#set-comment-set-piece-size-set-creator-set-hash-add-tracker-add-file" id="id53" name="id53">set_comment() set_piece_size() set_creator() set_hash() add_tracker() add_file()</a></li>
<li><a class="reference" href="#create-torrent" id="id54" name="id54">create_torrent()</a></li>
<li><a class="reference" href="#begin-files-end-files-rbegin-files-rend-files" id="id55" name="id55">begin_files() end_files() rbegin_files() rend_files()</a></li>
<li><a class="reference" href="#num-files-file-at" id="id56" name="id56">num_files() file_at()</a></li>
<li><a class="reference" href="#map-block" id="id57" name="id57">map_block()</a></li>
<li><a class="reference" href="#map-file" id="id58" name="id58">map_file()</a></li>
<li><a class="reference" href="#url-seeds" id="id59" name="id59">url_seeds()</a></li>
<li><a class="reference" href="#print" id="id60" name="id60">print()</a></li>
<li><a class="reference" href="#trackers" id="id61" name="id61">trackers()</a></li>
<li><a class="reference" href="#total-size-piece-length-piece-size-num-pieces" id="id62" name="id62">total_size() piece_length() piece_size() num_pieces()</a></li>
<li><a class="reference" href="#hash-for-piece-info-hash" id="id63" name="id63">hash_for_piece() info_hash()</a></li>
<li><a class="reference" href="#name-comment-creation-date-creator" id="id64" name="id64">name() comment() creation_date() creator()</a></li>
<li><a class="reference" href="#priv-set-priv" id="id65" name="id65">priv() set_priv()</a></li>
<li><a class="reference" href="#nodes" id="id66" name="id66">nodes()</a></li>
<li><a class="reference" href="#add-node" id="id67" name="id67">add_node()</a></li>
</ul>
</li>
<li><a class="reference" href="#torrent-handle" id="id64" name="id64">torrent_handle</a><ul>
<li><a class="reference" href="#file-progress" id="id65" name="id65">file_progress()</a></li>
<li><a class="reference" href="#save-path" id="id66" name="id66">save_path()</a></li>
<li><a class="reference" href="#move-storage" id="id67" name="id67">move_storage()</a></li>
<li><a class="reference" href="#force-reannounce" id="id68" name="id68">force_reannounce()</a></li>
<li><a class="reference" href="#connect-peer" id="id69" name="id69">connect_peer()</a></li>
<li><a class="reference" href="#set-ratio" id="id70" name="id70">set_ratio()</a></li>
<li><a class="reference" href="#set-upload-limit-set-download-limit" id="id71" name="id71">set_upload_limit() set_download_limit()</a></li>
<li><a class="reference" href="#set-peer-upload-limit-set-peer-download-limit" id="id72" name="id72">set_peer_upload_limit() set_peer_download_limit()</a></li>
<li><a class="reference" href="#pause-resume-is-paused" id="id73" name="id73">pause() resume() is_paused()</a></li>
<li><a class="reference" href="#is-seed" id="id74" name="id74">is_seed()</a></li>
<li><a class="reference" href="#has-metadata" id="id75" name="id75">has_metadata()</a></li>
<li><a class="reference" href="#set-tracker-login" id="id76" name="id76">set_tracker_login()</a></li>
<li><a class="reference" href="#trackers-replace-trackers" id="id77" name="id77">trackers() replace_trackers()</a></li>
<li><a class="reference" href="#add-url-seed" id="id78" name="id78">add_url_seed()</a></li>
<li><a class="reference" href="#use-interface" id="id79" name="id79">use_interface()</a></li>
<li><a class="reference" href="#info-hash" id="id80" name="id80">info_hash()</a></li>
<li><a class="reference" href="#id14" id="id81" name="id81">set_max_uploads() set_max_connections()</a></li>
<li><a class="reference" href="#write-resume-data" id="id82" name="id82">write_resume_data()</a></li>
<li><a class="reference" href="#metadata" id="id83" name="id83">metadata()</a></li>
<li><a class="reference" href="#id15" id="id84" name="id84">status()</a></li>
<li><a class="reference" href="#get-download-queue" id="id85" name="id85">get_download_queue()</a></li>
<li><a class="reference" href="#get-peer-info" id="id86" name="id86">get_peer_info()</a></li>
<li><a class="reference" href="#get-torrent-info" id="id87" name="id87">get_torrent_info()</a></li>
<li><a class="reference" href="#is-valid" id="id88" name="id88">is_valid()</a></li>
<li><a class="reference" href="#torrent-handle" id="id68" name="id68">torrent_handle</a><ul>
<li><a class="reference" href="#file-progress" id="id69" name="id69">file_progress()</a></li>
<li><a class="reference" href="#save-path" id="id70" name="id70">save_path()</a></li>
<li><a class="reference" href="#move-storage" id="id71" name="id71">move_storage()</a></li>
<li><a class="reference" href="#force-reannounce" id="id72" name="id72">force_reannounce()</a></li>
<li><a class="reference" href="#connect-peer" id="id73" name="id73">connect_peer()</a></li>
<li><a class="reference" href="#set-ratio" id="id74" name="id74">set_ratio()</a></li>
<li><a class="reference" href="#set-upload-limit-set-download-limit" id="id75" name="id75">set_upload_limit() set_download_limit()</a></li>
<li><a class="reference" href="#set-peer-upload-limit-set-peer-download-limit" id="id76" name="id76">set_peer_upload_limit() set_peer_download_limit()</a></li>
<li><a class="reference" href="#pause-resume-is-paused" id="id77" name="id77">pause() resume() is_paused()</a></li>
<li><a class="reference" href="#is-seed" id="id78" name="id78">is_seed()</a></li>
<li><a class="reference" href="#has-metadata" id="id79" name="id79">has_metadata()</a></li>
<li><a class="reference" href="#set-tracker-login" id="id80" name="id80">set_tracker_login()</a></li>
<li><a class="reference" href="#trackers-replace-trackers" id="id81" name="id81">trackers() replace_trackers()</a></li>
<li><a class="reference" href="#add-url-seed" id="id82" name="id82">add_url_seed()</a></li>
<li><a class="reference" href="#use-interface" id="id83" name="id83">use_interface()</a></li>
<li><a class="reference" href="#info-hash" id="id84" name="id84">info_hash()</a></li>
<li><a class="reference" href="#id14" id="id85" name="id85">set_max_uploads() set_max_connections()</a></li>
<li><a class="reference" href="#write-resume-data" id="id86" name="id86">write_resume_data()</a></li>
<li><a class="reference" href="#metadata" id="id87" name="id87">metadata()</a></li>
<li><a class="reference" href="#id15" id="id88" name="id88">status()</a></li>
<li><a class="reference" href="#get-download-queue" id="id89" name="id89">get_download_queue()</a></li>
<li><a class="reference" href="#get-peer-info" id="id90" name="id90">get_peer_info()</a></li>
<li><a class="reference" href="#get-torrent-info" id="id91" name="id91">get_torrent_info()</a></li>
<li><a class="reference" href="#is-valid" id="id92" name="id92">is_valid()</a></li>
</ul>
</li>
<li><a class="reference" href="#torrent-status" id="id89" name="id89">torrent_status</a></li>
<li><a class="reference" href="#peer-info" id="id90" name="id90">peer_info</a></li>
<li><a class="reference" href="#session-settings" id="id91" name="id91">session_settings</a></li>
<li><a class="reference" href="#ip-filter" id="id92" name="id92">ip_filter</a><ul>
<li><a class="reference" href="#id18" id="id93" name="id93">ip_filter()</a></li>
<li><a class="reference" href="#add-rule" id="id94" name="id94">add_rule()</a></li>
<li><a class="reference" href="#access" id="id95" name="id95">access()</a></li>
<li><a class="reference" href="#export-filter" id="id96" name="id96">export_filter()</a></li>
<li><a class="reference" href="#torrent-status" id="id93" name="id93">torrent_status</a></li>
<li><a class="reference" href="#peer-info" id="id94" name="id94">peer_info</a></li>
<li><a class="reference" href="#session-settings" id="id95" name="id95">session_settings</a></li>
<li><a class="reference" href="#ip-filter" id="id96" name="id96">ip_filter</a><ul>
<li><a class="reference" href="#id18" id="id97" name="id97">ip_filter()</a></li>
<li><a class="reference" href="#add-rule" id="id98" name="id98">add_rule()</a></li>
<li><a class="reference" href="#access" id="id99" name="id99">access()</a></li>
<li><a class="reference" href="#export-filter" id="id100" name="id100">export_filter()</a></li>
</ul>
</li>
<li><a class="reference" href="#big-number" id="id97" name="id97">big_number</a></li>
<li><a class="reference" href="#hasher" id="id98" name="id98">hasher</a></li>
<li><a class="reference" href="#fingerprint" id="id99" name="id99">fingerprint</a></li>
<li><a class="reference" href="#free-functions" id="id100" name="id100">free functions</a><ul>
<li><a class="reference" href="#identify-client" id="id101" name="id101">identify_client()</a></li>
<li><a class="reference" href="#client-fingerprint" id="id102" name="id102">client_fingerprint()</a></li>
<li><a class="reference" href="#bdecode-bencode" id="id103" name="id103">bdecode() bencode()</a></li>
<li><a class="reference" href="#big-number" id="id101" name="id101">big_number</a></li>
<li><a class="reference" href="#hasher" id="id102" name="id102">hasher</a></li>
<li><a class="reference" href="#fingerprint" id="id103" name="id103">fingerprint</a></li>
<li><a class="reference" href="#free-functions" id="id104" name="id104">free functions</a><ul>
<li><a class="reference" href="#identify-client" id="id105" name="id105">identify_client()</a></li>
<li><a class="reference" href="#client-fingerprint" id="id106" name="id106">client_fingerprint()</a></li>
<li><a class="reference" href="#bdecode-bencode" id="id107" name="id107">bdecode() bencode()</a></li>
</ul>
</li>
<li><a class="reference" href="#alerts" id="id104" name="id104">alerts</a><ul>
<li><a class="reference" href="#listen-failed-alert" id="id105" name="id105">listen_failed_alert</a></li>
<li><a class="reference" href="#file-error-alert" id="id106" name="id106">file_error_alert</a></li>
<li><a class="reference" href="#tracker-announce-alert" id="id107" name="id107">tracker_announce_alert</a></li>
<li><a class="reference" href="#tracker-alert" id="id108" name="id108">tracker_alert</a></li>
<li><a class="reference" href="#tracker-reply-alert" id="id109" name="id109">tracker_reply_alert</a></li>
<li><a class="reference" href="#tracker-warning-alert" id="id110" name="id110">tracker_warning_alert</a></li>
<li><a class="reference" href="#url-seed-alert" id="id111" name="id111">url_seed_alert</a></li>
<li><a class="reference" href="#hash-failed-alert" id="id112" name="id112">hash_failed_alert</a></li>
<li><a class="reference" href="#peer-ban-alert" id="id113" name="id113">peer_ban_alert</a></li>
<li><a class="reference" href="#peer-error-alert" id="id114" name="id114">peer_error_alert</a></li>
<li><a class="reference" href="#invalid-request-alert" id="id115" name="id115">invalid_request_alert</a></li>
<li><a class="reference" href="#torrent-finished-alert" id="id116" name="id116">torrent_finished_alert</a></li>
<li><a class="reference" href="#metadata-failed-alert" id="id117" name="id117">metadata_failed_alert</a></li>
<li><a class="reference" href="#metadata-received-alert" id="id118" name="id118">metadata_received_alert</a></li>
<li><a class="reference" href="#fastresume-rejected-alert" id="id119" name="id119">fastresume_rejected_alert</a></li>
<li><a class="reference" href="#dispatcher" id="id120" name="id120">dispatcher</a></li>
<li><a class="reference" href="#alerts" id="id108" name="id108">alerts</a><ul>
<li><a class="reference" href="#listen-failed-alert" id="id109" name="id109">listen_failed_alert</a></li>
<li><a class="reference" href="#file-error-alert" id="id110" name="id110">file_error_alert</a></li>
<li><a class="reference" href="#tracker-announce-alert" id="id111" name="id111">tracker_announce_alert</a></li>
<li><a class="reference" href="#tracker-alert" id="id112" name="id112">tracker_alert</a></li>
<li><a class="reference" href="#tracker-reply-alert" id="id113" name="id113">tracker_reply_alert</a></li>
<li><a class="reference" href="#tracker-warning-alert" id="id114" name="id114">tracker_warning_alert</a></li>
<li><a class="reference" href="#url-seed-alert" id="id115" name="id115">url_seed_alert</a></li>
<li><a class="reference" href="#hash-failed-alert" id="id116" name="id116">hash_failed_alert</a></li>
<li><a class="reference" href="#peer-ban-alert" id="id117" name="id117">peer_ban_alert</a></li>
<li><a class="reference" href="#peer-error-alert" id="id118" name="id118">peer_error_alert</a></li>
<li><a class="reference" href="#invalid-request-alert" id="id119" name="id119">invalid_request_alert</a></li>
<li><a class="reference" href="#torrent-finished-alert" id="id120" name="id120">torrent_finished_alert</a></li>
<li><a class="reference" href="#metadata-failed-alert" id="id121" name="id121">metadata_failed_alert</a></li>
<li><a class="reference" href="#metadata-received-alert" id="id122" name="id122">metadata_received_alert</a></li>
<li><a class="reference" href="#fastresume-rejected-alert" id="id123" name="id123">fastresume_rejected_alert</a></li>
<li><a class="reference" href="#dispatcher" id="id124" name="id124">dispatcher</a></li>
</ul>
</li>
<li><a class="reference" href="#exceptions" id="id121" name="id121">exceptions</a><ul>
<li><a class="reference" href="#invalid-handle" id="id122" name="id122">invalid_handle</a></li>
<li><a class="reference" href="#duplicate-torrent" id="id123" name="id123">duplicate_torrent</a></li>
<li><a class="reference" href="#invalid-encoding" id="id124" name="id124">invalid_encoding</a></li>
<li><a class="reference" href="#type-error" id="id125" name="id125">type_error</a></li>
<li><a class="reference" href="#invalid-torrent-file" id="id126" name="id126">invalid_torrent_file</a></li>
<li><a class="reference" href="#exceptions" id="id125" name="id125">exceptions</a><ul>
<li><a class="reference" href="#invalid-handle" id="id126" name="id126">invalid_handle</a></li>
<li><a class="reference" href="#duplicate-torrent" id="id127" name="id127">duplicate_torrent</a></li>
<li><a class="reference" href="#invalid-encoding" id="id128" name="id128">invalid_encoding</a></li>
<li><a class="reference" href="#type-error" id="id129" name="id129">type_error</a></li>
<li><a class="reference" href="#invalid-torrent-file" id="id130" name="id130">invalid_torrent_file</a></li>
</ul>
</li>
<li><a class="reference" href="#examples" id="id127" name="id127">examples</a><ul>
<li><a class="reference" href="#dump-torrent" id="id128" name="id128">dump_torrent</a></li>
<li><a class="reference" href="#simple-client" id="id129" name="id129">simple client</a></li>
<li><a class="reference" href="#make-torrent" id="id130" name="id130">make_torrent</a></li>
<li><a class="reference" href="#examples" id="id131" name="id131">examples</a><ul>
<li><a class="reference" href="#dump-torrent" id="id132" name="id132">dump_torrent</a></li>
<li><a class="reference" href="#simple-client" id="id133" name="id133">simple client</a></li>
<li><a class="reference" href="#make-torrent" id="id134" name="id134">make_torrent</a></li>
</ul>
</li>
<li><a class="reference" href="#fast-resume" id="id131" name="id131">fast resume</a><ul>
<li><a class="reference" href="#file-format" id="id132" name="id132">file format</a></li>
<li><a class="reference" href="#fast-resume" id="id135" name="id135">fast resume</a><ul>
<li><a class="reference" href="#file-format" id="id136" name="id136">file format</a></li>
</ul>
</li>
<li><a class="reference" href="#threads" id="id133" name="id133">threads</a></li>
<li><a class="reference" href="#storage-allocation" id="id134" name="id134">storage allocation</a><ul>
<li><a class="reference" href="#full-allocation" id="id135" name="id135">full allocation</a></li>
<li><a class="reference" href="#compact-allocation" id="id136" name="id136">compact allocation</a></li>
<li><a class="reference" href="#threads" id="id137" name="id137">threads</a></li>
<li><a class="reference" href="#storage-allocation" id="id138" name="id138">storage allocation</a><ul>
<li><a class="reference" href="#full-allocation" id="id139" name="id139">full allocation</a></li>
<li><a class="reference" href="#compact-allocation" id="id140" name="id140">compact allocation</a></li>
</ul>
</li>
<li><a class="reference" href="#extensions" id="id137" name="id137">extensions</a><ul>
<li><a class="reference" href="#chat-messages" id="id138" name="id138">chat messages</a></li>
<li><a class="reference" href="#metadata-from-peers" id="id139" name="id139">metadata from peers</a></li>
<li><a class="reference" href="#http-seeding" id="id140" name="id140">HTTP seeding</a></li>
<li><a class="reference" href="#extensions" id="id141" name="id141">extensions</a><ul>
<li><a class="reference" href="#chat-messages" id="id142" name="id142">chat messages</a></li>
<li><a class="reference" href="#metadata-from-peers" id="id143" name="id143">metadata from peers</a></li>
<li><a class="reference" href="#http-seeding" id="id144" name="id144">HTTP seeding</a></li>
</ul>
</li>
<li><a class="reference" href="#filename-checks" id="id141" name="id141">filename checks</a></li>
<li><a class="reference" href="#acknowledgments" id="id142" name="id142">acknowledgments</a></li>
<li><a class="reference" href="#filename-checks" id="id145" name="id145">filename checks</a></li>
<li><a class="reference" href="#acknowledgments" id="id146" name="id146">acknowledgments</a></li>
</ul>
</div>
<div class="section" id="introduction">
@ -185,6 +189,7 @@ example client.</p>
project (including this documentation). The current state includes the
following features:</p>
<ul class="simple">
<li>Trackerless torrents (using a kademlia DHT)</li>
<li>multitracker extension support (as <a class="reference" href="http://home.elp.rr.com/tur/multitracker-spec.txt">specified by John Hoffman</a>)</li>
<li>serves multiple torrents on a single port and in a single thread</li>
<li>gzipped tracker-responses</li>
@ -580,6 +585,10 @@ should be defined when building libtorrent as
a shared library. (This is set by the Jamfile
when <tt class="docutils literal"><span class="pre">link=shared</span></tt> is set).</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">TORRENT_DISABLE_DHT</span></tt></td>
<td>If this is defined, the support for trackerless
torrents will be disabled.</td>
</tr>
</tbody>
</table>
<p>If you experience that libtorrent uses unreasonable amounts of cpu, it will
@ -667,9 +676,15 @@ class session: public boost::noncopyable
std::pair&lt;int, int&gt; const&amp; port_range
, char const* interface = 0);
std::auto_ptr&lt;alert&gt; pop_alert();
void set_severity_level(alert::severity_t s);
void start_dht();
void stop_dht();
void set_dht_settings(dht_settings const&amp; settings);
entry dht_state() const;
void add_dht_node(std::pair&lt;std::string, int&gt; const&amp; node);
};
</pre>
<p>Once it's created, the session object will spawn the main thread that will do all the work.
@ -921,6 +936,60 @@ void set_severity_level(alert::severity_t s);
<tt class="docutils literal"><span class="pre">set_severity_level()</span></tt> you can filter how serious the event has to be for you to
receive it through <tt class="docutils literal"><span class="pre">pop_alert()</span></tt>. For information, see <a class="reference" href="#alerts">alerts</a>.</p>
</div>
<div class="section" id="start-dht-stop-dht-set-dht-settings-dht-state">
<h2><a name="start-dht-stop-dht-set-dht-settings-dht-state">start_dht() stop_dht() set_dht_settings() dht_state()</a></h2>
<blockquote>
<pre class="literal-block">
void start_dht(entry const&amp; startup_state);
void stop_dht();
void set_dht_settings(dht_settings const&amp; settings);
entry dht_state() const;
void add_dht_node(std::pair&lt;std::string, int&gt; const&amp; node);
</pre>
</blockquote>
<p>These functions are not available in case <tt class="docutils literal"><span class="pre">TORRENT_DISABLE_DHT</span></tt> is
defined. <tt class="docutils literal"><span class="pre">start_dht</span></tt> starts the dht node and makes the trackerless service
available to torrents. The startup state is optional and can contain nodes
and the node id from the previous session. The dht node state is a bencoded
dictionary with the following entries:</p>
<dl class="docutils">
<dt><tt class="docutils literal"><span class="pre">nodes</span></tt></dt>
<dd>is a string with the nodes written as 6 bytes each. 4 bytes ip
address and 2 bytes port number. Both are written in big endian byte order.</dd>
<dt><tt class="docutils literal"><span class="pre">node-id</span></tt></dt>
<dd>The node id written as a readable string as a hexadecimal number.</dd>
</dl>
<p><tt class="docutils literal"><span class="pre">dht_state</span></tt> will return the current state of the dht node, this can be used
to start up the node again, passing this entry to <tt class="docutils literal"><span class="pre">start_dht</span></tt>. It is a good
idea to save this to disk when the session is closed, and read it up again
when starting.</p>
<p><tt class="docutils literal"><span class="pre">stop_dht</span></tt> stops the dht node.</p>
<p><tt class="docutils literal"><span class="pre">add_dht_node</span></tt> adds a node to the routing table. This can be used if your
client has its own source of bootstrapping nodes.</p>
<p><tt class="docutils literal"><span class="pre">set_dht_settings</span></tt> sets some parameters availavle to the dht node. The
struct has the following members:</p>
<pre class="literal-block">
struct dht_settings
{
int max_peers_reply;
int search_branching;
int service_port;
int max_fail_count;
};
</pre>
<p><tt class="docutils literal"><span class="pre">max_peers_reply</span></tt> is the maximum number of peers the node will send in
response to a <tt class="docutils literal"><span class="pre">get_peers</span></tt> message from another node.</p>
<p><tt class="docutils literal"><span class="pre">search_branching</span></tt> is the number of concurrent search request the node will
send when announcing and refreshing the routing table. This parameter is
called alpha in the kademlia paper.</p>
<p><tt class="docutils literal"><span class="pre">service_port</span></tt> is the udp port the node will listen to. (currently this
cannot be changed while the node is running).</p>
<p><tt class="docutils literal"><span class="pre">max_fail_count</span></tt> is the maximum number of failed tries to contact a node
before it is removed from the routing table. If there are known working nodes
that are ready to replace a failing node, it will be replaced immediately,
this limit is only used to clear out nodes that don't have any node that can
replace them.</p>
</div>
</div>
<div class="section" id="entry">
<h1><a name="entry">entry</a></h1>
@ -1113,6 +1182,9 @@ public:
std::vector&lt;announce_entry&gt; const&amp; trackers() const;
bool priv() const;
void set_priv(bool v);
std::vector&lt;std::string&gt; const&amp; url_seeds() const;
size_type total_size() const;
@ -1123,6 +1195,9 @@ public:
std::string const&amp; comment() const;
std::string const&amp; creator() const;
std::vector&lt;std::pair&lt;std::string, int&gt; &gt; const&amp; nodes() const;
void add_node(std::pair&lt;std::string, int&gt; const&amp; node);
boost::optional&lt;boost::posix_time::ptime&gt;
creation_date() const;
@ -1201,6 +1276,8 @@ You can save this data as a torrent file with bencode() (see <a class="reference
complete example, see <a class="reference" href="#make-torrent">make_torrent</a>.</p>
<p>This function is not const because it will also set the info-hash of the <tt class="docutils literal"><span class="pre">torrent_info</span></tt>
object.</p>
<p>Note that a torrent file must include at least one file, and it must have at
least one tracker url or at least one DHT node.</p>
</div>
<div class="section" id="begin-files-end-files-rbegin-files-rend-files">
<h2><a name="begin-files-end-files-rbegin-files-rend-files">begin_files() end_files() rbegin_files() rend_files()</a></h2>
@ -1390,6 +1467,38 @@ in the torrent file, this will return a date of January 1:st 1970.</p>
<p><tt class="docutils literal"><span class="pre">creator()</span></tt> returns the creator string in the torrent. If there is no creator string
it will return an empty string.</p>
</div>
<div class="section" id="priv-set-priv">
<h2><a name="priv-set-priv">priv() set_priv()</a></h2>
<blockquote>
<pre class="literal-block">
bool priv() const;
void set_priv(bool v);
</pre>
</blockquote>
<p><tt class="docutils literal"><span class="pre">priv()</span></tt> returns true if this torrent is private. i.e., it should not be
distributed on the trackerless network (the kademlia DHT).</p>
<p><tt class="docutils literal"><span class="pre">set_priv()</span></tt> sets or clears the private flag on this torrent.</p>
</div>
<div class="section" id="nodes">
<h2><a name="nodes">nodes()</a></h2>
<blockquote>
<pre class="literal-block">
std::vector&lt;std::pair&lt;std::string, int&gt; &gt; const&amp; nodes() const;
</pre>
</blockquote>
<p>If this torrent contains any DHT nodes, they are put in this vector in their original
form (host name and port number).</p>
</div>
<div class="section" id="add-node">
<h2><a name="add-node">add_node()</a></h2>
<blockquote>
<pre class="literal-block">
void add_node(std::pair&lt;std::string, int&gt; const&amp; node);
</pre>
</blockquote>
<p>This is used when creating torrent. Use this to add a known DHT node. It may
be used, by the client, to bootstrap into the DHT network.</p>
</div>
</div>
<div class="section" id="torrent-handle">
<h1><a name="torrent-handle">torrent_handle</a></h1>
@ -2141,6 +2250,7 @@ struct session_settings
int whole_pieces_threshold;
int peer_timeout;
int urlseed_timeout;
int urlseed_pipeline_size;
};
</pre>
<p><tt class="docutils literal"><span class="pre">proxy_ip</span></tt> may be a hostname or ip to a http proxy to use. If this is
@ -2204,6 +2314,10 @@ in the protocol specification. After half the time out, a keep alive message
is sent.</p>
<p><tt class="docutils literal"><span class="pre">urlseed_timeout</span></tt> is the same as <tt class="docutils literal"><span class="pre">peer_timeout</span></tt> but applies only to
url seeds. This value defaults to 20 seconds.</p>
<p><tt class="docutils literal"><span class="pre">urlseed_pipeline_size</span></tt> controls the pipelining with the web server. When
using persistent connections to HTTP 1.1 servers, the client is allowed to
send more requests before the first response is received. This number controls
the number of outstanding requests to use with url-seeds. Default is 5.</p>
</div>
<div class="section" id="ip-filter">
<h1><a name="ip-filter">ip_filter</a></h1>
@ -3360,7 +3474,6 @@ bittorrent client.</p>
<p>Extension name: &quot;chat&quot;</p>
<p>The payload in the packet is a bencoded dictionary with any
combination of the following entries:</p>
<blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="15%" />
@ -3381,7 +3494,6 @@ Any unrecognized strings should be ignored.</td>
</tr>
</tbody>
</table>
</blockquote>
</div>
<div class="section" id="metadata-from-peers">
<h2><a name="metadata-from-peers">metadata from peers</a></h2>
@ -3402,7 +3514,6 @@ are put as payload to the extension message. The three packets are:</p>
</ul>
</blockquote>
<p>request metadata:</p>
<blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="17%" />
@ -3441,9 +3552,7 @@ metadata.</td>
</tr>
</tbody>
</table>
</blockquote>
<p>metadata:</p>
<blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="17%" />
@ -3481,9 +3590,7 @@ protocol packet.</td>
</tr>
</tbody>
</table>
</blockquote>
<p>Don't have metadata:</p>
<blockquote>
<table border="1" class="docutils">
<colgroup>
<col width="17%" />
@ -3506,7 +3613,6 @@ doesn't have any metadata.</td>
</tr>
</tbody>
</table>
</blockquote>
</div>
<div class="section" id="http-seeding">
<h2><a name="http-seeding">HTTP seeding</a></h2>

View File

@ -27,6 +27,7 @@ libtorrent is still being developed, however it is stable. It is an ongoing
project (including this documentation). The current state includes the
following features:
* Trackerless torrents (using a kademlia DHT)
* multitracker extension support (as `specified by John Hoffman`__)
* serves multiple torrents on a single port and in a single thread
* gzipped tracker-responses
@ -474,6 +475,9 @@ defines you can use to control the build.
| | a shared library. (This is set by the Jamfile |
| | when ``link=shared`` is set). |
+--------------------------------+-------------------------------------------------+
| ``TORRENT_DISABLE_DHT`` | If this is defined, the support for trackerless |
|Ê | torrents will be disabled. |
+--------------------------------+-------------------------------------------------+
If you experience that libtorrent uses unreasonable amounts of cpu, it will
@ -558,9 +562,15 @@ The ``session`` class has the following synopsis::
std::pair<int, int> const& port_range
, char const* interface = 0);
std::auto_ptr<alert> pop_alert();
void set_severity_level(alert::severity_t s);
void start_dht();
void stop_dht();
void set_dht_settings(dht_settings const& settings);
entry dht_state() const;
void add_dht_node(std::pair<std::string, int> const& node);
};
Once it's created, the session object will spawn the main thread that will do all the work.
@ -836,6 +846,68 @@ pop_alert() set_severity_level()
receive it through ``pop_alert()``. For information, see alerts_.
start_dht() stop_dht() set_dht_settings() dht_state()
-----------------------------------------------------
::
void start_dht(entry const& startup_state);
void stop_dht();
void set_dht_settings(dht_settings const& settings);
entry dht_state() const;
void add_dht_node(std::pair<std::string, int> const& node);
These functions are not available in case ``TORRENT_DISABLE_DHT`` is
defined. ``start_dht`` starts the dht node and makes the trackerless service
available to torrents. The startup state is optional and can contain nodes
and the node id from the previous session. The dht node state is a bencoded
dictionary with the following entries:
``nodes``
is a string with the nodes written as 6 bytes each. 4 bytes ip
address and 2 bytes port number. Both are written in big endian byte order.
``node-id``
The node id written as a readable string as a hexadecimal number.
``dht_state`` will return the current state of the dht node, this can be used
to start up the node again, passing this entry to ``start_dht``. It is a good
idea to save this to disk when the session is closed, and read it up again
when starting.
``stop_dht`` stops the dht node.
``add_dht_node`` adds a node to the routing table. This can be used if your
client has its own source of bootstrapping nodes.
``set_dht_settings`` sets some parameters availavle to the dht node. The
struct has the following members::
struct dht_settings
{
int max_peers_reply;
int search_branching;
int service_port;
int max_fail_count;
};
``max_peers_reply`` is the maximum number of peers the node will send in
response to a ``get_peers`` message from another node.
``search_branching`` is the number of concurrent search request the node will
send when announcing and refreshing the routing table. This parameter is
called alpha in the kademlia paper.
``service_port`` is the udp port the node will listen to. (currently this
cannot be changed while the node is running).
``max_fail_count`` is the maximum number of failed tries to contact a node
before it is removed from the routing table. If there are known working nodes
that are ready to replace a failing node, it will be replaced immediately,
this limit is only used to clear out nodes that don't have any node that can
replace them.
entry
=====
@ -1041,6 +1113,9 @@ The ``torrent_info`` has the following synopsis::
std::vector<announce_entry> const& trackers() const;
bool priv() const;
void set_priv(bool v);
std::vector<std::string> const& url_seeds() const;
size_type total_size() const;
@ -1051,6 +1126,9 @@ The ``torrent_info`` has the following synopsis::
std::string const& comment() const;
std::string const& creator() const;
std::vector<std::pair<std::string, int> > const& nodes() const;
void add_node(std::pair<std::string, int> const& node);
boost::optional<boost::posix_time::ptime>
creation_date() const;
@ -1141,6 +1219,9 @@ complete example, see make_torrent_.
This function is not const because it will also set the info-hash of the ``torrent_info``
object.
Note that a torrent file must include at least one file, and it must have at
least one tracker url or at least one DHT node.
begin_files() end_files() rbegin_files() rend_files()
-----------------------------------------------------
@ -1354,6 +1435,42 @@ it will return an empty string.
__ http://www.boost.org/libs/date_time/doc/class_ptime.html
priv() set_priv()
-----------------
::
bool priv() const;
void set_priv(bool v);
``priv()`` returns true if this torrent is private. i.e., it should not be
distributed on the trackerless network (the kademlia DHT).
``set_priv()`` sets or clears the private flag on this torrent.
nodes()
-------
::
std::vector<std::pair<std::string, int> > const& nodes() const;
If this torrent contains any DHT nodes, they are put in this vector in their original
form (host name and port number).
add_node()
----------
::
void add_node(std::pair<std::string, int> const& node);
This is used when creating torrent. Use this to add a known DHT node. It may
be used, by the client, to bootstrap into the DHT network.
torrent_handle
==============
@ -2146,6 +2263,7 @@ that will be sent to the tracker. The user-agent is a good way to identify your
int whole_pieces_threshold;
int peer_timeout;
int urlseed_timeout;
int urlseed_pipeline_size;
};
``proxy_ip`` may be a hostname or ip to a http proxy to use. If this is
@ -2225,6 +2343,10 @@ is sent.
``urlseed_timeout`` is the same as ``peer_timeout`` but applies only to
url seeds. This value defaults to 20 seconds.
``urlseed_pipeline_size`` controls the pipelining with the web server. When
using persistent connections to HTTP 1.1 servers, the client is allowed to
send more requests before the first response is received. This number controls
the number of outstanding requests to use with url-seeds. Default is 5.
ip_filter
=========
@ -3476,17 +3598,17 @@ Extension name: "chat"
The payload in the packet is a bencoded dictionary with any
combination of the following entries:
+----------+--------------------------------------------------------+
| "msg" | This is a string that contains a message that |
| | should be displayed to the user. |
+----------+--------------------------------------------------------+
| "ctrl" | This is a control string that can tell a client that |
| | it is ignored (to make the user aware of that) and |
| | it can also tell a client that it is no longer ignored.|
| | These notifications are encoded as the strings: |
| | "ignored" and "not ignored". |
| | Any unrecognized strings should be ignored. |
+----------+--------------------------------------------------------+
+----------+--------------------------------------------------------+
| "msg" | This is a string that contains a message that |
| | should be displayed to the user. |
+----------+--------------------------------------------------------+
| "ctrl" | This is a control string that can tell a client that |
| | it is ignored (to make the user aware of that) and |
| | it can also tell a client that it is no longer ignored.|
| | These notifications are encoded as the strings: |
| | "ignored" and "not ignored". |
| | Any unrecognized strings should be ignored. |
+----------+--------------------------------------------------------+
metadata from peers
-------------------
@ -3510,57 +3632,57 @@ are put as payload to the extension message. The three packets are:
request metadata:
+-----------+---------------+----------------------------------------+
| size | name | description |
+===========+===============+========================================+
| uint8_t | msg_type | Determines the kind of message this is |
| | | 0 means 'request metadata' |
+-----------+---------------+----------------------------------------+
| uint8_t | start | The start of the metadata block that |
| | | is requested. It is given in 256:ths |
| | | of the total size of the metadata, |
| | | since the requesting client don't know |
| | | the size of the metadata. |
+-----------+---------------+----------------------------------------+
| uint8_t | size | The size of the metadata block that is |
| | | requested. This is also given in |
| | | 256:ths of the total size of the |
| | | metadata. The size is given as size-1. |
| | | That means that if this field is set |
| | | 0, the request wants one 256:th of the |
| | | metadata. |
+-----------+---------------+----------------------------------------+
+-----------+---------------+----------------------------------------+
| size | name | description |
+===========+===============+========================================+
| uint8_t | msg_type | Determines the kind of message this is |
| | | 0 means 'request metadata' |
+-----------+---------------+----------------------------------------+
| uint8_t | start | The start of the metadata block that |
| | | is requested. It is given in 256:ths |
| | | of the total size of the metadata, |
| | | since the requesting client don't know |
| | | the size of the metadata. |
+-----------+---------------+----------------------------------------+
| uint8_t | size | The size of the metadata block that is |
| | | requested. This is also given in |
| | | 256:ths of the total size of the |
| | | metadata. The size is given as size-1. |
| | | That means that if this field is set |
| | | 0, the request wants one 256:th of the |
| | | metadata. |
+-----------+---------------+----------------------------------------+
metadata:
+-----------+---------------+----------------------------------------+
| size | name | description |
+===========+===============+========================================+
| uint8_t | msg_type | 1 means 'metadata' |
+-----------+---------------+----------------------------------------+
| int32_t | total_size | The total size of the metadata, given |
| | | in number of bytes. |
+-----------+---------------+----------------------------------------+
| int32_t | offset | The offset of where the metadata block |
| | | in this message belongs in the final |
| | | metadata. This is given in bytes. |
+-----------+---------------+----------------------------------------+
| uint8_t[] | metadata | The actual metadata block. The size of |
| | | this part is given implicit by the |
| | | length prefix in the bittorrent |
| | | protocol packet. |
+-----------+---------------+----------------------------------------+
+-----------+---------------+----------------------------------------+
| size | name | description |
+===========+===============+========================================+
| uint8_t | msg_type | 1 means 'metadata' |
+-----------+---------------+----------------------------------------+
| int32_t | total_size | The total size of the metadata, given |
| | | in number of bytes. |
+-----------+---------------+----------------------------------------+
| int32_t | offset | The offset of where the metadata block |
| | | in this message belongs in the final |
| | | metadata. This is given in bytes. |
+-----------+---------------+----------------------------------------+
| uint8_t[] | metadata | The actual metadata block. The size of |
| | | this part is given implicit by the |
| | | length prefix in the bittorrent |
| | | protocol packet. |
+-----------+---------------+----------------------------------------+
Don't have metadata:
+-----------+---------------+----------------------------------------+
| size | name | description |
+===========+===============+========================================+
| uint8_t | msg_type | 2 means 'I don't have metadata'. |
| | | This message is sent as a reply to a |
| | | metadata request if the the client |
| | | doesn't have any metadata. |
+-----------+---------------+----------------------------------------+
+-----------+---------------+----------------------------------------+
| size | name | description |
+===========+===============+========================================+
| uint8_t | msg_type | 2 means 'I don't have metadata'. |
| | | This message is sent as a reply to a |
| | | metadata request if the the client |
| | | doesn't have any metadata. |
+-----------+---------------+----------------------------------------+
HTTP seeding
------------

View File

@ -566,6 +566,23 @@ int main(int ac, char* av[])
handles_t handles;
session ses;
boost::filesystem::ifstream dht_state_file(".dht_state"
, std::ios_base::binary);
dht_state_file.unsetf(std::ios_base::skipws);
try
{
entry dht_state = bdecode(
std::istream_iterator<char>(dht_state_file)
, std::istream_iterator<char>());
ses.start_dht(dht_state);
}
catch (std::exception&)
{
ses.start_dht();
ses.add_dht_node(std::make_pair(std::string("router.bittorrent.com")
, 6881));
}
ses.set_max_half_open_connections(half_open_limit);
ses.set_download_rate_limit(download_limit);
ses.set_upload_rate_limit(upload_limit);
@ -914,11 +931,18 @@ int main(int ac, char* av[])
next_dir_scan = second_clock::universal_time() + seconds(poll_interval);
}
}
entry dht_state = ses.dht_state();
boost::filesystem::ofstream out(".dht_state"
, std::ios_base::binary);
out.unsetf(std::ios_base::skipws);
bencode(std::ostream_iterator<char>(out), dht_state);
}
catch (std::exception& e)
{
std::cout << e.what() << "\n";
}
#ifdef TORRENT_PROFILE
print_checkpoints();
#endif

View File

@ -25,6 +25,7 @@ libtorrent/peer_request.hpp \
libtorrent/piece_block_progress.hpp \
libtorrent/piece_picker.hpp \
libtorrent/policy.hpp \
libtorrent/random_sample.hpp \
libtorrent/resource_request.hpp \
libtorrent/session.hpp \
libtorrent/session_settings.hpp \
@ -40,6 +41,19 @@ libtorrent/udp_tracker_connection.hpp \
libtorrent/utf8.hpp \
libtorrent/version.hpp \
\
libtorrent/kademlia/closest_nodes.hpp \
libtorrent/kademlia/dht_tracker.hpp \
libtorrent/kademlia/find_data.hpp \
libtorrent/kademlia/logging.hpp \
libtorrent/kademlia/node.hpp \
libtorrent/kademlia/node_entry.hpp \
libtorrent/kademlia/node_id.hpp \
libtorrent/kademlia/packet_iterator.hpp \
libtorrent/kademlia/refresh.hpp \
libtorrent/kademlia/routing_table.hpp \
libtorrent/kademlia/rpc_manager.hpp \
libtorrent/kademlia/traversal_algorithm.hpp \
\
libtorrent/asio/basic_datagram_socket.hpp \
libtorrent/asio/basic_deadline_timer.hpp \
libtorrent/asio/basic_io_object.hpp \
@ -68,11 +82,13 @@ libtorrent/asio/detail/buffered_stream_storage.hpp \
libtorrent/asio/detail/call_stack.hpp \
libtorrent/asio/detail/const_buffers_iterator.hpp \
libtorrent/asio/detail/consuming_buffers.hpp \
libtorrent/asio/detail/deadline_timer_service.hpp \
libtorrent/asio/detail/epoll_reactor.hpp \
libtorrent/asio/detail/epoll_reactor_fwd.hpp \
libtorrent/asio/detail/event.hpp \
libtorrent/asio/detail/fd_set_adapter.hpp \
libtorrent/asio/detail/handler_alloc_helpers.hpp \
libtorrent/asio/detail/handler_dispatch_helpers.hpp \
libtorrent/asio/detail/hash_map.hpp \
libtorrent/asio/detail/io_control.hpp \
libtorrent/asio/detail/kqueue_reactor.hpp \
@ -112,6 +128,8 @@ libtorrent/asio/detail/strand_service.hpp \
libtorrent/asio/detail/task_io_service.hpp \
libtorrent/asio/detail/task_io_service_fwd.hpp \
libtorrent/asio/detail/thread.hpp \
libtorrent/asio/detail/timer_queue.hpp \
libtorrent/asio/detail/timer_queue_base.hpp \
libtorrent/asio/detail/tss_ptr.hpp \
libtorrent/asio/detail/win_event.hpp \
libtorrent/asio/detail/win_iocp_io_service.hpp \
@ -128,6 +146,7 @@ libtorrent/asio/detail/wrapped_handler.hpp \
libtorrent/asio/error.hpp \
libtorrent/asio/error_handler.hpp \
libtorrent/asio/handler_alloc_hook.hpp \
libtorrent/asio/handler_dispatch_hook.hpp \
libtorrent/asio/impl/io_service.ipp \
libtorrent/asio/impl/read.ipp \
libtorrent/asio/impl/read_until.ipp \
@ -174,6 +193,6 @@ libtorrent/asio/streambuf.hpp \
libtorrent/asio/system_exception.hpp \
libtorrent/asio/thread.hpp \
libtorrent/asio/time_traits.hpp \
libtorrent/asio/write.hpp \
libtorrent/asio.hpp
libtorrent/asio/write.hpp

View File

@ -0,0 +1,86 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CLOSEST_NODES_050323_HPP
#define CLOSEST_NODES_050323_HPP
#include <vector>
#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <boost/function.hpp>
namespace libtorrent { namespace dht
{
class rpc_manager;
// -------- closest nodes -----------
class closest_nodes : public traversal_algorithm
{
public:
typedef boost::function<
void(std::vector<node_entry> const&)
> done_callback;
static void initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
);
private:
void done();
void invoke(node_id const& id, asio::ip::udp::endpoint addr);
closest_nodes(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
);
done_callback m_done_callback;
};
} } // namespace libtorrent::dht
#endif // CLOSEST_NODES_050323_HPP

View File

@ -0,0 +1,129 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_DISABLE_DHT
#ifndef TORRENT_DHT_TRACKER
#define TORRENT_DHT_TRACKER
#include <fstream>
#include <set>
#include <numeric>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/ref.hpp>
#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/operations.hpp>
#include "libtorrent/kademlia/node.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/traversal_algorithm.hpp"
#include "libtorrent/kademlia/packet_iterator.hpp"
#include "libtorrent/session_settings.hpp"
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(dht_tracker);
#endif
struct dht_tracker
{
dht_tracker(asio::io_service& d, dht_settings const& settings
, entry const& bootstrap);
void add_node(udp::endpoint node);
void add_node(std::pair<std::string, int> const& node);
entry state() const;
void announce(sha1_hash const& ih, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&
, sha1_hash const&)> f);
private:
void on_name_lookup(asio::error const& e
, udp::resolver::iterator host);
void second_tick(asio::error const& e);
void tick(asio::error const& e);
// translate bittorrent kademlia message into the generice kademlia message
// used by the library
void on_receive(asio::error const& error, size_t bytes_transferred);
void on_bootstrap();
void send_packet(msg const& m);
asio::io_service& m_demuxer;
asio::ip::udp::socket m_socket;
node_impl m_dht;
// this is the index of the receive buffer we are currently receiving to
// the other buffer is the one containing the last message
int m_buffer;
std::vector<char> m_in_buf[2];
udp::endpoint m_remote_endpoint[2];
std::vector<char> m_send_buf;
boost::posix_time::ptime m_last_refresh;
deadline_timer m_timer;
deadline_timer m_second_timer;
dht_settings const& m_settings;
int m_refresh_bucket;
// used to resolve hostnames for nodes
udp::resolver m_host_resolver;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
int m_replies_sent[5];
int m_queries_received[5];
int m_replies_bytes_sent[5];
int m_queries_bytes_received[5];
int m_counter;
int m_announces;
int m_failed_announces;
int m_total_message_input;
int m_ut_message_input;
int m_lt_message_input;
int m_mp_message_input;
int m_gr_message_input;
#endif
};
}}
#endif
#endif

View File

@ -0,0 +1,95 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FIND_DATA_050323_HPP
#define FIND_DATA_050323_HPP
#include <vector>
#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/packet_iterator.hpp>
#include <boost/optional.hpp>
#include <boost/function.hpp>
namespace libtorrent { namespace dht
{
using asio::ip::udp;
typedef std::vector<char> packet_t;
class rpc_manager;
// -------- find data -----------
class find_data : public traversal_algorithm
{
public:
typedef boost::function<void(msg const*)> done_callback;
static void initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
);
void got_data(msg const* m);
private:
void done();
void invoke(node_id const& id, udp::endpoint addr);
find_data(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
);
done_callback m_done_callback;
boost::shared_ptr<packet_t> m_packet;
bool m_done;
};
} } // namespace libtorrent::dht
#endif // FIND_DATA_050323_HPP

View File

@ -0,0 +1,146 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_LOGGING_HPP
#define TORRENT_LOGGING_HPP
#include <iostream>
#include <fstream>
namespace libtorrent { namespace dht
{
class log
{
public:
log(char const* id, std::ostream& stream)
: m_id(id)
, m_enabled(true)
, m_stream(stream)
{
}
char const* id() const
{
return m_id;
}
bool enabled() const
{
return m_enabled;
}
void enable(bool e)
{
m_enabled = e;
}
void flush() { m_stream.flush(); }
template<class T>
log& operator<<(T const& x)
{
m_stream << x;
return *this;
}
private:
char const* m_id;
bool m_enabled;
std::ostream& m_stream;
};
class log_event
{
public:
log_event(log& log)
: log_(log)
{
if (log_.enabled())
log_ << '[' << log.id() << "] ";
}
~log_event()
{
if (log_.enabled())
{
log_ << "\n";
log_.flush();
}
}
template<class T>
log_event& operator<<(T const& x)
{
log_ << x;
return *this;
}
operator bool() const
{
return log_.enabled();
}
private:
log& log_;
};
class inverted_log_event : public log_event
{
public:
inverted_log_event(log& log) : log_event(log) {}
operator bool() const
{
return !log_event::operator bool();
}
};
} } // namespace libtorrent::dht
#define TORRENT_DECLARE_LOG(name) \
libtorrent::dht::log& name ## _log()
#define TORRENT_DEFINE_LOG(name) \
libtorrent::dht::log& name ## _log() \
{ \
static std::ofstream log_file("libtorrent_logs/dht.log", std::ios::app); \
static libtorrent::dht::log instance(#name, log_file); \
return instance; \
}
#define TORRENT_LOG(name) \
if (libtorrent::dht::inverted_log_event event_object__ = name ## _log()); \
else static_cast<log_event&>(event_object__)
#endif

View File

@ -0,0 +1,185 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef NODE_HPP
#define NODE_HPP
#include <algorithm>
#include <cassert>
#include <map>
#include <set>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/io.hpp>
#include <libtorrent/session_settings.hpp>
#include <boost/cstdint.hpp>
#include <boost/optional.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/ref.hpp>
namespace libtorrent { namespace dht
{
using asio::ip::udp;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(node);
#endif
// this is the entry for every peer
// the timestamp is there to make it possible
// to remove stale peers
struct peer_entry
{
tcp::endpoint addr;
boost::posix_time::ptime added;
};
// this is a group. It contains a set of group members
struct torrent_entry
{
std::set<peer_entry> peers;
};
inline bool operator<(peer_entry const& lhs, peer_entry const& rhs)
{
return lhs.addr.address() == rhs.addr.address()
? lhs.addr.port() < rhs.addr.port()
: lhs.addr.address() < rhs.addr.address();
}
struct null_type {};
class node_impl : boost::noncopyable
{
typedef std::map<node_id, torrent_entry> table_t;
public:
node_impl(boost::function<void(msg const&)> const& f
, dht_settings const& settings, boost::optional<node_id> node_id);
virtual ~node_impl() {}
void refresh(node_id const& id, boost::function0<void> f);
void bootstrap(std::vector<udp::endpoint> const& nodes
, boost::function0<void> f);
void find_node(node_id const& id, boost::function<
void(std::vector<node_entry> const&)> f);
void incoming(msg const& m);
void refresh();
void refresh_bucket(int bucket);
int bucket_size(int bucket);
typedef routing_table::iterator iterator;
iterator begin() const { return m_table.begin(); }
iterator end() const { return m_table.end(); }
typedef table_t::iterator data_iterator;
node_id const& nid() const { return m_id; }
boost::tuple<int, int> size() const{ return m_table.size(); }
data_iterator begin_data() { return m_map.begin(); }
data_iterator end_data() { return m_map.end(); }
void print_state(std::ostream& os) const
{ m_table.print_state(os); }
void announce(sha1_hash const& info_hash, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&
, sha1_hash const&)> f);
bool verify_token(msg const& m);
entry generate_token(msg const& m);
// the returned time is the delay until tick should be called
// again the next time
boost::posix_time::time_duration tick();
// checks the buckets for any that needs refreshing
void check_refresh();
// generates a new secret number used to generate write tokens
void new_write_key();
// pings the given node, and adds it to
// the routing table if it respons and if the
// bucket is not full.
void add_node(udp::endpoint node);
void replacement_cache(bucket_t& nodes) const
{ m_table.replacement_cache(nodes); }
protected:
// is called when a find data request is received. Should
// return false if the data is not stored on this node. If
// the data is stored, it should be serialized into 'data'.
bool on_find(msg const& m, std::vector<tcp::endpoint>& peers) const;
// this is called when a store request is received. The data
// is store-parameters and the data to be stored.
void on_announce(msg const& m, msg& reply);
dht_settings const& m_settings;
// the maximum number of peers to send in a get_peers
// reply. Ordinary trackers usually limit this to 50.
// 50 => 6 * 50 = 250 bytes + packet overhead
int m_max_peers_reply;
private:
void incoming_request(msg const& h);
node_id m_id;
routing_table m_table;
rpc_manager m_rpc;
table_t m_map;
boost::posix_time::ptime m_last_tracker_tick;
// secret random numbers used to create write tokens
int m_secret[2];
};
} } // namespace libtorrent::dht
#endif // NODE_HPP

View File

@ -0,0 +1,63 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef KADEMLIA_NODE_ENTRY_HPP
#define KADEMLIA_NODE_ENTRY_HPP
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/socket.hpp"
namespace libtorrent { namespace dht
{
struct node_entry
{
node_entry(node_id const& id_, asio::ip::udp::endpoint addr_)
: id(id_)
, addr(addr_)
, fail_count(0) {}
node_entry(asio::ip::udp::endpoint addr_)
: id(0)
, addr(addr_)
, fail_count(0) {}
node_id id;
udp::endpoint addr;
// the number of times this node has failed to
// respond in a row
int fail_count;
};
} } // namespace libtorrent::dht
#endif

View File

@ -0,0 +1,60 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef NODE_ID_HPP
#define NODE_ID_HPP
#include <algorithm>
#include <cassert>
#include <boost/cstdint.hpp>
#include "libtorrent/peer_id.hpp"
namespace libtorrent { namespace dht
{
typedef libtorrent::big_number node_id;
// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2);
// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref);
// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// usefult for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2);
} } // namespace libtorrent::dht
#endif // NODE_ID_HPP

View File

@ -0,0 +1,95 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PACKET_ITERATOR_HPP
#define PACKET_ITERATOR_HPP
#include <boost/iterator/iterator_facade.hpp>
#include <vector>
#include <stdexcept>
namespace libtorrent { namespace dht
{
class packet_iterator: public boost::iterator_facade<
packet_iterator, const char, boost::forward_traversal_tag>
{
public:
typedef std::vector<char>::const_iterator base_iterator;
packet_iterator() {}
packet_iterator(std::vector<char>::const_iterator start
, std::vector<char>::const_iterator end
, std::string const& error_msg = "")
: m_base(start)
, m_end(end)
, m_msg(error_msg)
{}
base_iterator base() const
{ return m_base; }
base_iterator end() const
{ return m_end; }
int left() const { return int(m_end - m_base); }
private:
friend class boost::iterator_core_access;
bool equal(packet_iterator const& other) const
{ return m_base == other.m_base; }
void advance(int n)
{
m_base += n;
}
void increment()
{ ++m_base; }
char const& dereference() const
{
if (m_base == m_end) throw std::runtime_error(m_msg);
return *m_base;
}
base_iterator m_base;
base_iterator m_end;
std::string m_msg;
};
} } // namespace libtorrent::dht
#endif // PACKET_ITERATOR_HPP

View File

@ -0,0 +1,158 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef REFRESH_050324_HPP
#define REFRESH_050324_HPP
#include <vector>
#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <boost/function.hpp>
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(refresh);
#endif
class routing_table;
class rpc_manager;
class refresh : public traversal_algorithm
{
public:
typedef boost::function<void()> done_callback;
template<class InIt>
static void initiate(
node_id target
, int branch_factor
, int max_active_pings
, int max_results
, routing_table& table
, InIt first
, InIt last
, rpc_manager& rpc
, done_callback const& callback
);
void ping_reply(node_id id);
void ping_timeout(node_id id);
private:
template<class InIt>
refresh(
node_id target
, int branch_factor
, int max_active_pings
, int max_results
, routing_table& table
, InIt first
, InIt last
, rpc_manager& rpc
, done_callback const& callback
);
void done();
void invoke(node_id const& id, udp::endpoint addr);
void invoke_pings_or_finish();
int m_max_active_pings;
int m_active_pings;
done_callback m_done_callback;
std::vector<result>::iterator m_leftover_nodes_iterator;
};
template<class InIt>
inline refresh::refresh(
node_id target
, int branch_factor
, int max_active_pings
, int max_results
, routing_table& table
, InIt first
, InIt last
, rpc_manager& rpc
, done_callback const& callback
)
: traversal_algorithm(
target
, branch_factor
, max_results
, table
, rpc
, first
, last
)
, m_max_active_pings(max_active_pings)
, m_active_pings(0)
, m_done_callback(callback)
{
boost::intrusive_ptr<refresh> self(this);
add_requests();
}
template<class InIt>
inline void refresh::initiate(
node_id target
, int branch_factor
, int max_active_pings
, int max_results
, routing_table& table
, InIt first
, InIt last
, rpc_manager& rpc
, done_callback const& callback
)
{
new refresh(
target
, branch_factor
, max_active_pings
, max_results
, table
, first
, last
, rpc
, callback
);
}
} } // namespace libtorrent::dht
#endif // REFRESH_050324_HPP

View File

@ -0,0 +1,224 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ROUTING_TABLE_HPP
#define ROUTING_TABLE_HPP
#include <vector>
#include <deque>
#include <boost/cstdint.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/utility.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/array.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/node_entry.hpp>
#include <libtorrent/session_settings.hpp>
namespace pt = boost::posix_time;
namespace libtorrent { namespace dht
{
using asio::ip::udp;
//TORRENT_DECLARE_LOG(table);
typedef std::deque<node_entry> bucket_t;
// differences in the implementation from the description in
// the paper:
//
// * The routing table tree is not allocated dynamically, there
// are always 160 buckets.
// * Nodes are not marked as being stale, they keep a counter
// that tells how many times in a row they have failed. When
// a new node is to be inserted, the node that has failed
// the most times is replaced. If none of the nodes in the
// bucket has failed, then it is put in the replacement
// cache (just like in the paper).
class routing_table;
namespace aux
{
// Iterates over a flattened routing_table structure.
class routing_table_iterator
: public boost::iterator_facade<
routing_table_iterator
, node_entry const
, boost::forward_traversal_tag
>
{
public:
routing_table_iterator()
{
}
private:
friend class libtorrent::dht::routing_table;
friend class boost::iterator_core_access;
typedef boost::array<std::pair<bucket_t, bucket_t>, 160>::const_iterator
bucket_iterator_t;
routing_table_iterator(
bucket_iterator_t begin
, bucket_iterator_t end)
: m_bucket_iterator(begin)
, m_bucket_end(end)
, m_iterator(begin != end ? begin->first.begin() : bucket_t::iterator())
{
if (m_bucket_iterator == m_bucket_end) return;
while (m_iterator == m_bucket_iterator->first.end())
{
if (++m_bucket_iterator == m_bucket_end)
break;
m_iterator = m_bucket_iterator->first.begin();
}
}
bool equal(routing_table_iterator const& other) const
{
return m_bucket_iterator == other.m_bucket_iterator
&& (m_iterator == other.m_iterator
|| m_bucket_iterator == m_bucket_end);
}
void increment()
{
assert(m_bucket_iterator != m_bucket_end);
++m_iterator;
while (m_iterator == m_bucket_iterator->first.end())
{
if (++m_bucket_iterator == m_bucket_end)
break;
m_iterator = m_bucket_iterator->first.begin();
}
}
node_entry const& dereference() const
{
assert(m_bucket_iterator != m_bucket_end);
return *m_iterator;
}
bucket_iterator_t m_bucket_iterator;
bucket_iterator_t m_bucket_end;
bucket_t::const_iterator m_iterator;
};
} // namespace aux
class routing_table
{
public:
typedef aux::routing_table_iterator iterator;
typedef iterator const_iterator;
routing_table(node_id const& id, int bucket_size
, dht_settings const& settings);
void node_failed(node_id const& id);
// this function is called every time the node sees
// a sign of a node being alive. This node will either
// be inserted in the k-buckets or be moved to the top
// of its bucket.
bool node_seen(node_id const& id, udp::endpoint addr);
// returns true if the given bucket is empty but there are nodes
// in a bucket closer to us, or if the bucket is non-empty and
// the time from the last activity is more than 15 minutes
bool should_refresh(int bucket);
// fills the vector with the count nodes from our buckets that
// are nearest to the given id.
void find_node(node_id const& id, std::vector<node_entry>& l
, bool include_self, int count = 0);
// returns true if the given node would be placed in a bucket
// that is not full. If the node already exists in the table
// this function returns false
bool need_node(node_id const& id);
int bucket_size(int bucket)
{
assert(bucket >= 0 && bucket < 160);
return (int)m_buckets[bucket].first.size();
}
int bucket_size() const { return m_bucket_size; }
iterator begin() const;
iterator end() const;
boost::tuple<int, int> size() const;
// returns true if there are no working nodes
// in the routing table
bool need_bootstrap() const;
void replacement_cache(bucket_t& nodes) const;
// used for debug and monitoring purposes. This will print out
// the state of the routing table to the given stream
void print_state(std::ostream& os) const;
private:
// constant called k in paper
int m_bucket_size;
dht_settings const& m_settings;
// 160 (k-bucket, replacement cache) pairs
typedef boost::array<std::pair<bucket_t, bucket_t>, 160> table_t;
table_t m_buckets;
// timestamps of the last activity in each bucket
boost::array<boost::posix_time::ptime, 160> m_bucket_activity;
node_id m_id; // our own node id
// this is the lowest bucket index with nodes in it
int m_lowest_active_bucket;
};
} } // namespace libtorrent::dht
#endif // ROUTING_TABLE_HPP

View File

@ -0,0 +1,196 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RPC_MANAGER_HPP
#define RPC_MANAGER_HPP
#include <vector>
#include <map>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boost/cstdint.hpp>
#include <boost/array.hpp>
#include <libtorrent/socket.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/kademlia/packet_iterator.hpp>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/kademlia/node_entry.hpp>
namespace libtorrent { namespace dht
{
using asio::ip::udp;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(rpc);
#endif
typedef std::vector<char> packet_t;
namespace messages
{
enum { ping = 0, find_node = 1, get_peers = 2, announce_peer = 3, error = 4 };
char const* const ids[] = { "ping", "find_node", "get_peers", "announce_peer", "error" };
} // namespace messages
struct msg
{
msg() : reply(false), piggy_backed_ping(false)
, port(0) {}
// true if this message is a reply
bool reply;
// true if this is a reply with a piggy backed ping
bool piggy_backed_ping;
// the kind if message
int message_id;
// if this is a reply, a copy of the transaction id
// from the request. If it's a request, a transaction
// id that should be sent back in the reply
std::string transaction_id;
// if this packet has a piggy backed ping, this
// is the transaction id of that ping
std::string ping_transaction_id;
// the node id of the process sending the message
node_id id;
// the address of the process sending or receiving
// the message.
udp::endpoint addr;
// if this is a nodes response, these are the nodes
typedef std::vector<node_entry> nodes_t;
nodes_t nodes;
typedef std::vector<tcp::endpoint> peers_t;
peers_t peers;
// similar to transaction_id but for write operations.
entry write_token;
// the info has for peer_requests, announce_peer
// and responses
node_id info_hash;
// port for announce_peer messages
int port;
// ERROR MESSAGES
int error_code;
std::string error_msg;
};
struct observer : boost::noncopyable
{
observer()
: sent(boost::posix_time::microsec_clock::universal_time())
{}
virtual ~observer() {}
// this two callbacks lets the observer add
// information to the message before it's sent
virtual void send(msg& m) = 0;
// this is called when a reply is received
virtual void reply(msg const& m) = 0;
// this is called when no reply has been received within
// some timeout
virtual void timeout() = 0;
udp::endpoint target_addr;
boost::posix_time::ptime sent;
};
class routing_table;
class rpc_manager
{
public:
typedef boost::function1<void, msg const&> fun;
typedef boost::function1<void, msg const&> send_fun;
rpc_manager(fun const& incoming_fun, node_id const& our_id
, routing_table& table, send_fun const& sf);
~rpc_manager();
// returns true if the node needs a refresh
bool incoming(msg const&);
boost::posix_time::time_duration tick();
void invoke(int message_id, udp::endpoint target
, boost::shared_ptr<observer> o);
void reply(msg& m, msg const& reply_to);
void reply_with_ping(msg& m, msg const& reply_to);
#ifndef NDEBUG
void check_invariant() const;
#endif
private:
enum { max_transactions = 1024 };
unsigned int new_transaction_id();
void update_oldest_transaction_id();
boost::uint32_t calc_connection_id(udp::endpoint addr);
typedef boost::array<boost::shared_ptr<observer>, max_transactions>
transactions_t;
transactions_t m_transactions;
// this is the next transaction id to be used
int m_next_transaction_id;
// this is the oldest transaction id still
// (possibly) in use. This is the transaction
// that will time out first, the one we are
// waiting for to time out
int m_oldest_transaction_id;
fun m_incoming;
send_fun m_send;
node_id m_our_id;
routing_table& m_table;
boost::posix_time::ptime m_timer;
node_id m_random_number;
// a cache of connection ids
std::map<udp::endpoint, boost::uint32_t> m_connection_id;
};
} } // namespace libtorrent::dht
#endif

View File

@ -0,0 +1,149 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TRAVERSAL_ALGORITHM_050324_HPP
#define TRAVERSAL_ALGORITHM_050324_HPP
#include <vector>
#include <libtorrent/kademlia/node_id.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <boost/noncopyable.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/bind.hpp>
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DECLARE_LOG(traversal);
#endif
class rpc_manager;
// this class may not be instantiated as a stack object
class traversal_algorithm : boost::noncopyable
{
public:
void traverse(node_id const& id, udp::endpoint addr);
void finished(node_id const& id);
void failed(node_id const& id);
virtual ~traversal_algorithm() {}
protected:
template<class InIt>
traversal_algorithm(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, InIt start
, InIt end
);
void add_request(node_id const& id, udp::endpoint addr);
void add_requests();
void add_entry(node_id const& id, udp::endpoint addr, unsigned char flags);
virtual void done() = 0;
virtual void invoke(node_id const& id, udp::endpoint addr) = 0;
struct result
{
result(node_id const& id, udp::endpoint addr, unsigned char f = 0)
: id(id), addr(addr), flags(f)
{}
node_id id;
udp::endpoint addr;
enum { queried = 1, initial = 2 };
unsigned char flags;
};
std::vector<result>::iterator last_iterator();
friend void intrusive_ptr_add_ref(traversal_algorithm* p)
{
p->m_ref_count++;
}
friend void intrusive_ptr_release(traversal_algorithm* p)
{
if (--p->m_ref_count == 0)
delete p;
}
int m_ref_count;
node_id m_target;
int m_branch_factor;
int m_max_results;
std::vector<result> m_results;
std::set<udp::endpoint> m_failed;
routing_table& m_table;
rpc_manager& m_rpc;
int m_invoke_count;
};
template<class InIt>
traversal_algorithm::traversal_algorithm(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, InIt start // <- nodes to initiate traversal with
, InIt end
)
: m_ref_count(0)
, m_target(target)
, m_branch_factor(branch_factor)
, m_max_results(max_results)
, m_table(table)
, m_rpc(rpc)
, m_invoke_count(0)
{
using boost::bind;
for (InIt i = start; i != end; ++i)
{
add_entry(i->id, i->addr, result::initial);
}
}
} } // namespace libtorrent::dht
#endif // TRAVERSAL_ALGORITHM_050324_HPP

View File

@ -367,6 +367,7 @@ namespace libtorrent
// in the session_settings structure. But it may be lowered
// if the peer is known to require a smaller limit (like BitComet).
// or if the extended handshake sets a limit.
// web seeds also has a limit on the queue size.
int m_max_out_request_queue;
void set_timeout(int s) { m_timeout = s; }

View File

@ -0,0 +1,72 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_RANDOM_SAMPLE_HPP
#define TORRENT_RANDOM_SAMPLE_HPP
#include <iterator>
#include <cstdlib>
namespace libtorrent
{
template<class InIter, class OutIter, class Distance>
inline void random_sample_n(InIter start, InIter end
, OutIter out, Distance n)
{
Distance t = 0;
Distance m = 0;
Distance N = std::distance(start, end);
assert(N >= n);
while (m < n)
{
if ((rand() / (RAND_MAX + 1.f)) * (N - t) >= n - m)
{
++start;
++t;
}
else
{
*out = *start;
++out;
++start;
++t;
++m;
}
}
}
}
#endif

View File

@ -72,6 +72,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/config.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/version.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp"
#if !defined(NDEBUG) && defined(_MSC_VER)
# include <float.h>
@ -309,6 +310,10 @@ namespace libtorrent
void second_tick(asio::error const& e);
boost::posix_time::ptime m_last_tick;
#ifndef TORRENT_DISABLE_DHT
boost::scoped_ptr<dht::dht_tracker> m_dht;
dht_settings m_dht_settings;
#endif
// the timer used to fire the second_tick
deadline_timer m_timer;
#ifndef NDEBUG
@ -385,6 +390,14 @@ namespace libtorrent
session_status status() const;
#ifndef TORRENT_DISABLE_DHT
void start_dht(entry const& startup_state = entry());
void stop_dht();
void set_dht_settings(dht_settings const& settings);
entry dht_state() const;
void add_dht_node(std::pair<std::string, int> const& node);
#endif
void enable_extension(extension_index i);
void disable_extensions();

View File

@ -56,6 +56,7 @@ namespace libtorrent
, whole_pieces_threshold(20)
, peer_timeout(120)
, urlseed_timeout(20)
, urlseed_pipeline_size(5)
{}
std::string proxy_ip;
@ -136,7 +137,38 @@ namespace libtorrent
// this is usually set lower, because web servers are
// expected to be more reliable.
int urlseed_timeout;
// controls the pipelining size of url-seeds
int urlseed_pipeline_size;
};
#ifndef TORRENT_DISABLE_DHT
struct dht_settings
{
dht_settings()
: max_peers_reply(50)
, search_branching(5)
, service_port(6881)
, max_fail_count(20)
{}
// the maximum number of peers to send in a
// reply to get_peers
int max_peers_reply;
// the number of simultanous "connections" when
// searching the DHT.
int search_branching;
// the listen port for the dht. This is a UDP port.
int service_port;
// the maximum number of times a node can fail
// in a row before it is removed from the table.
int max_fail_count;
};
#endif
}
#endif

View File

@ -55,6 +55,8 @@ POSSIBILITY OF SUCH DAMAGE.
#undef Protocol
#endif
#include "libtorrent/io.hpp"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
@ -87,6 +89,24 @@ namespace libtorrent
using asio::async_write;
using asio::deadline_timer;
namespace detail
{
template<class Endpoint, class OutIt>
void write_endpoint(Endpoint const& e, OutIt& out)
{
write_uint32(e.address().to_v4().to_ulong(), out);
write_uint16(e.port(), out);
}
template<class Endpoint, class InIt>
Endpoint read_endpoint(InIt& in)
{
unsigned int ip = read_uint32(in);
int port = read_uint16(in);
return Endpoint(address(ip), port);
}
}
}
#endif // TORRENT_SOCKET_HPP_INCLUDED

View File

@ -322,7 +322,7 @@ namespace libtorrent
// this is the asio callback that is called when a name
// lookup for a web seed is completed.
void on_name_lookup(asio::error const& e, tcp::resolver::iterator i
, int port, std::string url);
, std::string url);
// this is called when the torrent has finished. i.e.
// all the pieces we have not filtered have been downloaded.
@ -493,6 +493,12 @@ namespace libtorrent
// used to resolve the names of web seeds
tcp::resolver m_host_resolver;
#ifndef TORRENT_DISABLE_DHT
deadline_timer m_dht_announce_timer;
void on_dht_announce(asio::error const& e);
void on_dht_announce_response(std::vector<tcp::endpoint> const& peers);
#endif
// this is the upload and download statistics for the whole torrent.
// it's updated from all its peers once every second.

View File

@ -134,6 +134,9 @@ namespace libtorrent
void print(std::ostream& os) const;
bool is_valid() const { return m_piece_length > 0; }
bool priv() const { return m_private; }
void set_priv(bool v) { m_private = v; }
void convert_file_names();
size_type piece_size(int index) const;
@ -154,6 +157,14 @@ namespace libtorrent
const std::string& comment() const
{ return m_comment; }
// dht nodes to add to the routing table/bootstrap from
typedef std::vector<std::pair<std::string, int> > nodes_t;
nodes_t const& nodes() const
{ return m_nodes; }
void add_node(std::pair<std::string, int> const& node);
void parse_info_section(entry const& e);
private:
@ -176,6 +187,8 @@ namespace libtorrent
// the list of files that this torrent consists of
std::vector<file_entry> m_files;
nodes_t m_nodes;
// the sum of all filesizes
size_type m_total_size;
@ -205,6 +218,10 @@ namespace libtorrent
// or not. e.g. test/test there's one file and one directory
// and they have the same name.
bool m_multifile;
// this is true if the torrent is private. i.e., is should not
// be announced on the dht
bool m_private;
// contains any non-parsed entries from the info-section
// these are kept in order to be able to accurately

View File

@ -230,11 +230,12 @@ namespace libtorrent
void abort_all_requests();
void remove_request(tracker_connection const*);
bool empty() const;
private:
typedef boost::recursive_mutex mutex_t;
mutex_t m_mutex;
mutable mutex_t m_mutex;
typedef std::list<boost::intrusive_ptr<tracker_connection> >
tracker_connections_t;

View File

@ -36,7 +36,8 @@ if test "$ax_cv_boost_date_time" = yes; then
ax_boost_date_time_lib=boost_date_time-$with_boost_date_time
fi])
for ax_lib in $ax_date_time_lib $ax_boost_date_time_lib boost_date_time; do
AC_CHECK_LIB($ax_lib, main, [BOOST_DATE_TIME_LIB=$ax_lib break])
AC_CHECK_LIB($ax_lib, main, [BOOST_DATE_TIME_LIB=$ax_lib
break])
done
AC_SUBST(BOOST_DATE_TIME_LIB)
fi

View File

@ -38,7 +38,8 @@ if test "$ax_cv_boost_filesystem" = yes; then
ax_boost_filesystem_lib=boost_filesystem-$with_boost_filesystem
fi])
for ax_lib in $ax_filesystem_lib $ax_boost_filesystem_lib boost_filesystem; do
AC_CHECK_LIB($ax_lib, main, [BOOST_FILESYSTEM_LIB=$ax_lib break])
AC_CHECK_LIB($ax_lib, main, [BOOST_FILESYSTEM_LIB=$ax_lib
break])
done
AC_SUBST(BOOST_FILESYSTEM_LIB)
fi

View File

@ -34,7 +34,8 @@ if test "$ax_cv_boost_program_options" = yes; then
ax_boost_program_options_lib=boost_program_options-$with_boost_program_options
fi])
for ax_lib in $ax_program_options_lib $ax_boost_program_options_lib boost_program_options; do
AC_CHECK_LIB($ax_lib, main, [BOOST_PROGRAM_OPTIONS_LIB=$ax_lib break])
AC_CHECK_LIB($ax_lib, main, [BOOST_PROGRAM_OPTIONS_LIB=$ax_lib
break])
done
AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB)
fi

View File

@ -34,7 +34,8 @@ if test "$ax_cv_boost_regex" = yes; then
ax_boost_regex_lib=boost_regex-$with_boost_regex
fi])
for ax_lib in $ax_regex_lib $ax_boost_regex_lib boost_regex; do
AC_CHECK_LIB($ax_lib, main, [BOOST_REGEX_LIB=$ax_lib break])
AC_CHECK_LIB($ax_lib, main, [BOOST_REGEX_LIB=$ax_lib
break])
done
AC_SUBST(BOOST_REGEX_LIB)
fi

View File

@ -41,7 +41,8 @@ if test "$ax_cv_boost_thread" = yes; then
ax_boost_thread_lib=boost_thread-$with_boost_thread
fi])
for ax_lib in $ax_thread_lib $ax_boost_thread_lib boost_thread; do
AC_CHECK_LIB($ax_lib, main, [BOOST_THREAD_LIB=$ax_lib break])
AC_CHECK_LIB($ax_lib, main, [BOOST_THREAD_LIB=$ax_lib
break])
done
AC_SUBST(BOOST_THREAD_LIB)
fi

View File

@ -7,7 +7,17 @@ piece_picker.cpp policy.cpp session.cpp sha1.cpp stat.cpp \
storage.cpp torrent.cpp torrent_handle.cpp \
torrent_info.cpp tracker_manager.cpp \
http_tracker_connection.cpp udp_tracker_connection.cpp \
alert.cpp identify_client.cpp ip_filter.cpp file.cpp
alert.cpp identify_client.cpp ip_filter.cpp file.cpp \
\
kademlia/closest_nodes.cpp \
kademlia/dht_tracker.cpp \
kademlia/find_data.cpp \
kademlia/node.cpp \
kademlia/node_id.cpp \
kademlia/refresh.cpp \
kademlia/routing_table.cpp \
kademlia/rpc_manager.cpp \
kademlia/traversal_algorithm.cpp
noinst_HEADERS = \
$(top_srcdir)/include/libtorrent/alert.hpp \

View File

@ -272,8 +272,10 @@ namespace libtorrent
, i.begin + 8
, 0);
#ifndef TORRENT_DISABLE_DHT
// indicate that we support the DHT messages
*(i.begin + 7) = 0x01;
#endif
// we support extensions
*(i.begin + 5) = 0x10;
@ -1352,8 +1354,10 @@ namespace libtorrent
// reply with our handshake
write_handshake();
write_bitfield(t->pieces());
// if (m_supports_dht_port)
// write_dht_port(m_ses.dht_port());
#ifndef TORRENT_DISABLE_DHT
if (m_supports_dht_port && m_ses.m_dht)
write_dht_port(m_ses.m_dht_settings.service_port);
#endif
}
else
{

View File

@ -329,7 +329,9 @@ namespace libtorrent
{
for (int j = 0; j < indent+1; ++j) os << " ";
os << "[" << i->first << "]";
if (i->second.type() != entry::string_t && i->second.type() != entry::int_t) os << "\n";
if (i->second.type() != entry::string_t
&& i->second.type() != entry::int_t)
os << "\n";
else os << " ";
i->second.print(os, indent+2);
}

View File

@ -0,0 +1,146 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/closest_nodes.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
namespace libtorrent { namespace dht
{
using asio::ip::udp;
typedef boost::shared_ptr<observer> observer_ptr;
class closest_nodes_observer : public observer
{
public:
closest_nodes_observer(
boost::intrusive_ptr<traversal_algorithm> const& algorithm
, node_id self
, node_id target
)
: m_algorithm(algorithm)
, m_target(target)
, m_self(self)
{}
void send(msg& p)
{
p.info_hash = m_target;
}
void timeout();
void reply(msg const&);
private:
boost::intrusive_ptr<traversal_algorithm> m_algorithm;
node_id const m_target;
node_id const m_self;
};
void closest_nodes_observer::reply(msg const& in)
{
if (!in.nodes.empty())
{
for (msg::nodes_t::const_iterator i = in.nodes.begin()
, end(in.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void closest_nodes_observer::timeout()
{
m_algorithm->failed(m_self);
}
closest_nodes::closest_nodes(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
: traversal_algorithm(
target
, branch_factor
, max_results
, table
, rpc
, table.begin()
, table.end()
)
, m_done_callback(callback)
{
boost::intrusive_ptr<closest_nodes> self(this);
add_requests();
}
void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
{
observer_ptr p(new closest_nodes_observer(this, id, m_target));
m_rpc.invoke(messages::find_node, addr, p);
}
void closest_nodes::done()
{
std::cerr << "[closest_nodes] DONE" << std::endl;
std::vector<node_entry> results;
int result_size = m_table.bucket_size();
if (result_size > (int)m_results.size()) result_size = (int)m_results.size();
for (std::vector<result>::iterator i = m_results.begin()
, end(m_results.begin() + result_size); i != end; ++i)
{
results.push_back(node_entry(i->id, i->addr));
}
m_done_callback(results);
}
void closest_nodes::initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
{
new closest_nodes(target, branch_factor, max_results, table, rpc, callback);
}
} } // namespace libtorrent::dht

View File

@ -0,0 +1,772 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <fstream>
#include <set>
#include <numeric>
#include <stdexcept>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/ref.hpp>
#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/operations.hpp>
#include "libtorrent/kademlia/node.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/traversal_algorithm.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/version.hpp"
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::second_clock;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::minutes;
using boost::posix_time::hours;
using boost::posix_time::milliseconds;
using boost::ref;
using boost::lexical_cast;
using libtorrent::dht::node_impl;
using libtorrent::dht::node_id;
using libtorrent::dht::packet_t;
using libtorrent::dht::msg;
using libtorrent::dht::packet_iterator;
namespace messages = libtorrent::dht::messages;
using namespace libtorrent::detail;
using asio::ip::udp;
typedef asio::ip::address_v4 address;
namespace
{
const int tick_period = 5; // minutes
struct count_peers
{
int& count;
count_peers(int& c): count(c) {}
void operator()(std::pair<libtorrent::dht::node_id
, libtorrent::dht::torrent_entry> const& t)
{
count += std::distance(t.second.peers.begin()
, t.second.peers.end());
}
};
boost::optional<node_id> read_id(libtorrent::entry const& d)
{
using namespace libtorrent;
using libtorrent::dht::node_id;
if (d.type() != entry::dictionary_t) return boost::optional<node_id>();
entry const* nid = d.find_key("node-id");
if (!nid
|| nid->type() != entry::string_t
|| nid->string().length() != 40)
return boost::optional<node_id>();
return boost::optional<node_id>(
boost::lexical_cast<node_id>(nid->string()));
}
}
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(dht_tracker)
#endif
// class that puts the networking and the kademlia node in a single
// unit and connecting them together.
dht_tracker::dht_tracker(asio::io_service& d, dht_settings const& settings
, entry const& bootstrap)
: m_demuxer(d)
, m_socket(m_demuxer, udp::endpoint(address(), settings.service_port))
, m_dht(bind(&dht_tracker::send_packet, this, _1), settings
, read_id(bootstrap))
, m_buffer(0)
, m_last_refresh(second_clock::universal_time() - hours(1))
, m_timer(m_demuxer)
, m_second_timer(m_demuxer)
, m_settings(settings)
, m_refresh_bucket(160)
, m_host_resolver(d)
{
using boost::bind;
m_in_buf[0].resize(1000);
m_in_buf[1].resize(1000);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
m_counter = 0;
std::fill_n(m_replies_bytes_sent, 5, 0);
std::fill_n(m_queries_bytes_received, 5, 0);
std::fill_n(m_replies_sent, 5, 0);
std::fill_n(m_queries_received, 5, 0);
m_announces = 0;
m_failed_announces = 0;
m_total_message_input = 0;
m_ut_message_input = 0;
m_lt_message_input = 0;
m_mp_message_input = 0;
m_gr_message_input = 0;
// turns on and off individual components' logging
// rpc_log().enable(false);
// node_log().enable(false);
// traversal_log().enable(false);
// dht_tracker_log.enable(false);
#endif
std::vector<udp::endpoint> initial_nodes;
if (bootstrap.type() == entry::dictionary_t)
{
if (entry const* nodes = bootstrap.find_key("nodes"))
{
if (nodes->type() == entry::string_t)
{
std::string const& s = nodes->string();
std::string::const_iterator in(s.begin());
while (std::distance(in, s.end()) >= 6)
initial_nodes.push_back(read_endpoint<udp::endpoint>(in));
}
}
}
m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this));
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
, bind(&dht_tracker::on_receive, this, _1, _2));
m_timer.expires_from_now(seconds(1));
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
m_second_timer.expires_from_now(seconds(10));
m_second_timer.async_wait(bind(&dht_tracker::second_tick, this, _1));
}
void dht_tracker::second_tick(asio::error const& e)
try
{
if (e) return;
time_duration d = m_dht.tick();
m_second_timer.expires_from_now(d);
m_second_timer.async_wait(bind(&dht_tracker::second_tick, this, _1));
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::tick(asio::error const& e)
try
{
if (e) return;
m_timer.expires_from_now(minutes(tick_period));
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
m_dht.new_write_key();
m_dht.check_refresh();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
static bool first = true;
if (first)
{
boost::filesystem::create_directory("libtorrent_logs");
}
std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc);
m_dht.print_state(st);
// count torrents
int torrents = std::distance(m_dht.begin_data(), m_dht.end_data());
// count peers
int peers = 0;
std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers));
std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app);
if (first)
{
first = false;
using boost::posix_time::to_simple_string;
pc << "\n\n ***** starting log at " << to_simple_string(
second_clock::universal_time()) << " *****\n\n"
<< "minute:active nodes:passive nodes"
":ping replies sent:ping queries recvd:ping"
":ping replies sent:ping queries recvd:ping"
":find_node replies bytes sent:find_node queries bytes recv"
":find_node replies bytes sent:find_node queries bytes recv"
":get_peers replies sent:get_peers queries recvd:get_peers"
":get_peers replies bytes sent:get_peers queries bytes recv"
":announce_peer replies sent:announce_peer queries recvd:announce_peer"
":announce_peer replies bytes sent:announce_peer queries bytes recv"
":error replies sent:error queries recvd:error"
":error replies bytes sent:error queries bytes recv"
":num torrents:num peers:announces per min"
":failed announces per min:total msgs per min"
":ut msgs per min:lt msgs per min:mp msgs per min"
":gr msgs per min\n\n";
}
int active;
int passive;
boost::tie(active, passive) = m_dht.size();
pc << (m_counter * tick_period)
<< "\t" << active
<< "\t" << passive;
for (int i = 0; i < 5; ++i)
pc << "\t" << (m_replies_sent[i] / float(tick_period))
<< "\t" << (m_queries_received[i] / float(tick_period))
<< "\t" << (m_replies_bytes_sent[i] / float(tick_period*60))
<< "\t" << (m_queries_bytes_received[i] / float(tick_period*60));
pc << "\t" << torrents
<< "\t" << peers
<< "\t" << m_announces / float(tick_period)
<< "\t" << m_failed_announces / float(tick_period)
<< "\t" << (m_total_message_input / float(tick_period))
<< "\t" << (m_ut_message_input / float(tick_period))
<< "\t" << (m_lt_message_input / float(tick_period))
<< "\t" << (m_mp_message_input / float(tick_period))
<< "\t" << (m_gr_message_input / float(tick_period))
<< std::endl;
++m_counter;
std::fill_n(m_replies_bytes_sent, 5, 0);
std::fill_n(m_queries_bytes_received, 5, 0);
std::fill_n(m_replies_sent, 5, 0);
std::fill_n(m_queries_received, 5, 0);
m_announces = 0;
m_failed_announces = 0;
m_total_message_input = 0;
m_ut_message_input = 0;
m_lt_message_input = 0;
#endif
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::announce(sha1_hash const& ih, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&
, sha1_hash const&)> f)
{
m_dht.announce(ih, listen_port, f);
}
// translate bittorrent kademlia message into the generice kademlia message
// used by the library
void dht_tracker::on_receive(asio::error const& error, size_t bytes_transferred)
try
{
int current_buffer = m_buffer;
m_buffer = (m_buffer + 1) & 1;
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
, bind(&dht_tracker::on_receive, this, _1, _2));
#ifdef TORRENT_DHT_VERBOSE_LOGGING
++m_total_message_input;
#endif
try
{
using libtorrent::entry;
using libtorrent::bdecode;
assert(bytes_transferred > 0);
entry e = bdecode(m_in_buf[current_buffer].begin()
, m_in_buf[current_buffer].end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
<< " RECEIVED [" << m_remote_endpoint[current_buffer]
<< "]:";
#endif
libtorrent::dht::msg m;
m.message_id = 0;
m.addr = m_remote_endpoint[current_buffer];
m.transaction_id = e["t"].string();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
try
{
entry const* ver = e.find_key("v");
if (!ver) throw std::exception();
std::string const& client = ver->string();
if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT"))
{
++m_ut_message_input;
TORRENT_LOG(dht_tracker) << " client: uTorrent";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT"))
{
++m_lt_message_input;
TORRENT_LOG(dht_tracker) << " client: libtorrent";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP"))
{
++m_mp_message_input;
TORRENT_LOG(dht_tracker) << " client: MooPolice";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR"))
{
++m_gr_message_input;
TORRENT_LOG(dht_tracker) << " client: GetRight";
}
else
{
TORRENT_LOG(dht_tracker) << " client: generic";
}
}
catch (std::exception&)
{
TORRENT_LOG(dht_tracker) << " client: generic";
};
#endif
std::string const& msg_type = e["y"].string();
if (msg_type == "r")
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " reply: transaction: "
<< m.transaction_id;
#endif
m.reply = true;
entry const& r = e["r"];
std::string const& id = r["id"].string();
if (id.size() != 20) throw std::runtime_error("invalid size of id");
std::copy(id.begin(), id.end(), m.id.begin());
if (entry const* n = r.find_key("values"))
{
m.peers.clear();
entry::list_type const& peers = n->list();
for (entry::list_type::const_iterator i = peers.begin()
, end(peers.end()); i != end; ++i)
{
std::string const& p = i->string();
if (p.size() < 6) continue;
std::string::const_iterator in = p.begin();
m.peers.push_back(read_endpoint<tcp::endpoint>(in));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
#endif
}
if (entry const* n = r.find_key("nodes"))
{
m.nodes.clear();
std::string const& nodes = n->string();
std::string::const_iterator i = nodes.begin();
std::string::const_iterator end = nodes.end();
while (std::distance(i, end) >= 26)
{
node_id id;
std::copy(i, i + 20, id.begin());
i += 20;
m.nodes.push_back(libtorrent::dht::node_entry(
id, read_endpoint<udp::endpoint>(i)));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
}
entry const* token = r.find_key("token");
if (token) m.write_token = *token;
}
else if (msg_type == "q")
{
m.reply = false;
entry const& a = e["a"];
std::string const& id = a["id"].string();
if (id.size() != 20) throw std::runtime_error("invalid size of id");
std::copy(id.begin(), id.end(), m.id.begin());
std::string request_kind(e["q"].string());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " query: " << request_kind;
#endif
if (request_kind == "ping")
{
m.message_id = libtorrent::dht::messages::ping;
}
else if (request_kind == "find_node")
{
std::string const& target = a["target"].string();
if (target.size() != 20) throw std::runtime_error("invalid size of target id");
std::copy(target.begin(), target.end(), m.info_hash.begin());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " target: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
m.message_id = libtorrent::dht::messages::find_node;
}
else if (request_kind == "get_peers")
{
std::string const& info_hash = a["info_hash"].string();
if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash");
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
m.message_id = libtorrent::dht::messages::get_peers;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
}
else if (request_kind == "announce_peer")
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
++m_announces;
#endif
std::string const& info_hash = a["info_hash"].string();
if (info_hash.size() != 20)
throw std::runtime_error("invalid size of info-hash");
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
m.port = a["port"].integer();
m.write_token = a["token"];
m.message_id = libtorrent::dht::messages::announce_peer;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
TORRENT_LOG(dht_tracker) << " port: " << m.port;
if (!m_dht.verify_token(m))
++m_failed_announces;
#endif
}
else
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : "
<< request_kind;
#endif
throw std::runtime_error("unsupported request: " + request_kind);
}
}
else if (msg_type == "e")
{
entry::list_type const& list = e["e"].list();
m.message_id = messages::error;
m.error_msg = list.back().string();
m.error_code = list.front().integer();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
<< m.error_msg;
#endif
}
else
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : "
<< msg_type;
#endif
throw std::runtime_error("unsupported message type: " + msg_type);
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
if (!m.reply)
{
++m_queries_received[m.message_id];
m_queries_bytes_received[m.message_id] += int(bytes_transferred);
}
TORRENT_LOG(dht_tracker) << e;
#endif
m_dht.incoming(m);
}
catch (std::exception& e)
{
std::cerr << "invalid incoming packet: " << e.what();
}
}
catch (std::exception& e)
{
assert(false);
};
entry dht_tracker::state() const
{
entry ret(entry::dictionary_t);
{
std::string nodes;
std::back_insert_iterator<std::string> out(nodes);
for (node_impl::iterator i(m_dht.begin())
, end(m_dht.end()); i != end; ++i)
write_endpoint(i->addr, out);
bucket_t cache;
m_dht.replacement_cache(cache);
for (bucket_t::iterator i(cache.begin())
, end(cache.end()); i != end; ++i)
write_endpoint(i->addr, out);
if (!nodes.empty())
ret["nodes"] = nodes;
}
ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid());
return ret;
}
void dht_tracker::add_node(udp::endpoint node)
{
m_dht.add_node(node);
}
void dht_tracker::add_node(std::pair<std::string, int> const& node)
{
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
m_host_resolver.async_resolve(q, bind(&dht_tracker::on_name_lookup
, this, _1, _2));
}
void dht_tracker::on_name_lookup(asio::error const& e, udp::resolver::iterator host)
try
{
if (e || host == udp::resolver::iterator()) return;
add_node(host->endpoint());
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::on_bootstrap()
{}
void dht_tracker::send_packet(msg const& m)
{
using libtorrent::bencode;
using libtorrent::entry;
entry e(entry::dictionary_t);
e["t"] = m.transaction_id;
std::string version_str("LT ");
std::string::iterator i = version_str.begin() + 2;
detail::write_uint8(LIBTORRENT_VERSION_MAJOR, i);
detail::write_uint8(LIBTORRENT_VERSION_MINOR, i);
e["v"] = version_str;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
<< " SENDING [" << m.addr << "]:";
TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id;
// e.print(std::cerr);
#endif
if (m.message_id == messages::error)
{
assert(m.reply);
e["y"] = "e";
entry error_list(entry::list_t);
error_list.list().push_back(entry(m.error_code));
error_list.list().push_back(entry(m.error_msg));
e["e"] = error_list;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
<< m.error_msg;
#endif
}
else if (m.reply)
{
e["y"] = "r";
e["r"] = entry(entry::dictionary_t);
entry& r = e["r"];
r["id"] = std::string(m.id.begin(), m.id.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " reply: "
<< messages::ids[m.message_id];
#endif
if (m.write_token.type() != entry::undefined_t)
r["token"] = m.write_token;
switch (m.message_id)
{
case messages::ping:
break;
case messages::find_node:
{
r["nodes"] = entry(entry::string_t);
entry& n = r["nodes"];
std::back_insert_iterator<std::string> out(n.string());
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
break;
}
case messages::get_peers:
{
if (m.peers.empty())
{
r["nodes"] = entry(entry::string_t);
entry& n = r["nodes"];
std::back_insert_iterator<std::string> out(n.string());
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
}
else
{
r["values"] = entry(entry::list_t);
entry& p = r["values"];
std::string endpoint;
endpoint.resize(6);
for (msg::peers_t::const_iterator i = m.peers.begin()
, end(m.peers.end()); i != end; ++i)
{
std::string::iterator out = endpoint.begin();
write_endpoint(*i, out);
p.list().push_back(entry(endpoint));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
#endif
}
break;
}
case messages::announce_peer:
break;
break;
}
}
else
{
e["y"] = "q";
e["a"] = entry(entry::dictionary_t);
entry& a = e["a"];
a["id"] = std::string(m.id.begin(), m.id.end());
if (m.write_token.type() != entry::undefined_t)
a["token"] = m.write_token;
assert(m.message_id <= messages::error);
e["q"] = messages::ids[m.message_id];
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " query: "
<< messages::ids[m.message_id];
#endif
switch (m.message_id)
{
case messages::find_node:
{
a["target"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " target: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
}
case messages::get_peers:
{
a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
}
case messages::announce_peer:
a["port"] = m_settings.service_port;
a["info_hash"] = boost::lexical_cast<std::string>(m.info_hash);
a["token"] = m.write_token;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " port: "
<< m_settings.service_port
<< " info_hash: " << boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
default: break;
}
}
m_send_buf.clear();
bencode(std::back_inserter(m_send_buf), e);
m_socket.send_to(asio::buffer(&m_send_buf[0]
, (int)m_send_buf.size()), m.addr);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
if (m.reply)
{
++m_replies_sent[m.message_id];
m_replies_bytes_sent[m.message_id] += int(m_send_buf.size());
}
TORRENT_LOG(dht_tracker) << e;
#endif
if (!m.piggy_backed_ping) return;
msg pm;
pm.reply = false;
pm.piggy_backed_ping = false;
pm.message_id = messages::ping;
pm.transaction_id = m.ping_transaction_id;
pm.id = m.id;
pm.addr = m.addr;
send_packet(pm);
}
}}

156
src/kademlia/find_data.cpp Normal file
View File

@ -0,0 +1,156 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/find_data.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/io.hpp>
namespace libtorrent { namespace dht
{
typedef boost::shared_ptr<observer> observer_ptr;
class find_data_observer : public observer
{
public:
find_data_observer(
boost::intrusive_ptr<find_data> const& algorithm
, node_id self
, node_id target)
: m_algorithm(algorithm)
, m_target(target)
, m_self(self)
{}
void send(msg& m)
{
m.reply = false;
m.message_id = messages::get_peers;
m.info_hash = m_target;
}
void timeout();
void reply(msg const&);
private:
boost::intrusive_ptr<find_data> m_algorithm;
node_id const m_target;
node_id const m_self;
};
void find_data_observer::reply(msg const& m)
{
if (!m.peers.empty())
{
m_algorithm->got_data(&m);
}
else
{
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void find_data_observer::timeout()
{
m_algorithm->failed(m_self);
}
find_data::find_data(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
: traversal_algorithm(
target
, branch_factor
, max_results
, table
, rpc
, table.begin()
, table.end()
)
, m_done_callback(callback)
, m_done(false)
{
boost::intrusive_ptr<find_data> self(this);
add_requests();
}
void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr)
{
if (m_done)
{
m_invoke_count = -1;
return;
}
observer_ptr p(new find_data_observer(this, id, m_target));
m_rpc.invoke(messages::get_peers, addr, p);
}
void find_data::got_data(msg const* m)
{
m_done = true;
m_done_callback(m);
}
void find_data::done()
{
if (m_invoke_count != 0) return;
if (!m_done) m_done_callback(0);
}
void find_data::initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
{
std::cerr << "find_data::initiate, key: " << target << "\n";
new find_data(target, branch_factor, max_results, table, rpc, callback);
}
} } // namespace libtorrent::dht

501
src/kademlia/node.cpp Normal file
View File

@ -0,0 +1,501 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <utility>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/function.hpp>
#include <boost/iterator_adaptors.hpp>
#include "libtorrent/io.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/random_sample.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/rpc_manager.hpp"
#include "libtorrent/kademlia/packet_iterator.hpp"
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/node.hpp"
#include "libtorrent/kademlia/refresh.hpp"
#include "libtorrent/kademlia/closest_nodes.hpp"
#include "libtorrent/kademlia/find_data.hpp"
using boost::bind;
using boost::posix_time::second_clock;
using boost::posix_time::seconds;
using boost::posix_time::minutes;
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
namespace libtorrent { namespace dht
{
typedef boost::shared_ptr<observer> observer_ptr;
// TODO: configurable
enum { announce_interval = 30 };
using asio::ip::udp;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(node)
#endif
node_id generate_id()
{
char random[20];
std::srand(std::time(0));
std::generate(random, random + 20, &std::rand);
hasher h;
h.update(random, 20);
return h.final();
}
// remove peers that have timed out
void purge_peers(std::set<peer_entry>& peers)
{
for (std::set<peer_entry>::iterator i = peers.begin()
, end(peers.end()); i != end;)
{
// the peer has timed out
if (i->added + minutes(int(announce_interval * 1.5f)) < second_clock::universal_time())
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << "peer timed out at: " << i->addr.address();
#endif
peers.erase(i++);
}
else
++i;
}
}
void nop() {}
node_impl::node_impl(boost::function<void(msg const&)> const& f
, dht_settings const& settings, boost::optional<node_id> node_id)
: m_settings(settings)
, m_id(node_id ? *node_id : generate_id())
, m_table(m_id, 8, settings)
, m_rpc(bind(&node_impl::incoming_request, this, _1)
, m_id, m_table, f)
, m_last_tracker_tick(boost::posix_time::second_clock::universal_time())
{
m_secret[0] = std::rand();
m_secret[1] = std::rand();
}
bool node_impl::verify_token(msg const& m)
{
if (m.write_token.type() != entry::string_t)
return false;
std::string const& token = m.write_token.string();
if (token.length() != 4) return false;
hasher h1;
std::string address = m.addr.address().to_string();
h1.update(&address[0], address.length());
h1.update((char*)&m_secret[0], sizeof(m_secret[0]));
h1.update((char*)&m.info_hash[0], sha1_hash::size);
sha1_hash h = h1.final();
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
return true;
hasher h2;
h2.update(&address[0], address.length());
h2.update((char*)&m_secret[1], sizeof(m_secret[1]));
h = h2.final();
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
return true;
return false;
}
entry node_impl::generate_token(msg const& m)
{
std::string token;
token.resize(4);
hasher h;
std::string address = m.addr.address().to_string();
h.update(&address[0], address.length());
h.update((char*)&m_secret[0], sizeof(m_secret[0]));
h.update((char*)&m.info_hash[0], sha1_hash::size);
sha1_hash hash = h.final();
std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]);
return entry(token);
}
void node_impl::check_refresh()
{
for (int i = 0; i < 160; ++i)
{
if (m_table.should_refresh(i))
{
refresh_bucket(i);
}
}
}
void node_impl::refresh(node_id const& id
, boost::function0<void> f)
{
// use the 'bucket size' closest nodes
// to start the refresh with
std::vector<node_entry> start;
start.reserve(m_table.bucket_size());
m_table.find_node(id, start, false);
refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, f);
}
void node_impl::bootstrap(std::vector<udp::endpoint> const& nodes
, boost::function0<void> f)
{
std::vector<node_entry> start;
start.reserve(nodes.size());
std::copy(nodes.begin(), nodes.end(), std::back_inserter(start));
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, f);
}
void node_impl::refresh()
{
std::vector<node_entry> start;
start.reserve(m_table.size().get<0>());
std::copy(m_table.begin(), m_table.end(), std::back_inserter(start));
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
}
int node_impl::bucket_size(int bucket)
{
return m_table.bucket_size(bucket);
}
void node_impl::new_write_key()
{
m_secret[1] = m_secret[0];
m_secret[0] = std::rand();
}
void node_impl::refresh_bucket(int bucket)
{
assert(bucket >= 0 && bucket < 160);
// generate a random node_id within the given bucket
node_id target = generate_id();
int num_bits = 160 - bucket;
node_id mask(0);
for (int i = 0; i < num_bits; ++i)
{
int byte = i / 8;
mask[byte] |= 0x80 >> (i % 8);
}
node_id root = m_id;
root &= mask;
target &= ~mask;
target |= root;
// make sure this is in another subtree than m_id
// clear the (num_bits - 1) bit and then set it to the
// inverse of m_id's corresponding bit.
target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8));
target[(num_bits - 1) / 8] |=
(~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8));
int new_bucket = distance_exp(m_id, target);
assert(new_bucket == bucket);
std::vector<node_entry> start;
start.reserve(m_table.bucket_size());
m_table.find_node(target, start, false, m_table.bucket_size());
refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
}
void node_impl::incoming(msg const& m)
{
if (m_rpc.incoming(m))
{
refresh();
}
}
namespace
{
class announce_observer : public observer
{
public:
announce_observer(sha1_hash const& info_hash, int listen_port
, entry const& write_token)
: m_info_hash(info_hash)
, m_listen_port(listen_port)
, m_token(write_token)
{}
void send(msg& m)
{
m.port = m_listen_port;
m.info_hash = m_info_hash;
m.write_token = m_token;
}
void timeout() {}
void reply(msg const&) {}
private:
sha1_hash m_info_hash;
int m_listen_port;
entry m_token;
};
class get_peers_observer : public observer
{
public:
get_peers_observer(sha1_hash const& info_hash, int listen_port
, rpc_manager& rpc
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
: m_info_hash(info_hash)
, m_listen_port(listen_port)
, m_rpc(rpc)
, m_fun(f)
{}
void send(msg& m)
{
m.port = m_listen_port;
m.info_hash = m_info_hash;
}
void timeout() {}
void reply(msg const& r)
{
m_rpc.invoke(messages::announce_peer, r.addr
, boost::shared_ptr<observer>(
new announce_observer(m_info_hash, m_listen_port, r.write_token)));
m_fun(r.peers, m_info_hash);
}
private:
sha1_hash m_info_hash;
int m_listen_port;
rpc_manager& m_rpc;
boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> m_fun;
};
void announce_fun(std::vector<node_entry> const& v, rpc_manager& rpc
, int listen_port, sha1_hash const& ih
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
{
bool nodes = false;
// only store on the first k nodes
for (std::vector<node_entry>::const_iterator i = v.begin()
, end(v.end()); i != end; ++i)
{
rpc.invoke(messages::get_peers, i->addr, boost::shared_ptr<observer>(
new get_peers_observer(ih, listen_port, rpc, f)));
nodes = true;
}
}
}
namespace
{
struct dummy_observer : observer
{
virtual void reply(msg const&) {}
virtual void timeout() {}
virtual void send(msg&) {}
};
}
void node_impl::add_node(udp::endpoint node)
{
// ping the node, and if we get a reply, it
// will be added to the routing table
observer_ptr p(new dummy_observer());
m_rpc.invoke(messages::ping, node, p);
}
void node_impl::announce(sha1_hash const& info_hash, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
{
// search for nodes with ids close to id, and then invoke the
// get_peers and then announce_peer rpc on them.
closest_nodes::initiate(info_hash, m_settings.search_branching
, m_table.bucket_size(), m_table, m_rpc
, boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port
, info_hash, f));
}
time_duration node_impl::tick()
{
time_duration d = m_rpc.tick();
ptime now(second_clock::universal_time());
if (now - m_last_tracker_tick < minutes(10)) return d;
m_last_tracker_tick = now;
// look through all peers and see if any have timed out
for (data_iterator i = begin_data(), end(end_data()); i != end;)
{
torrent_entry& t = i->second;
node_id const& key = i->first;
++i;
purge_peers(t.peers);
// if there are no more peers, remove the entry altogether
if (t.peers.empty())
{
table_t::iterator i = m_map.find(key);
if (i != m_map.end()) m_map.erase(i);
}
}
return d;
}
void node_impl::on_announce(msg const& m, msg& reply)
{
if (!verify_token(m))
{
reply.message_id = messages::error;
reply.error_code = 203;
reply.error_msg = "Incorrect write token in announce_peer message";
return;
}
torrent_entry& v = m_map[m.info_hash];
peer_entry e;
e.addr = tcp::endpoint(m.addr.address(), m.addr.port());
e.added = second_clock::universal_time();
std::set<peer_entry>::iterator i = v.peers.find(e);
if (i != v.peers.end()) v.peers.erase(i++);
v.peers.insert(i, e);
}
namespace
{
tcp::endpoint get_endpoint(peer_entry const& p)
{
return p.addr;
}
}
bool node_impl::on_find(msg const& m, std::vector<tcp::endpoint>& peers) const
{
table_t::const_iterator i = m_map.find(m.info_hash);
if (i == m_map.end()) return false;
torrent_entry const& v = i->second;
int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply);
peers.clear();
peers.reserve(num);
random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint)
, boost::make_transform_iterator(v.peers.end(), &get_endpoint)
, std::back_inserter(peers), num);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<tcp::endpoint>::iterator i = peers.begin()
, end(peers.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << *i;
}
#endif
return true;
}
void node_impl::incoming_request(msg const& m)
{
msg reply;
switch (m.message_id)
{
case messages::ping:
break;
case messages::get_peers:
{
reply.info_hash = m.info_hash;
reply.write_token = generate_token(m);
if (!on_find(m, reply.peers))
{
// we don't have any peers for this info_hash,
// return nodes instead
m_table.find_node(m.info_hash, reply.nodes, false);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
, end(reply.nodes.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
}
#endif
}
}
break;
case messages::find_node:
{
reply.info_hash = m.info_hash;
m_table.find_node(m.info_hash, reply.nodes, false);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
, end(reply.nodes.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
}
#endif
}
break;
case messages::announce_peer:
{
on_announce(m, reply);
}
break;
};
if (m_table.need_node(m.id))
m_rpc.reply_with_ping(reply, m);
else
m_rpc.reply(reply, m);
}
} } // namespace libtorrent::dht

97
src/kademlia/node_id.cpp Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <algorithm>
#include <iomanip>
#include <cassert>
#include <boost/bind.hpp>
#include "libtorrent/kademlia/node_id.hpp"
using boost::bind;
namespace libtorrent { namespace dht
{
// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2)
{
node_id ret;
node_id::iterator k = ret.begin();
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, end(n1.end()); i != end; ++i, ++j, ++k)
{
*k = *i ^ *j;
}
return ret;
}
// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
{
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
{
boost::uint8_t lhs = (*i ^ *k);
boost::uint8_t rhs = (*j ^ *k);
if (lhs < rhs) return true;
if (lhs > rhs) return false;
}
return false;
}
// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// useful for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2)
{
int byte = node_id::size - 1;
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, end(n1.end()); i != end; ++i, ++j, --byte)
{
assert(byte >= 0);
boost::uint8_t t = *i ^ *j;
if (t == 0) continue;
// we have found the first non-zero byte
// return the bit-number of the first bit
// that differs
int bit = byte * 8;
for (int b = 7; b > 0; --b)
if (t >= (1 << b)) return bit + b;
return bit;
}
return 0;
}
} } // namespace libtorrent::dht

188
src/kademlia/refresh.cpp Normal file
View File

@ -0,0 +1,188 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/refresh.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/io.hpp>
#include <boost/bind.hpp>
using boost::bind;
namespace libtorrent { namespace dht
{
using asio::ip::udp;
TORRENT_DEFINE_LOG(refresh)
typedef boost::shared_ptr<observer> observer_ptr;
class refresh_observer : public observer
{
public:
refresh_observer(
boost::intrusive_ptr<refresh> const& algorithm
, node_id self
, node_id target
)
: m_target(target)
, m_self(self)
, m_algorithm(algorithm)
{}
void send(msg& m)
{
m.info_hash = m_target;
}
void timeout();
void reply(msg const& m);
private:
node_id const m_target;
node_id const m_self;
boost::intrusive_ptr<refresh> m_algorithm;
};
void refresh_observer::reply(msg const& in)
{
if (!in.nodes.empty())
{
for (msg::nodes_t::const_iterator i = in.nodes.begin()
, end(in.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void refresh_observer::timeout()
{
m_algorithm->failed(m_self);
}
class ping_observer : public observer
{
public:
ping_observer(
boost::intrusive_ptr<refresh> const& algorithm
, node_id self
)
: m_self(self)
, m_algorithm(algorithm)
{}
void send(msg& p) {}
void timeout();
void reply(msg const& m);
private:
node_id const m_self;
boost::intrusive_ptr<refresh> m_algorithm;
};
void ping_observer::reply(msg const& m)
{
m_algorithm->ping_reply(m_self);
}
void ping_observer::timeout()
{
m_algorithm->ping_timeout(m_self);
}
void refresh::invoke(node_id const& id, udp::endpoint addr)
{
observer_ptr p(new refresh_observer(
this
, id
, m_target
));
m_rpc.invoke(messages::find_node, addr, p);
}
void refresh::done()
{
m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ?
m_results.begin() + m_max_results : m_results.end();
invoke_pings_or_finish();
}
void refresh::ping_reply(node_id id)
{
m_active_pings--;
invoke_pings_or_finish();
}
void refresh::ping_timeout(node_id id)
{
m_active_pings--;
invoke_pings_or_finish();
}
void refresh::invoke_pings_or_finish()
{
while (m_active_pings < m_max_active_pings)
{
if (m_leftover_nodes_iterator == m_results.end()) break;
result const& node = *m_leftover_nodes_iterator;
// Skip initial nodes
if (node.flags & result::initial)
{
++m_leftover_nodes_iterator;
continue;
}
observer_ptr p(new ping_observer(this, node.id));
m_rpc.invoke(messages::ping, node.addr, p);
++m_active_pings;
++m_leftover_nodes_iterator;
}
if (m_active_pings == 0)
{
m_done_callback();
}
}
} } // namespace libtorrent::dht

View File

@ -0,0 +1,417 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <vector>
#include <deque>
#include <algorithm>
#include <functional>
#include <numeric>
#include <boost/cstdint.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/session_settings.hpp"
using boost::bind;
using boost::uint8_t;
using boost::posix_time::second_clock;
using boost::posix_time::minutes;
using boost::posix_time::seconds;
using boost::posix_time::hours;
namespace pt = boost::posix_time;
namespace libtorrent { namespace dht
{
using asio::ip::udp;
typedef asio::ip::address_v4 address;
routing_table::routing_table(node_id const& id, int bucket_size
, dht_settings const& settings)
: m_bucket_size(bucket_size)
, m_settings(settings)
, m_id(id)
, m_lowest_active_bucket(160)
{
std::fill(m_bucket_activity.begin(), m_bucket_activity.end()
, second_clock::universal_time() - hours(1));
}
boost::tuple<int, int> routing_table::size() const
{
int nodes = 0;
int replacements = 0;
for (table_t::const_iterator i = m_buckets.begin()
, end(m_buckets.end()); i != end; ++i)
{
nodes += i->first.size();
replacements += i->second.size();
}
return boost::make_tuple(nodes, replacements);
}
void routing_table::print_state(std::ostream& os) const
{
os << "kademlia routing table state\n"
<< "bucket_size: " << m_bucket_size << "\n"
<< "node_id: " << m_id << "\n\n";
os << "number of nodes per bucket:\n"
"live\n";
for (int k = 0; k < 8; ++k)
{
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << (int(i->first.size()) > (7 - k) ? "|" : " ");
}
os << "\n";
}
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << "+";
}
os << "\n";
for (int k = 0; k < 8; ++k)
{
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << (int(i->second.size()) > k ? "|" : " ");
}
os << "\n";
}
os << "cached\n-----------\n";
os << "nodes:\n";
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
int bucket_index = int(i - m_buckets.begin());
os << "bucket " << bucket_index << " "
<< to_simple_string(m_bucket_activity[bucket_index])
<< " " << (bucket_index >= m_lowest_active_bucket?"active":"inactive")
<< "\n";
for (bucket_t::const_iterator j = i->first.begin()
, end(i->first.end()); j != end; ++j)
{
os << "ip: " << j->addr << " fails: " << j->fail_count
<< " id: " << j->id << "\n";
}
}
}
bool routing_table::should_refresh(int bucket)
{
assert(bucket < 160);
assert(bucket >= 0);
// lower than or equal to since a refresh of bucket 0 will
// effectively refresh the lowest active bucket as well
if (bucket <= m_lowest_active_bucket && bucket > 0) return false;
if (m_bucket_activity[bucket] + minutes(15)
> second_clock::universal_time())
return false;
return true;
}
void routing_table::replacement_cache(bucket_t& nodes) const
{
for (table_t::const_iterator i = m_buckets.begin()
, end(m_buckets.end()); i != end; ++i)
{
std::copy(i->second.begin(), i->second.end()
, std::back_inserter(nodes));
}
}
bool routing_table::need_node(node_id const& id)
{
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t& rb = m_buckets[bucket_index].second;
// if the replacement cache is full, we don't
// need another node. The table is fine the
// way it is.
if ((int)rb.size() >= m_bucket_size) return false;
// if the node already exists, we don't need it
if (std::find_if(b.begin(), b.end(), bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id)) != b.end()) return false;
if (std::find_if(rb.begin(), rb.end(), bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id)) != rb.end()) return false;
return true;
}
void routing_table::node_failed(node_id const& id)
{
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t& rb = m_buckets[bucket_index].second;
bucket_t::iterator i = std::find_if(b.begin(), b.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
if (i == b.end()) return;
if (rb.empty())
{
++i->fail_count;
if (i->fail_count >= m_settings.max_fail_count)
{
b.erase(i);
assert(m_lowest_active_bucket <= bucket_index);
while (m_buckets[m_lowest_active_bucket].first.empty()
&& m_lowest_active_bucket < 160)
{
++m_lowest_active_bucket;
}
}
return;
}
b.erase(i);
b.push_back(rb.back());
rb.erase(rb.end() - 1);
}
// this function is called every time the node sees
// a sign of a node being alive. This node will either
// be inserted in the k-buckets or be moved to the top
// of its bucket.
// the return value indicates if the table needs a refresh.
// if true, the node should refresh the table (i.e. do a find_node
// on its own id)
bool routing_table::node_seen(node_id const& id, udp::endpoint addr)
{
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t::iterator i = std::find_if(b.begin(), b.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
bool ret = need_bootstrap();
m_bucket_activity[bucket_index] = second_clock::universal_time();
if (i != b.end())
{
// TODO: what do we do if we see a node with
// the same id as a node at a different address?
// assert(i->addr == addr);
// we already have the node in our bucket
// just move it to the back since it was
// the last node we had any contact with
// in this bucket
b.erase(i);
b.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "replacing node: " << id << " " << addr;
return ret;
}
// if the node was not present in our list
// we will only insert it if there is room
// for it, or if some of our nodes have gone
// offline
if ((int)b.size() < m_bucket_size)
{
b.push_back(node_entry(id, addr));
if (bucket_index < m_lowest_active_bucket)
m_lowest_active_bucket = bucket_index;
// TORRENT_LOG(table) << "inserting node: " << id << " " << addr;
return ret;
}
// if there is no room, we look for nodes marked as stale
// in the k-bucket. If we find one, we can replace it.
// A node is considered stale if it has failed at least one
// time. Here we choose the node that has failed most times.
// If we don't find one, place this node in the replacement-
// cache and replace any nodes that will fail in the future
// with nodes from that cache.
i = std::max_element(b.begin(), b.end()
, bind(std::less<int>()
, bind(&node_entry::fail_count, _1)
, bind(&node_entry::fail_count, _2)));
if (i != b.end() && i->fail_count > 0)
{
// i points to a node that has been marked
// as stale. Replace it with this new one
b.erase(i);
b.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr;
return ret;
}
// if we don't have any identified stale nodes in
// the bucket, and the bucket is full, we have to
// cache this node and wait until some node fails
// and then replace it.
bucket_t& rb = m_buckets[bucket_index].second;
i = std::find_if(rb.begin(), rb.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
// if the node is already in the replacement bucket
// just return.
if (i != rb.end()) return ret;
if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin());
rb.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr;
return ret;
}
bool routing_table::need_bootstrap() const
{
for (const_iterator i = begin(); i != end(); ++i)
{
if (i->fail_count == 0) return false;
}
return true;
}
// fills the vector with the k nodes from our buckets that
// are nearest to the given id.
void routing_table::find_node(node_id const& target
, std::vector<node_entry>& l, bool include_self, int count)
{
l.clear();
if (count == 0) count = m_bucket_size;
l.reserve(count);
int bucket_index = distance_exp(m_id, target);
bucket_t& b = m_buckets[bucket_index].first;
// copy all nodes that hasn't failed into the target
// vector.
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
, bind(&node_entry::fail_count, _1));
assert((int)l.size() <= count);
if ((int)l.size() == count)
{
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
// if we didn't have enough nodes in that bucket
// we have to reply with nodes from buckets closer
// to us. i.e. all the buckets in the range
// [0, bucket_index) if we are to include ourself
// or [1, bucket_index) if not.
bucket_t tmpb;
for (int i = include_self?0:1; i < count; ++i)
{
bucket_t& b = m_buckets[i].first;
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb)
, bind(&node_entry::fail_count, _1));
}
std::random_shuffle(tmpb.begin(), tmpb.end());
size_t to_copy = (std::min)(m_bucket_size - l.size()
, tmpb.size());
std::copy(tmpb.begin(), tmpb.begin() + to_copy
, std::back_inserter(l));
assert((int)l.size() <= m_bucket_size);
// return if we have enough nodes or if the bucket index
// is the biggest index available (there are no more buckets)
// to look in.
if ((int)l.size() == count
|| bucket_index == (int)m_buckets.size() - 1)
{
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i)
{
bucket_t& b = m_buckets[i].first;
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
, bind(&node_entry::fail_count, _1));
if ((int)l.size() >= count)
{
l.erase(l.begin() + count, l.end());
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
}
assert((int)l.size() == count
|| std::distance(l.begin(), l.end()) < m_bucket_size);
assert((int)l.size() <= count);
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
}
routing_table::iterator routing_table::begin() const
{
return iterator(m_buckets.begin(), m_buckets.end());
}
routing_table::iterator routing_table::end() const
{
return iterator(m_buckets.end(), m_buckets.end());
}
} } // namespace libtorrent::dht

View File

@ -0,0 +1,343 @@
/*
Copyright (c) 2006, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <libtorrent/io.hpp>
#include <libtorrent/invariant_check.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/hasher.hpp>
#include <fstream>
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::milliseconds;
using boost::shared_ptr;
namespace libtorrent { namespace dht
{
namespace io = libtorrent::detail;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(rpc)
#endif
node_id generate_id();
rpc_manager::rpc_manager(fun const& f, node_id const& our_id
, routing_table& table, send_fun const& sf)
: m_next_transaction_id(rand() % max_transactions)
, m_oldest_transaction_id(m_next_transaction_id)
, m_incoming(f)
, m_send(sf)
, m_our_id(our_id)
, m_table(table)
, m_timer(boost::posix_time::microsec_clock::universal_time())
, m_random_number(generate_id())
{
std::srand(time(0));
}
rpc_manager::~rpc_manager()
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Destructing";
#endif
}
#ifndef NDEBUG
void rpc_manager::check_invariant() const
{
assert(m_oldest_transaction_id >= 0);
assert(m_oldest_transaction_id < max_transactions);
assert(m_next_transaction_id >= 0);
assert(m_next_transaction_id < max_transactions);
for (int i = m_next_transaction_id; i != m_oldest_transaction_id;
i = (i + 1) % max_transactions)
{
assert(!m_transactions[i]);
}
}
#endif
bool rpc_manager::incoming(msg const& m)
{
INVARIANT_CHECK;
if (m.reply)
{
// if we don't have the transaction id in our
// request list, ignore the packet
if (m.transaction_id.size() != 2)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with invalid transaction id size: "
<< m.transaction_id.size() << " from " << m.addr;
#endif
return false;
}
std::string::const_iterator i = m.transaction_id.begin();
int tid = io::read_uint16(i);
if (tid >= (int)m_transactions.size()
|| tid < 0)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
boost::shared_ptr<observer> o = m_transactions[tid];
if (!o)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
if (m.addr != o->target_addr)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app);
reply_stats << m.addr << "\t" << (microsec_clock::universal_time()
- o->sent).total_milliseconds() << std::endl;
#endif
o->reply(m);
m_transactions[tid].reset();
if (m.piggy_backed_ping)
{
// there is a ping request piggy
// backed in this reply
msg ph;
ph.message_id = messages::ping;
ph.transaction_id = m.ping_transaction_id;
ph.id = m_our_id;
ph.addr = m.addr;
msg empty;
reply(empty, ph);
}
return m_table.node_seen(m.id, m.addr);
}
else
{
// this is an incoming request
m_incoming(m);
}
return false;
}
time_duration rpc_manager::tick()
{
INVARIANT_CHECK;
using boost::posix_time::microsec_clock;
const int timeout_ms = 20 * 1000;
// look for observers that has timed out
if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms);
for (;m_next_transaction_id != m_oldest_transaction_id;
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions)
{
assert(m_oldest_transaction_id >= 0);
assert(m_oldest_transaction_id < max_transactions);
boost::shared_ptr<observer>& o = m_transactions[m_oldest_transaction_id];
if (!o) continue;
time_duration diff = o->sent + milliseconds(timeout_ms)
- microsec_clock::universal_time();
if (diff > seconds(0))
{
if (diff < seconds(1)) return seconds(1);
return diff;
}
o->timeout();
o.reset();
}
return milliseconds(timeout_ms);
}
unsigned int rpc_manager::new_transaction_id()
{
INVARIANT_CHECK;
unsigned int tid = m_next_transaction_id;
m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions;
assert(!m_transactions[tid]);
if (m_transactions[m_next_transaction_id])
{
m_transactions[m_next_transaction_id]->timeout();
m_transactions[m_next_transaction_id].reset();
assert(m_oldest_transaction_id == m_next_transaction_id);
}
if (m_oldest_transaction_id == m_next_transaction_id)
{
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions;
update_oldest_transaction_id();
}
#ifndef NDEBUG
assert(!m_transactions[m_next_transaction_id]);
for (int i = (m_next_transaction_id + 1) % max_transactions;
i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
{
assert(!m_transactions[i]);
}
#endif
return tid;
}
void rpc_manager::update_oldest_transaction_id()
{
INVARIANT_CHECK;
while (!m_transactions[m_oldest_transaction_id])
{
m_oldest_transaction_id = (m_oldest_transaction_id + 1)
% max_transactions;
if (m_oldest_transaction_id == m_next_transaction_id)
break;
}
}
void rpc_manager::invoke(int message_id, udp::endpoint target_addr
, shared_ptr<observer> o)
{
INVARIANT_CHECK;
msg m;
m.message_id = message_id;
m.reply = false;
m.id = m_our_id;
m.addr = target_addr;
int tid = new_transaction_id();
m.transaction_id.clear();
std::back_insert_iterator<std::string> out(m.transaction_id);
io::write_uint16(tid, out);
o->send(m);
m_transactions[tid] = o;
o->sent = boost::posix_time::microsec_clock::universal_time();
o->target_addr = target_addr;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id]
<< " -> " << target_addr;
#endif
m_send(m);
}
void rpc_manager::reply(msg& m, msg const& reply_to)
{
INVARIANT_CHECK;
if (m.message_id != messages::error)
m.message_id = reply_to.message_id;
m.addr = reply_to.addr;
m.reply = true;
m.piggy_backed_ping = false;
m.id = m_our_id;
m.transaction_id = reply_to.transaction_id;
m_send(m);
}
namespace
{
struct dummy_observer : observer
{
virtual void reply(msg const&) {}
virtual void timeout() {}
virtual void send(msg&) {}
};
}
void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
{
INVARIANT_CHECK;
if (m.message_id != messages::error)
m.message_id = reply_to.message_id;
m.addr = reply_to.addr;
m.reply = true;
m.piggy_backed_ping = true;
m.id = m_our_id;
m.transaction_id = reply_to.transaction_id;
int ptid = new_transaction_id();
m.ping_transaction_id.clear();
std::back_insert_iterator<std::string> out(m.ping_transaction_id);
io::write_uint16(ptid, out);
boost::shared_ptr<observer> o(new dummy_observer);
m_transactions[ptid] = o;
o->sent = boost::posix_time::microsec_clock::universal_time();
o->target_addr = m.addr;
m_send(m);
}
} } // namespace libtorrent::dht

View File

@ -0,0 +1,162 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <boost/bind.hpp>
using boost::bind;
using asio::ip::udp;
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(traversal)
#endif
void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags)
{
if (m_failed.find(addr) != m_failed.end()) return;
result const entry(id, addr, flags);
std::vector<result>::iterator i = std::lower_bound(
m_results.begin()
, m_results.end()
, entry
, bind(
compare_ref
, bind(&result::id, _1)
, bind(&result::id, _2)
, m_target
)
);
if (i == m_results.end() || i->id != id)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "adding result: " << id << " " << addr;
#endif
m_results.insert(i, entry);
}
}
void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr)
{
add_entry(id, addr, 0);
}
void traversal_algorithm::finished(node_id const& id)
{
m_invoke_count--;
add_requests();
if (m_invoke_count == 0) done();
}
void traversal_algorithm::failed(node_id const& id)
{
m_invoke_count--;
std::vector<result>::iterator i = std::find_if(
m_results.begin()
, m_results.end()
, bind(
std::equal_to<node_id>()
, bind(&result::id, _1)
, id
)
);
assert(i != m_results.end());
assert(i->flags & result::queried);
m_failed.insert(i->addr);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr;
#endif
m_results.erase(i);
m_table.node_failed(id);
add_requests();
if (m_invoke_count == 0) done();
}
void traversal_algorithm::add_request(node_id const& id, udp::endpoint addr)
{
invoke(id, addr);
m_invoke_count++;
}
namespace
{
bool bitwise_nand(unsigned char lhs, unsigned char rhs)
{
return (lhs & rhs) == 0;
}
}
void traversal_algorithm::add_requests()
{
while (m_invoke_count < m_branch_factor)
{
// Find the first node that hasn't already been queried.
// TODO: Better heuristic
std::vector<result>::iterator i = std::find_if(
m_results.begin()
, last_iterator()
, bind(
&bitwise_nand
, bind(&result::flags, _1)
, (unsigned char)result::queried
)
);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i);
#endif
if (i == last_iterator()) break;
add_request(i->id, i->addr);
i->flags |= result::queried;
}
}
std::vector<traversal_algorithm::result>::iterator traversal_algorithm::last_iterator()
{
return (int)m_results.size() >= m_max_results ?
m_results.begin() + m_max_results
: m_results.end();
}
} } // namespace libtorrent::dht

View File

@ -1081,6 +1081,10 @@ namespace libtorrent
using namespace boost::posix_time;
(*m_logger) << to_simple_string(second_clock::universal_time())
<< " <== DHT_PORT [ p: " << listen_port << " ]\n";
#endif
#ifndef TORRENT_DISABLE_DHT
if (m_ses.m_dht) m_ses.m_dht->add_node(udp::endpoint(
m_remote.address(), listen_port));
#endif
}

View File

@ -70,6 +70,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp"
using namespace boost::posix_time;
using boost::shared_ptr;
@ -430,7 +431,7 @@ namespace libtorrent { namespace detail
session_impl::session_impl(
std::pair<int, int> listen_port_range
, const fingerprint& cl_fprint
, fingerprint const& cl_fprint
, char const* listen_interface)
: m_tracker_manager(m_settings)
, m_listen_port_range(listen_port_range)
@ -858,14 +859,11 @@ namespace libtorrent { namespace detail
}
}
// tick() will set the used upload quota
// second_tick() will set the used upload quota
t.second_tick(m_stat, tick_interval);
++i;
}
// don't pass in the tick_interval here, because
// the stats have already been adjusted in
// the peer's second tick.
m_stat.second_tick(tick_interval);
// distribute the maximum upload rate among the torrents
@ -959,10 +957,10 @@ namespace libtorrent { namespace detail
}
catch (std::exception& e)
{
#ifndef NDEBUG
#ifndef NDEBUG
std::cerr << e.what() << "\n";
std::string err = e.what();
#endif
#endif
assert(false);
}
@ -984,13 +982,20 @@ namespace libtorrent { namespace detail
m_tracker_manager.queue_request(m_selector, req, login);
}
}
tracker_timer.expires_from_now(boost::posix_time::seconds(
m_settings.stop_tracker_timeout));
tracker_timer.async_wait(bind(&demuxer::interrupt, &m_selector));
ptime start(microsec_clock::universal_time());
l.unlock();
m_selector.reset();
m_selector.run();
while (microsec_clock::universal_time() - start < seconds(
m_settings.stop_tracker_timeout)
&& !m_tracker_manager.empty())
{
tracker_timer.expires_from_now(boost::posix_time::milliseconds(100));
tracker_timer.async_wait(bind(&demuxer::interrupt, &m_selector));
m_selector.reset();
m_selector.run();
}
l.lock();
assert(m_abort);
@ -1263,6 +1268,14 @@ namespace libtorrent
d->info_hash = ti.info_hash();
d->resume_data = resume_data;
#ifndef TORRENT_DISABLE_DHT
torrent_info::nodes_t const& nodes = ti.nodes();
std::for_each(nodes.begin(), nodes.end(), bind(
(void(dht::dht_tracker::*)(std::pair<std::string, int> const&))
&dht::dht_tracker::add_node
, boost::ref(m_impl.m_dht), _1));
#endif
// add the torrent to the queue to be checked
m_checker_impl.m_torrents.push_back(d);
// and notify the thread that it got another
@ -1444,6 +1457,43 @@ namespace libtorrent
return s;
}
#ifndef TORRENT_DISABLE_DHT
void session::start_dht(entry const& startup_state)
{
m_impl.m_dht.reset(new dht::dht_tracker(m_impl.m_selector
, m_impl.m_dht_settings, startup_state));
}
void session::stop_dht()
{
m_impl.m_dht.reset();
}
void session::set_dht_settings(dht_settings const& settings)
{
if (settings.service_port != m_impl.m_dht_settings.service_port
&& m_impl.m_dht)
{
assert(false); // not implemented yet
// TODO: change dht service port!
}
m_impl.m_dht_settings = settings;
}
entry session::dht_state() const
{
assert(m_impl.m_dht);
return m_impl.m_dht->state();
}
void session::add_dht_node(std::pair<std::string, int> const& node)
{
assert(m_impl.m_dht);
m_impl.m_dht->add_node(node);
}
#endif
bool session::is_listening() const
{
session_impl::mutex_t::scoped_lock l(m_impl.m_mutex);

View File

@ -211,6 +211,9 @@ namespace libtorrent
, m_complete(-1)
, m_incomplete(-1)
, m_host_resolver(ses.m_selector)
#ifndef TORRENT_DISABLE_DHT
, m_dht_announce_timer(ses.m_selector)
#endif
, m_policy()
, m_ses(ses)
, m_checker(checker)
@ -276,6 +279,14 @@ namespace libtorrent
m_policy.reset(new policy(this));
init();
#ifndef TORRENT_DISABLE_DHT
if (!tf.priv())
{
m_dht_announce_timer.expires_from_now(seconds(10));
m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1));
}
#endif
}
torrent::torrent(
@ -300,6 +311,9 @@ namespace libtorrent
, m_complete(-1)
, m_incomplete(-1)
, m_host_resolver(ses.m_selector)
#ifndef TORRENT_DISABLE_DHT
, m_dht_announce_timer(ses.m_selector)
#endif
, m_policy()
, m_ses(ses)
, m_checker(checker)
@ -368,6 +382,10 @@ namespace libtorrent
m_policy.reset(new policy(this));
m_torrent_file.add_tracker(tracker_url);
#ifndef TORRENT_DISABLE_DHT
m_dht_announce_timer.expires_from_now(seconds(10));
m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1));
#endif
}
torrent::~torrent()
@ -419,11 +437,34 @@ namespace libtorrent
m_net_interface = tcp::endpoint(address::from_string(net_interface), 0);
}
#ifndef TORRENT_DISABLE_DHT
void torrent::on_dht_announce(asio::error const& e)
{
if (e) return;
m_dht_announce_timer.expires_from_now(boost::posix_time::minutes(30));
m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1));
if (!m_ses.m_dht) return;
m_ses.m_dht->announce(m_torrent_file.info_hash()
, m_ses.m_listen_interface.port()
, bind(&torrent::on_dht_announce_response, this, _1));
}
void torrent::on_dht_announce_response(std::vector<tcp::endpoint> const& peers)
{
std::for_each(peers.begin(), peers.end(), bind(
&policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0)));
}
#endif
// returns true if it is time for this torrent to make another
// tracker request
bool torrent::should_request()
{
INVARIANT_CHECK;
if (m_torrent_file.trackers().empty()) return false;
if (m_just_paused)
{
@ -962,22 +1003,23 @@ namespace libtorrent
m_resolving_web_seeds.insert(url);
if (m_ses.m_settings.proxy_ip.empty())
{
tcp::resolver::query q(hostname, "0");
tcp::resolver::query q(hostname, boost::lexical_cast<std::string>(port));
m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup
, shared_from_this(), _1, _2, port, url));
, shared_from_this(), _1, _2, url));
}
else
{
// use proxy
tcp::resolver::query q(m_ses.m_settings.proxy_ip, "0");
tcp::resolver::query q(m_ses.m_settings.proxy_ip
, boost::lexical_cast<std::string>(m_ses.m_settings.proxy_port));
m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup
, shared_from_this(), _1, _2, m_ses.m_settings.proxy_port, url));
, shared_from_this(), _1, _2, url));
}
}
void torrent::on_name_lookup(asio::error const& e, tcp::resolver::iterator host
, int port, std::string url) try
, std::string url) try
{
detail::session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
@ -1012,7 +1054,7 @@ namespace libtorrent
if (m_ses.m_abort) return;
tcp::endpoint a(host->endpoint().address(), port);
tcp::endpoint a(host->endpoint());
boost::shared_ptr<stream_socket> s(new stream_socket(m_ses.m_selector));
boost::intrusive_ptr<peer_connection> c(new web_peer_connection(

View File

@ -127,6 +127,7 @@ namespace libtorrent
torrent_info::torrent_info(const entry& torrent_file)
: m_creation_date(date(not_a_date_time))
, m_multifile(false)
, m_private(false)
, m_extra_info(entry::dictionary_t)
{
try
@ -269,6 +270,13 @@ namespace libtorrent
m_extra_info[i->first] = i->second;
}
if (info.find_key("private"))
{
// this key exists, don't care about its value, consider
// the torrent private
m_private = true;
}
#ifndef NDEBUG
std::vector<char> info_section_buf;
entry gen_info_section = create_info_metadata();
@ -319,10 +327,27 @@ namespace libtorrent
}
std::random_shuffle(start, stop);
}
else
else if (entry const* i = torrent_file.find_key("announce"))
{
m_urls.push_back(announce_entry(
torrent_file["announce"].string()));
m_urls.push_back(announce_entry(i->string()));
}
if (entry const* i = torrent_file.find_key("nodes"))
{
entry::list_type const& list = i->list();
for (entry::list_type::const_iterator i(list.begin())
, end(list.end()); i != end; ++i)
{
if (i->type() != entry::list_t) continue;
entry::list_type const& l = i->list();
entry::list_type::const_iterator iter = l.begin();
if (l.size() < 1) continue;
std::string const& hostname = iter->string();
++iter;
int port = 6881;
if (list.end() != iter) port = iter->integer();
m_nodes.push_back(std::make_pair(hostname, port));
}
}
// extract creation date
@ -518,14 +543,32 @@ namespace libtorrent
entry dict(entry::dictionary_t);
if (m_urls.empty() || m_files.empty())
if ((m_urls.empty() && m_nodes.empty()) || m_files.empty())
{
// TODO: throw something here
// throw
return entry();
}
dict["announce"] = m_urls.front().url;
if (m_private) dict["private"] = 1;
if (!m_urls.empty())
dict["announce"] = m_urls.front().url;
if (!m_nodes.empty())
{
entry& nodes = dict["nodes"];
nodes = entry(entry::list_t);
entry::list_type& nodes_list = nodes.list();
for (nodes_t::const_iterator i = m_nodes.begin()
, end(m_nodes.end()); i != end; ++i)
{
entry::list_type node;
node.push_back(entry(i->first));
node.push_back(entry(i->second));
nodes_list.push_back(entry(node));
}
}
if (m_urls.size() > 1)
{
@ -630,7 +673,12 @@ namespace libtorrent
else
return piece_length();
}
void torrent_info::add_node(std::pair<std::string, int> const& node)
{
m_nodes.push_back(node);
}
std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
, int size) const
{

View File

@ -559,4 +559,11 @@ namespace libtorrent
std::swap(m_connections, keep_connections);
}
bool tracker_manager::empty() const
{
mutex_t::scoped_lock l(m_mutex);
return m_connections.empty();
}
}

View File

@ -66,6 +66,8 @@ namespace libtorrent
{
INVARIANT_CHECK;
m_max_out_request_queue = ses.m_settings.urlseed_pipeline_size;
// since this is a web seed, change the timeout
// according to the settings.
set_timeout(ses.m_settings.urlseed_timeout);