first version with DHT support. Limited pipelining used by url-seeds. Fixed one configuration problem on FreeBSD.
This commit is contained in:
parent
f1548fd668
commit
5ef57265bf
19
ChangeLog
19
ChangeLog
@ -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
12
Jamfile
@ -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 =
|
||||
|
@ -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 \
|
||||
|
@ -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&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>
|
||||
|
@ -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
|
||||
|
318
docs/manual.html
318
docs/manual.html
@ -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<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);
|
||||
|
||||
};
|
||||
</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& 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);
|
||||
</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<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;
|
||||
@ -1123,6 +1195,9 @@ public:
|
||||
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;
|
||||
|
||||
@ -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<std::pair<std::string, int> > const& 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<std::string, int> const& 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: "chat"</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>
|
||||
|
236
docs/manual.rst
236
docs/manual.rst
@ -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
|
||||
------------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
86
include/libtorrent/kademlia/closest_nodes.hpp
Normal file
86
include/libtorrent/kademlia/closest_nodes.hpp
Normal 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
|
||||
|
129
include/libtorrent/kademlia/dht_tracker.hpp
Normal file
129
include/libtorrent/kademlia/dht_tracker.hpp
Normal 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
|
95
include/libtorrent/kademlia/find_data.hpp
Normal file
95
include/libtorrent/kademlia/find_data.hpp
Normal 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
|
||||
|
146
include/libtorrent/kademlia/logging.hpp
Normal file
146
include/libtorrent/kademlia/logging.hpp
Normal 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
|
||||
|
185
include/libtorrent/kademlia/node.hpp
Normal file
185
include/libtorrent/kademlia/node.hpp
Normal 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
|
||||
|
63
include/libtorrent/kademlia/node_entry.hpp
Normal file
63
include/libtorrent/kademlia/node_entry.hpp
Normal 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
|
||||
|
60
include/libtorrent/kademlia/node_id.hpp
Normal file
60
include/libtorrent/kademlia/node_id.hpp
Normal 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
|
||||
|
95
include/libtorrent/kademlia/packet_iterator.hpp
Normal file
95
include/libtorrent/kademlia/packet_iterator.hpp
Normal 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
|
||||
|
158
include/libtorrent/kademlia/refresh.hpp
Normal file
158
include/libtorrent/kademlia/refresh.hpp
Normal 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
|
||||
|
224
include/libtorrent/kademlia/routing_table.hpp
Normal file
224
include/libtorrent/kademlia/routing_table.hpp
Normal 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
|
||||
|
196
include/libtorrent/kademlia/rpc_manager.hpp
Normal file
196
include/libtorrent/kademlia/rpc_manager.hpp
Normal 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
|
||||
|
||||
|
149
include/libtorrent/kademlia/traversal_algorithm.hpp
Normal file
149
include/libtorrent/kademlia/traversal_algorithm.hpp
Normal 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
|
||||
|
@ -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; }
|
||||
|
72
include/libtorrent/random_sample.hpp
Normal file
72
include/libtorrent/random_sample.hpp
Normal 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
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
146
src/kademlia/closest_nodes.cpp
Normal file
146
src/kademlia/closest_nodes.cpp
Normal 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
|
||||
|
772
src/kademlia/dht_tracker.cpp
Normal file
772
src/kademlia/dht_tracker.cpp
Normal 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
156
src/kademlia/find_data.cpp
Normal 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
501
src/kademlia/node.cpp
Normal 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
97
src/kademlia/node_id.cpp
Normal 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
188
src/kademlia/refresh.cpp
Normal 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
|
||||
|
417
src/kademlia/routing_table.cpp
Normal file
417
src/kademlia/routing_table.cpp
Normal 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
|
||||
|
343
src/kademlia/rpc_manager.cpp
Normal file
343
src/kademlia/rpc_manager.cpp
Normal 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
|
||||
|
162
src/kademlia/traversal_algorithm.cpp
Normal file
162
src/kademlia/traversal_algorithm.cpp
Normal 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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user