forked from premiere/premiere-libtorrent
merged uTP branch into trunk (yay)
This commit is contained in:
parent
5a1669cf03
commit
e5f980d80d
|
@ -31,6 +31,7 @@ set(sources
|
|||
http_seed_connection
|
||||
instantiate_connection
|
||||
natpmp
|
||||
packet_buffer
|
||||
piece_picker
|
||||
policy
|
||||
puff
|
||||
|
@ -206,6 +207,7 @@ endif(MSVC)
|
|||
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
add_definitions(-DBOOST_DISABLE_EXCEPTION)
|
||||
add_definitions(-DBOOST_ASIO_ENABLE_CANCELIO)
|
||||
|
||||
if (tcmalloc)
|
||||
target_link_libraries(torrent-rasterbar tcmalloc)
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
* support trackerid tracker extension
|
||||
* graceful peer disconnect mode which finishes transactions before disconnecting peers
|
||||
* support chunked encoding for web seeds
|
||||
* uTP protocol support
|
||||
* resistance towards certain flood attacks
|
||||
* support chunked encoding for web seeds (only for BEP 19, web seeds)
|
||||
* optimized session startup time
|
||||
* support SSL for web seeds, through all proxies
|
||||
* support extending web seeds with custom authorization and extra headers
|
||||
|
|
10
Jamfile
10
Jamfile
|
@ -81,8 +81,9 @@ rule linking ( properties * )
|
|||
{
|
||||
result += <library>ws2_32
|
||||
<library>wsock32
|
||||
<library>iphlpapi
|
||||
<define>WIN32_LEAN_AND_MEAN
|
||||
<define>_WIN32_WINNT=0x0500
|
||||
<define>_WIN32_WINNT=0x0600
|
||||
<define>__USE_W32_SOCKETS
|
||||
<define>WIN32
|
||||
<define>_WIN32
|
||||
|
@ -353,6 +354,7 @@ lib GeoIP : : <name>GeoIP <link>shared ;
|
|||
# socket libraries on windows
|
||||
lib wsock32 : : <name>wsock32 <link>shared ;
|
||||
lib ws2_32 : : <name>ws2_32 <link>shared ;
|
||||
lib iphlpapi : : <name>iphlpapi <link>shared ;
|
||||
|
||||
SOURCES =
|
||||
alert
|
||||
|
@ -384,6 +386,7 @@ SOURCES =
|
|||
i2p_stream
|
||||
instantiate_connection
|
||||
natpmp
|
||||
packet_buffer
|
||||
piece_picker
|
||||
policy
|
||||
puff
|
||||
|
@ -403,8 +406,11 @@ SOURCES =
|
|||
http_tracker_connection
|
||||
udp_tracker_connection
|
||||
sha1
|
||||
timestamp_history
|
||||
udp_socket
|
||||
upnp
|
||||
utp_socket_manager
|
||||
utp_stream
|
||||
logger
|
||||
file_pool
|
||||
lsd
|
||||
|
@ -443,6 +449,8 @@ local usage-requirements =
|
|||
<variant>debug:<define>TORRENT_DEBUG
|
||||
<define>_FILE_OFFSET_BITS=64
|
||||
<define>BOOST_EXCEPTION_DISABLE
|
||||
# enable cancel support in asio
|
||||
<define>BOOST_ASIO_ENABLE_CANCELIO
|
||||
<conditional>@linking
|
||||
# these compiler settings just makes the compiler standard conforming
|
||||
<toolset>msvc:<cflags>/Zc:wchar_t
|
||||
|
|
|
@ -593,6 +593,9 @@ COMPILETIME_OPTIONS+="-DBOOST_ASIO_HASH_MAP_BUCKETS=1021 "
|
|||
AC_DEFINE([BOOST_EXCEPTION_DISABLE],[1],[Define to disable the boost.exception features.])
|
||||
COMPILETIME_OPTIONS+="-DBOOST_EXCEPTION_DISABLE "
|
||||
|
||||
AC_DEFINE([BOOST_ASIO_ENABLE_CANCELIO],[1],[Define to enable cancel support in asio on windows XP and older.])
|
||||
COMPILETIME_OPTIONS+="-DBOOST_ASIO_ENABLE_CANCELIO "
|
||||
|
||||
dnl Use possibly specific python install params
|
||||
AC_ARG_VAR([PYTHON_INSTALL_PARAMS], [Set specific install parameters for python bindings.])
|
||||
AS_IF([test "x$PYTHON_INSTALL_PARAMS" = "x"],
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -56,12 +56,13 @@
|
|||
<li><a class="reference internal" href="#high-performance-disk-subsystem" id="id9">high performance disk subsystem</a></li>
|
||||
<li><a class="reference internal" href="#network-buffers" id="id10">network buffers</a></li>
|
||||
<li><a class="reference internal" href="#piece-picker" id="id11">piece picker</a></li>
|
||||
<li><a class="reference internal" href="#merkle-hash-tree-torrents" id="id12">merkle hash tree torrents</a></li>
|
||||
<li><a class="reference internal" href="#customizable-file-storage" id="id13">customizable file storage</a></li>
|
||||
<li><a class="reference internal" href="#easy-to-use-api" id="id14">easy to use API</a></li>
|
||||
<li><a class="reference internal" href="#share-mode" id="id12">share mode</a></li>
|
||||
<li><a class="reference internal" href="#merkle-hash-tree-torrents" id="id13">merkle hash tree torrents</a></li>
|
||||
<li><a class="reference internal" href="#customizable-file-storage" id="id14">customizable file storage</a></li>
|
||||
<li><a class="reference internal" href="#easy-to-use-api" id="id15">easy to use API</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#portability" id="id15">portability</a></li>
|
||||
<li><a class="reference internal" href="#portability" id="id16">portability</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="introduction">
|
||||
|
@ -97,9 +98,12 @@ uTorrent interpretation).</li>
|
|||
<li>supports the <tt class="docutils literal"><span class="pre">compact=1</span></tt> tracker parameter.</li>
|
||||
<li>super seeding/initial seeding (<a class="reference external" href="http://bittorrent.org/beps/bep_0016.html">BEP 16</a>).</li>
|
||||
<li>private torrents (<a class="reference external" href="http://bittorrent.org/beps/bep_0027.html">BEP 27</a>).</li>
|
||||
<li>upload-only extension (<a class="reference external" href="http://bittorrent.org/beps/bep_0021.html">BEP 21</a>).</li>
|
||||
<li>support for IPv6, including <a class="reference external" href="http://bittorrent.org/beps/bep_0007.html">BEP 7</a> and <a class="reference external" href="http://bittorrent.org/beps/bep_0024.html">BEP 24</a>.</li>
|
||||
<li>support for merkle hash tree torrents. This makes the size of torrent files
|
||||
scale well with the size of the content.</li>
|
||||
<li>share-mode. This is a special mode torrents can be put in to optimize share
|
||||
ratio rather than downloading the torrent.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="disk-management">
|
||||
|
@ -124,6 +128,8 @@ piece's hash is verified the first time it is requested.</li>
|
|||
<div class="section" id="network">
|
||||
<h2>network</h2>
|
||||
<ul class="simple">
|
||||
<li>a high quality uTP implementation (<a href="#id17"><span class="problematic" id="id18">BEP29_</span></a>). A transport protocol with
|
||||
delay based congestion control. See separate <a class="reference external" href="utp.html">article</a>.</li>
|
||||
<li>adjusts the length of the request queue depending on download rate.</li>
|
||||
<li>serves multiple torrents on a single port and in a single thread</li>
|
||||
<li>piece picking on block-level (as opposed to piece-level).
|
||||
|
@ -253,6 +259,15 @@ makes slow peers pick blocks from the same piece, and fast peers pick from the s
|
|||
and hence decreasing the likelihood of slow peers blocking the completion of pieces.</p>
|
||||
<p>The piece picker can also be set to download pieces in sequential order.</p>
|
||||
</div>
|
||||
<div class="section" id="share-mode">
|
||||
<h2>share mode</h2>
|
||||
<p>The share mode feature in libtorrent is intended for users who are only interested in
|
||||
helping out swarms, not downloading the torrents.</p>
|
||||
<p>It works by predicting the demand for pieces, and only download pieces if there is enough
|
||||
demand. New pieces will only be downloaded once the share ratio has hit a certain target.</p>
|
||||
<p>This feature is especially useful when combined with RSS, so that a client can be set up
|
||||
to provide additional bandwidth to an entire feed.</p>
|
||||
</div>
|
||||
<div class="section" id="merkle-hash-tree-torrents">
|
||||
<h2>merkle hash tree torrents</h2>
|
||||
<p>Merkle hash tree torrents is an extension that lets a torrent file only contain the
|
||||
|
@ -363,6 +378,12 @@ epoll on linux and kqueue on MacOS X and BSD.</p>
|
|||
<li>GCC 2.95.4</li>
|
||||
<li>msvc6</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="system-messages section">
|
||||
<h1>Docutils System Messages</h1>
|
||||
<div class="system-message" id="id17">
|
||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">features.rst</tt>, line 82); <em><a href="#id18">backlink</a></em></p>
|
||||
Unknown target name: "bep29".</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
|
|
|
@ -52,6 +52,7 @@ extensions
|
|||
* share-mode. This is a special mode torrents can be put in to optimize share
|
||||
ratio rather than downloading the torrent.
|
||||
|
||||
.. _article: utp.html
|
||||
.. _extensions: manual.html#extensions
|
||||
.. _`http seeding`: manual.html#http-seeding
|
||||
|
||||
|
@ -78,6 +79,8 @@ disk management
|
|||
network
|
||||
-------
|
||||
|
||||
* a high quality uTP implementation (BEP29_). A transport protocol with
|
||||
delay based congestion control. See separate article_.
|
||||
* adjusts the length of the request queue depending on download rate.
|
||||
* serves multiple torrents on a single port and in a single thread
|
||||
* piece picking on block-level (as opposed to piece-level).
|
||||
|
@ -116,6 +119,7 @@ network
|
|||
.. _`BEP 21`: http://bittorrent.org/beps/bep_0021.html
|
||||
.. _`BEP 24`: http://bittorrent.org/beps/bep_0024.html
|
||||
.. _`BEP 27`: http://bittorrent.org/beps/bep_0027.html
|
||||
.. _`BEP 29`: http://bittorrent.org/beps/bep_0029.html
|
||||
.. _`extension protocol`: extension_protocol.html
|
||||
|
||||
highlighted features
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
<hr class="docutils" />
|
||||
<p>Extensions</p>
|
||||
<ul class="simple">
|
||||
<li><a class="reference external" href="utp.html">uTP</a></li>
|
||||
<li><a class="reference external" href="extension_protocol.html">extensions protocol</a></li>
|
||||
<li><a class="reference external" href="libtorrent_plugins.html">plugin interface</a></li>
|
||||
<li><a class="reference external" href="dht_extensions.html">DHT extensions</a></li>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
Extensions
|
||||
|
||||
* `uTP`_
|
||||
* `extensions protocol`_
|
||||
* `plugin interface`_
|
||||
* `DHT extensions`_
|
||||
|
@ -59,6 +60,7 @@ libtorrent
|
|||
.. _`running tests`: running_tests.html
|
||||
.. _`tuning`: tuning.html
|
||||
.. _screenshot: client_test.png
|
||||
.. _`uTP`: utp.html
|
||||
.. _`extensions protocol`: extension_protocol.html
|
||||
.. _`plugin interface`: libtorrent_plugins.html
|
||||
.. _`DHT extensions`: dht_extensions.html
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
<tbody valign="top">
|
||||
<tr><th class="docinfo-name">Author:</th>
|
||||
<td>Arvid Norberg, <a class="last reference external" href="mailto:arvid@rasterbar.com">arvid@rasterbar.com</a></td></tr>
|
||||
<tr><th class="docinfo-name">Version:</th>
|
||||
<td>0.16.0</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="contents topic" id="table-of-contents">
|
||||
|
@ -58,7 +60,7 @@
|
|||
<li><a class="reference internal" href="#set-creator" id="id14">set_creator()</a></li>
|
||||
<li><a class="reference internal" href="#set-hash" id="id15">set_hash()</a></li>
|
||||
<li><a class="reference internal" href="#set-file-hash" id="id16">set_file_hash()</a></li>
|
||||
<li><a class="reference internal" href="#add-url-seed" id="id17">add_url_seed()</a></li>
|
||||
<li><a class="reference internal" href="#add-url-seed-add-http-seed" id="id17">add_url_seed() add_http_seed()</a></li>
|
||||
<li><a class="reference internal" href="#add-node" id="id18">add_node()</a></li>
|
||||
<li><a class="reference internal" href="#add-tracker" id="id19">add_tracker()</a></li>
|
||||
<li><a class="reference internal" href="#set-priv-priv" id="id20">set_priv() priv()</a></li>
|
||||
|
@ -291,6 +293,7 @@ struct create_torrent
|
|||
void set_hash(int index, sha1_hash const& h);
|
||||
void set_file_hash(int index, sha1_hash const& h);
|
||||
void add_url_seed(std::string const& url);
|
||||
void add_http_seed(std::string const& url);
|
||||
void add_node(std::pair<std::string, int> const& node);
|
||||
void add_tracker(std::string const& url, int tier = 0);
|
||||
void set_priv(bool p);
|
||||
|
@ -433,11 +436,12 @@ void set_file_hash(int index, sha1_hash const& h);
|
|||
associated with this file (for multi-file torrents) or in the root info dictionary
|
||||
for single-file torrents.</p>
|
||||
</div>
|
||||
<div class="section" id="add-url-seed">
|
||||
<h2>add_url_seed()</h2>
|
||||
<div class="section" id="add-url-seed-add-http-seed">
|
||||
<h2>add_url_seed() add_http_seed()</h2>
|
||||
<blockquote>
|
||||
<pre class="literal-block">
|
||||
void add_url_seed(std::string const& url);
|
||||
void add_http_seed(std::string const& url);
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>This adds a url seed to the torrent. You can have any number of url seeds. For a
|
||||
|
@ -445,6 +449,7 @@ single file torrent, this should be an HTTP url, pointing to a file with identic
|
|||
content as the file of the torrent. For a multi-file torrent, it should point to
|
||||
a directory containing a directory with the same name as this torrent, and all the
|
||||
files of the torrent in it.</p>
|
||||
<p>The second function, <tt class="docutils literal"><span class="pre">add_http_seed()</span></tt> adds an HTTP seed instead.</p>
|
||||
</div>
|
||||
<div class="section" id="add-node">
|
||||
<h2>add_node()</h2>
|
||||
|
|
|
@ -3,6 +3,7 @@ creating torrents
|
|||
=================
|
||||
|
||||
:Author: Arvid Norberg, arvid@rasterbar.com
|
||||
:Version: 0.16.0
|
||||
|
||||
.. contents:: Table of contents
|
||||
:depth: 2
|
||||
|
|
|
@ -17,6 +17,7 @@ TARGETS = index \
|
|||
python_binding \
|
||||
projects \
|
||||
running_tests \
|
||||
utp \
|
||||
tuning
|
||||
|
||||
FIGURES = read_disk_buffers write_disk_buffers
|
||||
|
|
1063
docs/manual.html
1063
docs/manual.html
File diff suppressed because it is too large
Load Diff
133
docs/manual.rst
133
docs/manual.rst
|
@ -618,6 +618,15 @@ struct has the following members::
|
|||
int branch_factor;
|
||||
};
|
||||
|
||||
struct utp_status
|
||||
{
|
||||
int num_idle;
|
||||
int num_syn_sent;
|
||||
int num_connected;
|
||||
int num_fin_sent;
|
||||
int num_close_wait;
|
||||
};
|
||||
|
||||
struct session_status
|
||||
{
|
||||
bool has_incoming_connections;
|
||||
|
@ -663,6 +672,8 @@ struct has the following members::
|
|||
size_type dht_global_nodes;
|
||||
std::vector<dht_lookup> active_requests;
|
||||
int dht_total_allocations;
|
||||
|
||||
utp_status utp_stats;
|
||||
};
|
||||
|
||||
``has_incoming_connections`` is false as long as no incoming connections have been
|
||||
|
@ -730,6 +741,8 @@ network.
|
|||
particular DHT lookup. This represents roughly the amount of memory used
|
||||
by the DHT.
|
||||
|
||||
``utp_stats`` contains statistics on the uTP sockets.
|
||||
|
||||
get_cache_status()
|
||||
------------------
|
||||
|
||||
|
@ -1074,7 +1087,6 @@ struct has the following members::
|
|||
{
|
||||
int max_peers_reply;
|
||||
int search_branching;
|
||||
int service_port;
|
||||
int max_fail_count;
|
||||
};
|
||||
|
||||
|
@ -1085,19 +1097,18 @@ response to a ``get_peers`` message from another node.
|
|||
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. This will default
|
||||
to 0, which means the udp listen port will be the same as the tcp listen
|
||||
port. This is in general a good idea, since some NAT implementations
|
||||
reserves the udp port for any mapped tcp port, and vice versa. NAT-PMP
|
||||
guarantees this for example.
|
||||
|
||||
``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.
|
||||
|
||||
``is_dht_running`` returns true if the DHT support has been started and false
|
||||
The ``dht_settings`` struct used to contain a ``service_port`` member to control
|
||||
which port the DHT would listen on and send messages from. This field is deprecated
|
||||
and ignored. libtorrent always tries to open the UDP socket on the same port
|
||||
as the TCP socket.
|
||||
|
||||
``is_dht_running()`` returns true if the DHT support has been started and false
|
||||
otherwise.
|
||||
|
||||
|
||||
|
@ -3387,6 +3398,7 @@ It contains the following fields::
|
|||
optimistic_unchoke = 0x800,
|
||||
snubbed = 0x1000,
|
||||
upload_only = 0x2000,
|
||||
holepunched = 0x4000,
|
||||
rc4_encrypted = 0x100000,
|
||||
plaintext_encrypted = 0x200000
|
||||
};
|
||||
|
@ -3535,6 +3547,11 @@ any combination of the enums above. The following table describes each flag:
|
|||
| | will not downloading anything more, regardless of |
|
||||
| | which pieces we have. |
|
||||
+-------------------------+-------------------------------------------------------+
|
||||
| ``holepunched`` | This flag is set if the peer was in holepunch mode |
|
||||
| | when the connection succeeded. This typically only |
|
||||
| | happens if both peers are behind a NAT and the peers |
|
||||
| | connect via the NAT holepunch mechanism. |
|
||||
+-------------------------+-------------------------------------------------------+
|
||||
|
||||
__ extension_protocol.html
|
||||
|
||||
|
@ -3673,8 +3690,19 @@ that may give away something about which software is running in the other end.
|
|||
In the case of a web seed, the server type and version will be a part of this
|
||||
string.
|
||||
|
||||
``connection_type`` can currently be one of ``standard_bittorrent`` or
|
||||
``web_seed``. These are currently the only implemented protocols.
|
||||
``connection_type`` can currently be one of:
|
||||
|
||||
+---------------------------------------+-------------------------------------------------------+
|
||||
| type | meaning |
|
||||
+=======================================+=======================================================+
|
||||
| ``peer_info::standard_bittorrent`` | Regular bittorrent connection over TCP |
|
||||
+---------------------------------------+-------------------------------------------------------+
|
||||
| ``peer_info::bittorrent_utp`` | Bittorrent connection over uTP |
|
||||
+---------------------------------------+-------------------------------------------------------+
|
||||
| ``peer_info::web_sesed`` | HTTP connection using the `BEP 19`_ protocol |
|
||||
+---------------------------------------+-------------------------------------------------------+
|
||||
| ``peer_info::http_seed`` | HTTP connection using the `BEP 17`_ protocol |
|
||||
+---------------------------------------+-------------------------------------------------------+
|
||||
|
||||
``remote_dl_rate`` is an estimate of the rate this peer is downloading at, in
|
||||
bytes per second.
|
||||
|
@ -3927,10 +3955,17 @@ session_settings
|
|||
int default_peer_upload_rate;
|
||||
int default_peer_download_rate;
|
||||
bool broadcast_lsd;
|
||||
|
||||
bool enable_outgoing_utp;
|
||||
bool enable_incoming_utp;
|
||||
bool enable_outgoing_tcp;
|
||||
bool enable_incoming_tcp;
|
||||
int max_pex_peers;
|
||||
bool ignore_resume_timestamps;
|
||||
bool anonymous_mode;
|
||||
int tick_interval;
|
||||
int share_mode_target;
|
||||
|
||||
int upload_rate_limit;
|
||||
int download_rate_limit;
|
||||
int local_upload_rate_limit;
|
||||
|
@ -3939,6 +3974,24 @@ session_settings
|
|||
int half_open_limit;
|
||||
int connections_limit;
|
||||
|
||||
int utp_target_delay;
|
||||
int utp_gain_factor;
|
||||
int utp_min_timeout;
|
||||
int utp_syn_resends;
|
||||
int utp_num_resends;
|
||||
int utp_connect_timeout;
|
||||
int utp_delayed_ack;
|
||||
bool utp_dynamic_sock_buf;
|
||||
|
||||
enum bandwidth_mixed_algo_t
|
||||
{
|
||||
prefer_tcp = 0,
|
||||
peer_proportional = 1
|
||||
|
||||
};
|
||||
int mixed_mode_algorithm;
|
||||
bool rate_limit_utp;
|
||||
|
||||
int listen_queue_size;
|
||||
};
|
||||
|
||||
|
@ -4604,6 +4657,11 @@ if ``broadcast_lsd`` is set to true, the local peer discovery
|
|||
broadcast its messages. This can be useful when running on networks
|
||||
that don't support multicast. It's off by default since it's inefficient.
|
||||
|
||||
``enable_outgoing_utp``, ``enable_incoming_utp``, ``enable_outgoing_tcp``,
|
||||
``enable_incoming_tcp`` all determines if libtorrent should attempt to make
|
||||
outgoing connections of the specific type, or allow incoming connection. By
|
||||
default all of them are enabled.
|
||||
|
||||
``ignore_resume_timestamps`` determines if the storage, when loading
|
||||
resume data files, should verify that the file modification time
|
||||
with the timestamps in the resume data. This defaults to false, which
|
||||
|
@ -4673,6 +4731,58 @@ opened. The number of connections is set to a hard minimum of at least two per
|
|||
torrent, so if you set a too low connections limit, and open too many torrents,
|
||||
the limit will not be met.
|
||||
|
||||
``utp_target_delay`` is the target delay for uTP sockets in milliseconds. A high
|
||||
value will make uTP connections more aggressive and cause longer queues in the upload
|
||||
bottleneck. It cannot be too low, since the noise in the measurements would cause
|
||||
it to send too slow. The default is 50 milliseconds.
|
||||
|
||||
``utp_gain_factor`` is the number of bytes the uTP congestion window can increase
|
||||
at the most in one RTT. This defaults to 300 bytes. If this is set too high,
|
||||
the congestion controller reacts too hard to noise and will not be stable, if it's
|
||||
set too low, it will react slow to congestion and not back off as fast.
|
||||
|
||||
``utp_min_timeout`` is the shortest allowed uTP socket timeout, specified in milliseconds.
|
||||
This defaults to 500 milliseconds. The timeout depends on the RTT of the connection, but
|
||||
is never smaller than this value. A connection times out when every packet in a window
|
||||
is lost, or when a packet is lost twice in a row (i.e. the resent packet is lost as well).
|
||||
|
||||
The shorter the timeout is, the faster the connection will recover from this situation,
|
||||
assuming the RTT is low enough.
|
||||
|
||||
``utp_syn_resends`` is the number of SYN packets that are sent (and timed out) before
|
||||
giving up and closing the socket.
|
||||
|
||||
``utp_num_resends`` is the number of times a packet is sent (and lossed or timed out)
|
||||
before giving up and closing the connection.
|
||||
|
||||
``utp_connect_timeout`` is the number of milliseconds of timeout for the initial SYN
|
||||
packet for uTP connections. For each timed out packet (in a row), the timeout is doubled.
|
||||
|
||||
``utp_delayed_ack`` is the number of milliseconds to delay ACKs the most. Delaying ACKs
|
||||
significantly helps reducing the amount of protocol overhead in the reverse direction
|
||||
from downloads. It defaults to 100 milliseconds. If set to 0, delayed ACKs are disabled
|
||||
and every incoming payload packet is ACKed. The granularity of this timer is capped by
|
||||
the tick interval (as specified by ``tick_interval``).
|
||||
|
||||
``utp_dynamic_sock_buf`` controls if the uTP socket manager is allowed to increase
|
||||
the socket buffer if a network interface with a large MTU is used (such as loopback
|
||||
or ethernet jumbo frames). This defaults to true and might improve uTP throughput.
|
||||
For RAM constrained systems, disabling this typically saves around 30kB in user space
|
||||
and probably around 400kB in kernel socket buffers (it adjusts the send and receive
|
||||
buffer size on the kernel socket, both for IPv4 and IPv6).
|
||||
|
||||
The ``mixed_mode_algorithm`` determines how to treat TCP connections when there are
|
||||
uTP connections. Since uTP is designed to yield to TCP, there's an inherent problem
|
||||
when using swarms that have both TCP and uTP connections. If nothing is done, uTP
|
||||
connections would often be starved out for bandwidth by the TCP connections. This mode
|
||||
is ``prefer_tcp``. The ``peer_proportional`` mode simply looks at the current throughput
|
||||
and rate limits all TCP connections to their proportional share based on how many of
|
||||
the connections are TCP. This works best if uTP connections are not rate limited by
|
||||
the global rate limiter (which they aren't by default).
|
||||
|
||||
``rate_limit_utp`` determines if uTP connections should be throttled by the global rate
|
||||
limiter or not. By default they are not, since uTP manages its own rate.
|
||||
|
||||
``listen_queue_size`` is the value passed in to listen() for the listen socket.
|
||||
It is the number of outstanding incoming connections to queue up while we're not
|
||||
actively waiting for a connection to be accepted. The default is 5 which should
|
||||
|
@ -6797,6 +6907,9 @@ code symbol description
|
|||
106 invalid_pex_message The peer sent an invalid peer exchange message
|
||||
------ ----------------------------------------- -----------------------------------------------------------------
|
||||
107 invalid_lt_tracker_message The peer sent an invalid tracker exchange message
|
||||
------ ----------------------------------------- -----------------------------------------------------------------
|
||||
108 too_frequent_pex The peer sent an pex messages too often. This is a possible
|
||||
attempt of and attack
|
||||
====== ========================================= =================================================================
|
||||
|
||||
NAT-PMP errors:
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
|
@ -203,6 +203,16 @@ of C++ exceptions. By disabling exceptions (-fno-exceptions on GCC), you can
|
|||
reduce the executable size with up to 45%. In order to build without exception
|
||||
support, you need to patch parts of boost.</p>
|
||||
<p>Also make sure to optimize for size when compiling.</p>
|
||||
<p>Another way of reducing the executable size is to disable code that isn't used.
|
||||
There are a number of <tt class="docutils literal"><span class="pre">TORRENT_*</span></tt> macros that control which features are included
|
||||
in libtorrent. If these macros are used to strip down libtorrent, make sure the same
|
||||
macros are defined when building libtorrent as when linking against it. If these
|
||||
are different the structures will look different from the libtorrent side and from
|
||||
the client side and memory corruption will follow.</p>
|
||||
<p>One, probably, safe macro to define is <tt class="docutils literal"><span class="pre">TORRENT_NO_DEPRECATE</span></tt> which removes all
|
||||
deprecated functions and struct members. As long as no deprecated functions are
|
||||
relied upon, this should be a simple way to eliminate a little bit of code.</p>
|
||||
<p>For all available options, see the <a class="reference external" href="building.html">building libtorrent</a> secion.</p>
|
||||
</div>
|
||||
<div class="section" id="reduce-statistics">
|
||||
<h2>reduce statistics</h2>
|
||||
|
|
|
@ -0,0 +1,342 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils 0.5: http://docutils.sourceforge.net/" />
|
||||
<title>libtorrent manual</title>
|
||||
<meta name="author" content="Arvid Norberg, arvid@rasterbar.com" />
|
||||
<link rel="stylesheet" type="text/css" href="../../css/base.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../../css/rst.css" />
|
||||
<link rel="stylesheet" href="style.css" type="text/css" />
|
||||
<style type="text/css">
|
||||
/* Hides from IE-mac \*/
|
||||
* html pre { height: 1%; }
|
||||
/* End hide from IE-mac */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="libtorrent-manual">
|
||||
<div id="container">
|
||||
<div id="headerNav">
|
||||
<ul>
|
||||
<li class="first"><a href="/">Home</a></li>
|
||||
<li><a href="../../products.html">Products</a></li>
|
||||
<li><a href="../../contact.html">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="header">
|
||||
<h1><span>Rasterbar Software</span></h1>
|
||||
<h2><span>Software developement and consulting</span></h2>
|
||||
</div>
|
||||
<div id="main">
|
||||
<h1 class="title">libtorrent manual</h1>
|
||||
<table class="docinfo" frame="void" rules="none">
|
||||
<col class="docinfo-name" />
|
||||
<col class="docinfo-content" />
|
||||
<tbody valign="top">
|
||||
<tr><th class="docinfo-name">Author:</th>
|
||||
<td>Arvid Norberg, <a class="last reference external" href="mailto:arvid@rasterbar.com">arvid@rasterbar.com</a></td></tr>
|
||||
<tr><th class="docinfo-name">Version:</th>
|
||||
<td>0.16.0</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="contents topic" id="table-of-contents">
|
||||
<p class="topic-title first">Table of contents</p>
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#utp" id="id1">uTP</a><ul>
|
||||
<li><a class="reference internal" href="#rationale" id="id2">rationale</a></li>
|
||||
<li><a class="reference internal" href="#tcp" id="id3">TCP</a></li>
|
||||
<li><a class="reference internal" href="#ledbat-congestion-controller" id="id4">LEDBAT congestion controller</a></li>
|
||||
<li><a class="reference internal" href="#one-way-delays" id="id5">one way delays</a></li>
|
||||
<li><a class="reference internal" href="#path-mtu-discovery" id="id6">Path MTU discovery</a></li>
|
||||
<li><a class="reference internal" href="#clock-drift" id="id7">clock drift</a></li>
|
||||
<li><a class="reference internal" href="#features" id="id8">features</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="utp">
|
||||
<h1>uTP</h1>
|
||||
<p>uTP (uTorrent transport protocol) is a transport protocol which uses one-way
|
||||
delay measurements for its congestion controller. This article is about uTP
|
||||
in general and specifically about libtorrent's implementation of it.</p>
|
||||
<div class="section" id="rationale">
|
||||
<h2>rationale</h2>
|
||||
<p>One of the most common problems users are experiencing using bittorrent is
|
||||
that their internet "stops working". This can be caused by a number of things,
|
||||
for example:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>a home router that crashes or slows down when its NAT pin-hole
|
||||
table overflows, triggered by DHT or simply many TCP connections.</li>
|
||||
<li>a home router that crashes or slows down by UDP traffic (caused by
|
||||
the DHT)</li>
|
||||
<li>a home DSL or cable modem having its send buffer filled up by outgoing
|
||||
data, and the buffer fits seconds worth of bytes. This adds seconds
|
||||
of delay on interactive traffic. For a web site that needs 10 round
|
||||
trips to load this may mean 10s of seconds of delay to load compared
|
||||
to without bittorrent. Skype or other delay sensitive applications
|
||||
would be affected even more.</li>
|
||||
</ol>
|
||||
<p>This document will cover (3).</p>
|
||||
<p>Typically this is solved by asking the user to enter a number of bytes
|
||||
that the client is allowed to send per second (i.e. setting an upload
|
||||
rate limit). The common recommendation is to set this limit to 80% of the
|
||||
uplink's capacity. This is to leave some headroom for things like TCP
|
||||
ACKs as well as the user's interactive use of the connection such as
|
||||
browsing the web or checking email.</p>
|
||||
<p>There are two major drawbacks with this technique:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>The user needs to actively make this setting (very few protocols
|
||||
require the user to provide this sort of information). This also
|
||||
means the user needs to figure out what its up-link capacity is.
|
||||
This is unfortunately a number that many ISPs are not advertizing
|
||||
(because it's often much lower than the download capacity) which
|
||||
might make it hard to find.</li>
|
||||
<li>The 20% headroom is wasted most of the time. Whenever the user
|
||||
is not using the internet connection for anything, those extra 20%
|
||||
could have been used by bittorrent to upload, but they're already
|
||||
allocated for interactive traffic. On top of that, 20% of the up-link
|
||||
is often not enough to give a good and responsive browsing experience.</li>
|
||||
</ol>
|
||||
<p>The ideal bandwidth allocation would be to use 100% for bittorrent when
|
||||
there is no interactive cross traffic, and 100% for interactive traffic
|
||||
whenever there is any. This would not waste any bandwidth while the user
|
||||
is idling, and it would make for a much better experience when the user
|
||||
is using the internet connection for other things.</p>
|
||||
<p>This is what uTP does.</p>
|
||||
</div>
|
||||
<div class="section" id="tcp">
|
||||
<h2>TCP</h2>
|
||||
<p>The reason TCP will fill the send buffer, and cause the delay on all traffic,
|
||||
is because its congestion control is <em>only</em> based on packet loss (and timeout).</p>
|
||||
<p>Since the modem is buffering, packets won't get dropped until the entire queue
|
||||
is full, and no more packets will fit. The packets will be dropped, TCP will
|
||||
detect this within an RTT or so. When TCP notices a packet loss, it will slow
|
||||
down its send rate and the queue will start to drain again. However, TCP will
|
||||
immediately start to ramp up its send rate again until the buffer is full and
|
||||
it detects packet loss again.</p>
|
||||
<p>TCP is designed to fully utilize the link capacity, without causing congestion.
|
||||
Whenever it sense congestion (through packet loss) it backs off. TCP is not
|
||||
designed to keep delays low. When you get the first packet loss (assuming the
|
||||
kind of queue described above, tail-queue) it is already too late. Your queue
|
||||
is full and you have the maximum amount of delay your modem can provide.</p>
|
||||
<p>TCP controls its send rate by limiting the number of bytes in-flight at any
|
||||
given time. This limit is called congestion window (<em>cwnd</em> for short). During
|
||||
steady state, the congestion window is constantly increasing linearly. Each
|
||||
packet that is successfully transferred will increase cwnd.</p>
|
||||
<pre class="literal-block">
|
||||
cwnd
|
||||
send_rate = ----
|
||||
RTT
|
||||
</pre>
|
||||
<p>Send rate is proportional to cwnd divided by RTT. A smaller cwnd will cause
|
||||
the send rate to be lower and a larger cwnd will cause the send rate to be
|
||||
higher.</p>
|
||||
<p>Using a congestion window instead of controlling the rate directly is simple
|
||||
because it also introduces an upper bound for memory usage for packets that
|
||||
haven't been ACKed yet and needs to be kept around.</p>
|
||||
<p>The behavior of TCP, where it bumps up against the ceiling, backs off and then
|
||||
starts increasing again until it hits the ceiling again, forms a saw tooth shape.
|
||||
If the modem wouldn't have any send buffer at all, a single TCP stream would
|
||||
not be able to fully utilize the link because of this behavior, since it would
|
||||
only fully utilize the link right before the packet loss and the back-off.</p>
|
||||
</div>
|
||||
<div class="section" id="ledbat-congestion-controller">
|
||||
<h2>LEDBAT congestion controller</h2>
|
||||
<p>The congestion controller in uTP is called <a class="reference external" href="https://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/">LEDBAT</a>, which also is an IETF working
|
||||
group attempting to standardize it. The congestion controller, on top of reacting
|
||||
to packet loss the same way TCP does, also reacts to changes in delays.</p>
|
||||
<p>For any uTP (or <a class="reference external" href="https://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/">LEDBAT</a>) implementation, there is a target delay. This is the
|
||||
amount of delay that is acceptable, and is in fact targeted for the connection.
|
||||
The target delay is defined to 25 ms in <a class="reference external" href="https://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/">LEDBAT</a>, uTorrent uses 100 ms and
|
||||
libtorrent uses 75 ms. Whenever a delay measurement is lower than the target,
|
||||
cwnd is increased proportional to (target_delay - delay). Whenever the measurement
|
||||
is higher than the target, cwnd is decreased proportional to (delay - target_delay).</p>
|
||||
<p>It can simply be expressed as:</p>
|
||||
<pre class="literal-block">
|
||||
cwnd += gain * (target_delay - delay)
|
||||
</pre>
|
||||
<a class="reference external image-reference" href="cwnd.png"><img align="right" alt="cwnd_thumb.png" class="align-right" src="cwnd_thumb.png" /></a>
|
||||
<p>Similarly to TCP, this is scaled so that the increase is evened out over one RTT.</p>
|
||||
<p>The linear controller will adjust the cwnd more for delays that are far off the
|
||||
target, and less for delays that are close to the target. This makes it converge
|
||||
at the target delay. Although, due to noise there is almost always some amount of
|
||||
oscillation. This oscillation is typically smaller than the saw tooth TCP forms.</p>
|
||||
<p>The figure to the right shows how (TCP) cross traffic causese uTP to essentially
|
||||
entirely stop sending anything. Its delay measurements are mostly well above the target
|
||||
during this time. The cross traffic is only a single TCP stream in this test.</p>
|
||||
<p>As soon as the cross traffic ceases, uTP will pick up its original send rate within
|
||||
a second.</p>
|
||||
<p>Since uTP constantly measures the delay, with every single packet, the reaction time
|
||||
to cross traffic causing delays is a single RTT (typically a fraction of a second).</p>
|
||||
</div>
|
||||
<div class="section" id="one-way-delays">
|
||||
<h2>one way delays</h2>
|
||||
<p>uTP measures the delay imposed on packets being sent to the other end
|
||||
of the connection. This measurement only includes buffering delay along
|
||||
the link, not propagation delay (the speed of light times distance) nor
|
||||
the routing delay (the time routers spend figuring out where to forward
|
||||
the packet). It does this by always comparing all measurements to a
|
||||
baseline measurement, to cancel out any fixed delay. By focusing on the
|
||||
variable delay along a link, it will specifically detect points where
|
||||
there might be congestion, since those points will have buffers.</p>
|
||||
<a class="reference external image-reference" href="delays.png"><img align="right" alt="delays_thumb.png" class="align-right" src="delays_thumb.png" /></a>
|
||||
<p>Delay on the return link is explicitly not included in the delay measurement.
|
||||
This is because in a peer-to-peer application, the other end is likely to also
|
||||
be connected via a modem, with the same send buffer restrictions as we assume
|
||||
for the sending side. The other end having its send queue full is not an indication
|
||||
of congestion on the path going the other way.</p>
|
||||
<p>In order to measure one way delays for packets, we cannot rely on clocks being
|
||||
synchronized, especially not at the microsecond level. Instead, the actual time
|
||||
it takes for a packet to arrive at the destination is not measured, only the changes
|
||||
in the transit time is measured.</p>
|
||||
<p>Each packet that is sent includes a time stamp of the current time, in microseconds,
|
||||
of the sending machine. The receiving machine calculates the difference between its
|
||||
own timestamp and the one in the packet and sends this back in the ACK. This difference,
|
||||
since it is in microseconds, will essentially be a random 32 bit number. However,
|
||||
the difference will stay somewhat similar over time. Any changes in this difference
|
||||
indicates that packets are either going through faster or slower.</p>
|
||||
<p>In order to measure the one-way buffering delay, a base delay is established. The
|
||||
base delay is the lowest ever seen value of the time stamp difference. Each delay
|
||||
sample we receive back, is compared against the base delay and the delay is the
|
||||
difference.</p>
|
||||
<p>This is the delay that's fed into the congestion controller.</p>
|
||||
<p>A histogram of typical delay measurements is shown to the right. This is from
|
||||
a transfer between a cable modem connection and a DSL connection.</p>
|
||||
<p>The details of the delay measurements are slightly more complicated since the
|
||||
values needs to be able to wrap (cross the 2^32 boundry and start over at 0).</p>
|
||||
</div>
|
||||
<div class="section" id="path-mtu-discovery">
|
||||
<h2>Path MTU discovery</h2>
|
||||
<p>MTU is short for <em>Maximum Transfer Unit</em> and describes the largest packet size that
|
||||
can be sent over a link. Any datagrams which size exceeds this limit will either
|
||||
be <em>fragmented</em> or dropped. A fragmented datagram means that the payload is split up
|
||||
in multiple packets, each with its own individual packet header.</p>
|
||||
<p>There are several reasons to avoid sending datagrams that get fragmented:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>A fragmented datagram is more likely to be lost. If any fragment is lost,
|
||||
the whole datagram is dropped.</li>
|
||||
<li>Bandwidth is likely to be wasted. If the datagram size is not divisible
|
||||
by the MTU the last packet will not contain as much payload as it could, and the
|
||||
payload over protocol header ratio decreases.</li>
|
||||
<li>It's expensive to fragment datagrams. Few routers are optimized to handle large
|
||||
numbers of fragmented packets. Datagrams that have to fragment are likely to
|
||||
be delayed significantly, and contribute to more CPU being used on routers.
|
||||
Typically fragmentation (and other advanced IP features) are implemented in
|
||||
software (slow) and not hardware (fast).</li>
|
||||
</ol>
|
||||
<p>The path MTU is the lowest MTU of any link along a path from two endpoints on the
|
||||
internet. The MTU bottleneck isn't necessarily at one of the endpoints, but can
|
||||
be anywhere in between.</p>
|
||||
<p>The most common MTU is 1500 bytes, which is the largest packet size for ethernet
|
||||
networks. Many home DSL connections, however, tunnel IP through PPPoE (Point to
|
||||
Point Protocol over Ethernet. Yes, that is the old dial-up modem protocol). This
|
||||
protocol uses up 8 bytes per packet for its own header.</p>
|
||||
<p>If the user happens to be on an internet connection over a VPN, it will add another
|
||||
layer, with its own packet headers.</p>
|
||||
<p>In short; if you would pick the largest possible packet size on an ethernet network,
|
||||
1472, and stick with it, you would be quite likely to generate fragments for a lot
|
||||
of connections. The fragments that will be created will be very small and especially
|
||||
inflate the overhead waste.</p>
|
||||
<p>The other approach of picking a very conservative packet size, that would be very
|
||||
unlikely to get fragmented has the following drawbacks:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>People on good, normal, networks will be penalized with a small packet size.
|
||||
Both in terms of router load but also bandwidth waste.</li>
|
||||
<li>Software routers are typically not limited by the number of bytes they can route,
|
||||
but the number of packets. Small packets means more of them, and more load on
|
||||
software routers.</li>
|
||||
</ol>
|
||||
<p>The solution to the problem of finding the optimal packet size, is to dynamically
|
||||
adjust the packet size and search for the largest size that can make it through
|
||||
without being fragmented along the path.</p>
|
||||
<p>To help do this, you can set the DF bit (Don't Fragment) in your Datagrams. This
|
||||
asks routers that otherwise would fragment packets to instead drop them, and send
|
||||
back an ICMP message reporting the MTU of the link the packet couldn't fit. With
|
||||
this message, it's very simple to discover the path MTU. You simply mark your packets
|
||||
not to be fragmented, and change your packet size whenever you receive the ICMP
|
||||
packet-too-big message.</p>
|
||||
<p>Unfortunately it's not quite that simple. There are a significant number of firewalls
|
||||
in the wild blocking all ICMP messages. This means we can't rely on them, we also have
|
||||
to guess that a packet was dropped because of its size. This is done by only marking
|
||||
certain packets with DF, and if all other packets go through, except for the MTU probes,
|
||||
we know that we need to lower our packet sizes.</p>
|
||||
<p>If we set up bounds for the path MTU (say the minimum internet MTU, 576 and ethernet's 1500),
|
||||
we can do a binary search for the MTU. This would let us find it in just a few round-trips.</p>
|
||||
<p>On top of this, libtorrent has an optimization where it figures out which interface a
|
||||
uTP connection will be sent over, and initialize the MTU ceiling to that interface's MTU.
|
||||
This means that a VPN tunnel would advertize its MTU as lower, and the uTP connection would
|
||||
immediately know to send smaller packets, no search required. It also has the side-effect
|
||||
of being able to use much larger packet sizes for non-ethernet interfaces or ethernet links
|
||||
with jumbo frames.</p>
|
||||
</div>
|
||||
<div class="section" id="clock-drift">
|
||||
<h2>clock drift</h2>
|
||||
<a class="reference external image-reference" href="our_delay_base.png"><img align="right" alt="our_delay_base_thumb.png" class="align-right" src="our_delay_base_thumb.png" /></a>
|
||||
<p>Clock drift is clocks progressing at different rates. It's different from clock
|
||||
skew which means clocks set to different values (but which may progress at the same
|
||||
rate).</p>
|
||||
<p>Any clock drift between the two machines involved in a uTP transfer will result
|
||||
in systematically inflated or deflated delay measurements.</p>
|
||||
<p>This can be solved by letting the base delay be the lowest seen sample in the last
|
||||
<em>n</em> minutes. This is a trade-off between seeing a single packet go straight through
|
||||
the queue, with no delay, and the amount of clock drift one can assume on normal computers.</p>
|
||||
<p>It turns out that it's fairly safe to assume that one of your packets will in fact go
|
||||
straight through without any significant delay, once every 20 minutes or so. However,
|
||||
the clock drift between normal computers can be as much as 17 ms in 10 minutes. 17 ms
|
||||
is quite significant, especially if your target delay is 25 ms (as in the <a class="reference external" href="https://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/">LEDBAT</a> spec).</p>
|
||||
<p>Clocks progresses at different rates depending on temperature. This means computers
|
||||
running hot are likely to have a clock drift compared to computers running cool.</p>
|
||||
<p>So, by updating the delay base periodically based on the lowest seen sample, you'll either
|
||||
end up changing it upwards (artificaially making the delay samples appear small) without
|
||||
the congestion or delay actually having changed, or you'll end up with a significant clock
|
||||
drift and have artificially low samples because of that.</p>
|
||||
<p>The solution to this problem is based on the fact that the clock drift is only a problem
|
||||
for one of the sides of the connection. Only when your delay measurements keep increasing
|
||||
is it a problem. If your delay measurements keep decreasing, the samples will simply push
|
||||
down the delay base along with it. With this in mind, we can simply keep track of the
|
||||
other end's delay measurements as well, applying the same logic to it. Whenever the
|
||||
other end's base delay is adjusted downwards, we adjust our base delay upwards by the same
|
||||
amount.</p>
|
||||
<p>This will accurately keep the base delay updated with the clock drift and improve
|
||||
the delay measurements. The figure on the right shows the absolute timestamp differences
|
||||
along with the base delay. The slope of the measurements is caused by clock drift.</p>
|
||||
<p>For more information on the clock drift compensation, see the slides from BitTorrent's
|
||||
presentation at <a class="reference external" href="http://www.usenix.org/event/iptps10/tech/slides/cohen.pdf">IPTPS10</a>.</p>
|
||||
</div>
|
||||
<div class="section" id="features">
|
||||
<h2>features</h2>
|
||||
<p>libtorrent's uTP implementation includes the following features:</p>
|
||||
<ul class="simple">
|
||||
<li>Path MTU discovery, including jumbo frames and detecting restricted
|
||||
MTU tunnels. Binary search packet sizes to find the largest non-fragmented.</li>
|
||||
<li>Selective ACK. The ability to acknowledge individual packets in the
|
||||
event of packet loss</li>
|
||||
<li>Fast resend. The first time a packet is lost, it's resent immediately.
|
||||
Triggered by duplicate ACKs.</li>
|
||||
<li>Nagle's algorithm. Minimize protocol overhead by attempting to lump
|
||||
full packets of payload together before sending a packet.</li>
|
||||
<li>Delayed ACKs to minimize protocol overhead.</li>
|
||||
<li>Microsecond resolution timestamps.</li>
|
||||
<li>Advertised receive window, to support download rate limiting.</li>
|
||||
<li>Correct handling of wrapping sequence numbers.</li>
|
||||
<li>Easy configuration of target-delay, gain-factor, timeouts, delayed-ack
|
||||
and socket buffers.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="footer">
|
||||
<span>Copyright © 2005 Rasterbar Software.</span>
|
||||
</div>
|
||||
</div>
|
||||
<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
_uacct = "UA-1599045-1";
|
||||
urchinTracker();
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,347 @@
|
|||
=================
|
||||
libtorrent manual
|
||||
=================
|
||||
|
||||
:Author: Arvid Norberg, arvid@rasterbar.com
|
||||
:Version: 0.16.0
|
||||
|
||||
.. contents:: Table of contents
|
||||
:depth: 2
|
||||
:backlinks: none
|
||||
|
||||
uTP
|
||||
===
|
||||
|
||||
uTP (uTorrent transport protocol) is a transport protocol which uses one-way
|
||||
delay measurements for its congestion controller. This article is about uTP
|
||||
in general and specifically about libtorrent's implementation of it.
|
||||
|
||||
rationale
|
||||
---------
|
||||
|
||||
One of the most common problems users are experiencing using bittorrent is
|
||||
that their internet "stops working". This can be caused by a number of things,
|
||||
for example:
|
||||
|
||||
1. a home router that crashes or slows down when its NAT pin-hole
|
||||
table overflows, triggered by DHT or simply many TCP connections.
|
||||
|
||||
2. a home router that crashes or slows down by UDP traffic (caused by
|
||||
the DHT)
|
||||
|
||||
3. a home DSL or cable modem having its send buffer filled up by outgoing
|
||||
data, and the buffer fits seconds worth of bytes. This adds seconds
|
||||
of delay on interactive traffic. For a web site that needs 10 round
|
||||
trips to load this may mean 10s of seconds of delay to load compared
|
||||
to without bittorrent. Skype or other delay sensitive applications
|
||||
would be affected even more.
|
||||
|
||||
This document will cover (3).
|
||||
|
||||
Typically this is solved by asking the user to enter a number of bytes
|
||||
that the client is allowed to send per second (i.e. setting an upload
|
||||
rate limit). The common recommendation is to set this limit to 80% of the
|
||||
uplink's capacity. This is to leave some headroom for things like TCP
|
||||
ACKs as well as the user's interactive use of the connection such as
|
||||
browsing the web or checking email.
|
||||
|
||||
There are two major drawbacks with this technique:
|
||||
|
||||
1. The user needs to actively make this setting (very few protocols
|
||||
require the user to provide this sort of information). This also
|
||||
means the user needs to figure out what its up-link capacity is.
|
||||
This is unfortunately a number that many ISPs are not advertizing
|
||||
(because it's often much lower than the download capacity) which
|
||||
might make it hard to find.
|
||||
|
||||
2. The 20% headroom is wasted most of the time. Whenever the user
|
||||
is not using the internet connection for anything, those extra 20%
|
||||
could have been used by bittorrent to upload, but they're already
|
||||
allocated for interactive traffic. On top of that, 20% of the up-link
|
||||
is often not enough to give a good and responsive browsing experience.
|
||||
|
||||
The ideal bandwidth allocation would be to use 100% for bittorrent when
|
||||
there is no interactive cross traffic, and 100% for interactive traffic
|
||||
whenever there is any. This would not waste any bandwidth while the user
|
||||
is idling, and it would make for a much better experience when the user
|
||||
is using the internet connection for other things.
|
||||
|
||||
This is what uTP does.
|
||||
|
||||
TCP
|
||||
---
|
||||
|
||||
The reason TCP will fill the send buffer, and cause the delay on all traffic,
|
||||
is because its congestion control is *only* based on packet loss (and timeout).
|
||||
|
||||
Since the modem is buffering, packets won't get dropped until the entire queue
|
||||
is full, and no more packets will fit. The packets will be dropped, TCP will
|
||||
detect this within an RTT or so. When TCP notices a packet loss, it will slow
|
||||
down its send rate and the queue will start to drain again. However, TCP will
|
||||
immediately start to ramp up its send rate again until the buffer is full and
|
||||
it detects packet loss again.
|
||||
|
||||
TCP is designed to fully utilize the link capacity, without causing congestion.
|
||||
Whenever it sense congestion (through packet loss) it backs off. TCP is not
|
||||
designed to keep delays low. When you get the first packet loss (assuming the
|
||||
kind of queue described above, tail-queue) it is already too late. Your queue
|
||||
is full and you have the maximum amount of delay your modem can provide.
|
||||
|
||||
TCP controls its send rate by limiting the number of bytes in-flight at any
|
||||
given time. This limit is called congestion window (*cwnd* for short). During
|
||||
steady state, the congestion window is constantly increasing linearly. Each
|
||||
packet that is successfully transferred will increase cwnd.
|
||||
|
||||
::
|
||||
|
||||
cwnd
|
||||
send_rate = ----
|
||||
RTT
|
||||
|
||||
|
||||
Send rate is proportional to cwnd divided by RTT. A smaller cwnd will cause
|
||||
the send rate to be lower and a larger cwnd will cause the send rate to be
|
||||
higher.
|
||||
|
||||
Using a congestion window instead of controlling the rate directly is simple
|
||||
because it also introduces an upper bound for memory usage for packets that
|
||||
haven't been ACKed yet and needs to be kept around.
|
||||
|
||||
The behavior of TCP, where it bumps up against the ceiling, backs off and then
|
||||
starts increasing again until it hits the ceiling again, forms a saw tooth shape.
|
||||
If the modem wouldn't have any send buffer at all, a single TCP stream would
|
||||
not be able to fully utilize the link because of this behavior, since it would
|
||||
only fully utilize the link right before the packet loss and the back-off.
|
||||
|
||||
LEDBAT congestion controller
|
||||
----------------------------
|
||||
|
||||
The congestion controller in uTP is called LEDBAT_, which also is an IETF working
|
||||
group attempting to standardize it. The congestion controller, on top of reacting
|
||||
to packet loss the same way TCP does, also reacts to changes in delays.
|
||||
|
||||
For any uTP (or LEDBAT_) implementation, there is a target delay. This is the
|
||||
amount of delay that is acceptable, and is in fact targeted for the connection.
|
||||
The target delay is defined to 25 ms in LEDBAT_, uTorrent uses 100 ms and
|
||||
libtorrent uses 75 ms. Whenever a delay measurement is lower than the target,
|
||||
cwnd is increased proportional to (target_delay - delay). Whenever the measurement
|
||||
is higher than the target, cwnd is decreased proportional to (delay - target_delay).
|
||||
|
||||
It can simply be expressed as::
|
||||
|
||||
cwnd += gain * (target_delay - delay)
|
||||
|
||||
.. image:: cwnd_thumb.png
|
||||
:target: cwnd.png
|
||||
:align: right
|
||||
|
||||
Similarly to TCP, this is scaled so that the increase is evened out over one RTT.
|
||||
|
||||
The linear controller will adjust the cwnd more for delays that are far off the
|
||||
target, and less for delays that are close to the target. This makes it converge
|
||||
at the target delay. Although, due to noise there is almost always some amount of
|
||||
oscillation. This oscillation is typically smaller than the saw tooth TCP forms.
|
||||
|
||||
The figure to the right shows how (TCP) cross traffic causese uTP to essentially
|
||||
entirely stop sending anything. Its delay measurements are mostly well above the target
|
||||
during this time. The cross traffic is only a single TCP stream in this test.
|
||||
|
||||
As soon as the cross traffic ceases, uTP will pick up its original send rate within
|
||||
a second.
|
||||
|
||||
Since uTP constantly measures the delay, with every single packet, the reaction time
|
||||
to cross traffic causing delays is a single RTT (typically a fraction of a second).
|
||||
|
||||
one way delays
|
||||
--------------
|
||||
|
||||
uTP measures the delay imposed on packets being sent to the other end
|
||||
of the connection. This measurement only includes buffering delay along
|
||||
the link, not propagation delay (the speed of light times distance) nor
|
||||
the routing delay (the time routers spend figuring out where to forward
|
||||
the packet). It does this by always comparing all measurements to a
|
||||
baseline measurement, to cancel out any fixed delay. By focusing on the
|
||||
variable delay along a link, it will specifically detect points where
|
||||
there might be congestion, since those points will have buffers.
|
||||
|
||||
.. image:: delays_thumb.png
|
||||
:target: delays.png
|
||||
:align: right
|
||||
|
||||
Delay on the return link is explicitly not included in the delay measurement.
|
||||
This is because in a peer-to-peer application, the other end is likely to also
|
||||
be connected via a modem, with the same send buffer restrictions as we assume
|
||||
for the sending side. The other end having its send queue full is not an indication
|
||||
of congestion on the path going the other way.
|
||||
|
||||
In order to measure one way delays for packets, we cannot rely on clocks being
|
||||
synchronized, especially not at the microsecond level. Instead, the actual time
|
||||
it takes for a packet to arrive at the destination is not measured, only the changes
|
||||
in the transit time is measured.
|
||||
|
||||
Each packet that is sent includes a time stamp of the current time, in microseconds,
|
||||
of the sending machine. The receiving machine calculates the difference between its
|
||||
own timestamp and the one in the packet and sends this back in the ACK. This difference,
|
||||
since it is in microseconds, will essentially be a random 32 bit number. However,
|
||||
the difference will stay somewhat similar over time. Any changes in this difference
|
||||
indicates that packets are either going through faster or slower.
|
||||
|
||||
In order to measure the one-way buffering delay, a base delay is established. The
|
||||
base delay is the lowest ever seen value of the time stamp difference. Each delay
|
||||
sample we receive back, is compared against the base delay and the delay is the
|
||||
difference.
|
||||
|
||||
This is the delay that's fed into the congestion controller.
|
||||
|
||||
A histogram of typical delay measurements is shown to the right. This is from
|
||||
a transfer between a cable modem connection and a DSL connection.
|
||||
|
||||
The details of the delay measurements are slightly more complicated since the
|
||||
values needs to be able to wrap (cross the 2^32 boundry and start over at 0).
|
||||
|
||||
Path MTU discovery
|
||||
------------------
|
||||
|
||||
MTU is short for *Maximum Transfer Unit* and describes the largest packet size that
|
||||
can be sent over a link. Any datagrams which size exceeds this limit will either
|
||||
be *fragmented* or dropped. A fragmented datagram means that the payload is split up
|
||||
in multiple packets, each with its own individual packet header.
|
||||
|
||||
There are several reasons to avoid sending datagrams that get fragmented:
|
||||
|
||||
1. A fragmented datagram is more likely to be lost. If any fragment is lost,
|
||||
the whole datagram is dropped.
|
||||
|
||||
2. Bandwidth is likely to be wasted. If the datagram size is not divisible
|
||||
by the MTU the last packet will not contain as much payload as it could, and the
|
||||
payload over protocol header ratio decreases.
|
||||
|
||||
3. It's expensive to fragment datagrams. Few routers are optimized to handle large
|
||||
numbers of fragmented packets. Datagrams that have to fragment are likely to
|
||||
be delayed significantly, and contribute to more CPU being used on routers.
|
||||
Typically fragmentation (and other advanced IP features) are implemented in
|
||||
software (slow) and not hardware (fast).
|
||||
|
||||
The path MTU is the lowest MTU of any link along a path from two endpoints on the
|
||||
internet. The MTU bottleneck isn't necessarily at one of the endpoints, but can
|
||||
be anywhere in between.
|
||||
|
||||
The most common MTU is 1500 bytes, which is the largest packet size for ethernet
|
||||
networks. Many home DSL connections, however, tunnel IP through PPPoE (Point to
|
||||
Point Protocol over Ethernet. Yes, that is the old dial-up modem protocol). This
|
||||
protocol uses up 8 bytes per packet for its own header.
|
||||
|
||||
If the user happens to be on an internet connection over a VPN, it will add another
|
||||
layer, with its own packet headers.
|
||||
|
||||
In short; if you would pick the largest possible packet size on an ethernet network,
|
||||
1472, and stick with it, you would be quite likely to generate fragments for a lot
|
||||
of connections. The fragments that will be created will be very small and especially
|
||||
inflate the overhead waste.
|
||||
|
||||
The other approach of picking a very conservative packet size, that would be very
|
||||
unlikely to get fragmented has the following drawbacks:
|
||||
|
||||
1. People on good, normal, networks will be penalized with a small packet size.
|
||||
Both in terms of router load but also bandwidth waste.
|
||||
|
||||
2. Software routers are typically not limited by the number of bytes they can route,
|
||||
but the number of packets. Small packets means more of them, and more load on
|
||||
software routers.
|
||||
|
||||
The solution to the problem of finding the optimal packet size, is to dynamically
|
||||
adjust the packet size and search for the largest size that can make it through
|
||||
without being fragmented along the path.
|
||||
|
||||
To help do this, you can set the DF bit (Don't Fragment) in your Datagrams. This
|
||||
asks routers that otherwise would fragment packets to instead drop them, and send
|
||||
back an ICMP message reporting the MTU of the link the packet couldn't fit. With
|
||||
this message, it's very simple to discover the path MTU. You simply mark your packets
|
||||
not to be fragmented, and change your packet size whenever you receive the ICMP
|
||||
packet-too-big message.
|
||||
|
||||
Unfortunately it's not quite that simple. There are a significant number of firewalls
|
||||
in the wild blocking all ICMP messages. This means we can't rely on them, we also have
|
||||
to guess that a packet was dropped because of its size. This is done by only marking
|
||||
certain packets with DF, and if all other packets go through, except for the MTU probes,
|
||||
we know that we need to lower our packet sizes.
|
||||
|
||||
If we set up bounds for the path MTU (say the minimum internet MTU, 576 and ethernet's 1500),
|
||||
we can do a binary search for the MTU. This would let us find it in just a few round-trips.
|
||||
|
||||
On top of this, libtorrent has an optimization where it figures out which interface a
|
||||
uTP connection will be sent over, and initialize the MTU ceiling to that interface's MTU.
|
||||
This means that a VPN tunnel would advertize its MTU as lower, and the uTP connection would
|
||||
immediately know to send smaller packets, no search required. It also has the side-effect
|
||||
of being able to use much larger packet sizes for non-ethernet interfaces or ethernet links
|
||||
with jumbo frames.
|
||||
|
||||
clock drift
|
||||
-----------
|
||||
|
||||
.. image:: our_delay_base_thumb.png
|
||||
:target: our_delay_base.png
|
||||
:align: right
|
||||
|
||||
Clock drift is clocks progressing at different rates. It's different from clock
|
||||
skew which means clocks set to different values (but which may progress at the same
|
||||
rate).
|
||||
|
||||
Any clock drift between the two machines involved in a uTP transfer will result
|
||||
in systematically inflated or deflated delay measurements.
|
||||
|
||||
This can be solved by letting the base delay be the lowest seen sample in the last
|
||||
*n* minutes. This is a trade-off between seeing a single packet go straight through
|
||||
the queue, with no delay, and the amount of clock drift one can assume on normal computers.
|
||||
|
||||
It turns out that it's fairly safe to assume that one of your packets will in fact go
|
||||
straight through without any significant delay, once every 20 minutes or so. However,
|
||||
the clock drift between normal computers can be as much as 17 ms in 10 minutes. 17 ms
|
||||
is quite significant, especially if your target delay is 25 ms (as in the LEDBAT_ spec).
|
||||
|
||||
Clocks progresses at different rates depending on temperature. This means computers
|
||||
running hot are likely to have a clock drift compared to computers running cool.
|
||||
|
||||
So, by updating the delay base periodically based on the lowest seen sample, you'll either
|
||||
end up changing it upwards (artificaially making the delay samples appear small) without
|
||||
the congestion or delay actually having changed, or you'll end up with a significant clock
|
||||
drift and have artificially low samples because of that.
|
||||
|
||||
The solution to this problem is based on the fact that the clock drift is only a problem
|
||||
for one of the sides of the connection. Only when your delay measurements keep increasing
|
||||
is it a problem. If your delay measurements keep decreasing, the samples will simply push
|
||||
down the delay base along with it. With this in mind, we can simply keep track of the
|
||||
other end's delay measurements as well, applying the same logic to it. Whenever the
|
||||
other end's base delay is adjusted downwards, we adjust our base delay upwards by the same
|
||||
amount.
|
||||
|
||||
This will accurately keep the base delay updated with the clock drift and improve
|
||||
the delay measurements. The figure on the right shows the absolute timestamp differences
|
||||
along with the base delay. The slope of the measurements is caused by clock drift.
|
||||
|
||||
For more information on the clock drift compensation, see the slides from BitTorrent's
|
||||
presentation at IPTPS10_.
|
||||
|
||||
.. _IPTPS10: http://www.usenix.org/event/iptps10/tech/slides/cohen.pdf
|
||||
.. _LEDBAT: https://datatracker.ietf.org/doc/draft-ietf-ledbat-congestion/
|
||||
|
||||
features
|
||||
--------
|
||||
|
||||
libtorrent's uTP implementation includes the following features:
|
||||
|
||||
* Path MTU discovery, including jumbo frames and detecting restricted
|
||||
MTU tunnels. Binary search packet sizes to find the largest non-fragmented.
|
||||
* Selective ACK. The ability to acknowledge individual packets in the
|
||||
event of packet loss
|
||||
* Fast resend. The first time a packet is lost, it's resent immediately.
|
||||
Triggered by duplicate ACKs.
|
||||
* Nagle's algorithm. Minimize protocol overhead by attempting to lump
|
||||
full packets of payload together before sending a packet.
|
||||
* Delayed ACKs to minimize protocol overhead.
|
||||
* Microsecond resolution timestamps.
|
||||
* Advertised receive window, to support download rate limiting.
|
||||
* Correct handling of wrapping sequence numbers.
|
||||
* Easy configuration of target-delay, gain-factor, timeouts, delayed-ack
|
||||
and socket buffers.
|
||||
|
|
@ -3,7 +3,8 @@ example_programs = \
|
|||
dump_torrent \
|
||||
enum_if \
|
||||
make_torrent \
|
||||
simple_client
|
||||
simple_client \
|
||||
utp_test
|
||||
|
||||
if ENABLE_EXAMPLES
|
||||
bin_PROGRAMS = $(example_programs)
|
||||
|
|
|
@ -174,6 +174,7 @@ bool print_file_progress = false;
|
|||
bool show_pad_files = false;
|
||||
bool show_dht_status = false;
|
||||
bool sequential_download = false;
|
||||
bool print_utp_stats = false;
|
||||
|
||||
bool print_ip = true;
|
||||
bool print_as = false;
|
||||
|
@ -384,7 +385,7 @@ int peer_index(libtorrent::tcp::endpoint addr, std::vector<libtorrent::peer_info
|
|||
void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const& peers)
|
||||
{
|
||||
using namespace libtorrent;
|
||||
if (print_ip) out += "IP ";
|
||||
if (print_ip) out += "IP ";
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
if (print_as) out += "AS ";
|
||||
#endif
|
||||
|
@ -409,8 +410,8 @@ void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const&
|
|||
|
||||
if (print_ip)
|
||||
{
|
||||
error_code ec;
|
||||
snprintf(str, sizeof(str), "%-22s %22s ", print_endpoint(i->ip).c_str()
|
||||
snprintf(str, sizeof(str), "%-30s %-22s", (print_endpoint(i->ip) +
|
||||
(i->connection_type == peer_info::bittorrent_utp ? " [uTP]" : "")).c_str()
|
||||
, print_endpoint(i->local_endpoint).c_str());
|
||||
out += str;
|
||||
}
|
||||
|
@ -425,7 +426,7 @@ void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const&
|
|||
#endif
|
||||
|
||||
snprintf(str, sizeof(str)
|
||||
, "%s%s (%s|%s) %s%s (%s|%s) %s%3d (%3d) %3d %c%c%c%c%c%c%c%c%c%c%c%c%c%c %c%c%c%c%c%c "
|
||||
, "%s%s (%s|%s) %s%s (%s|%s) %s%3d (%3d) %3d %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %c%c%c%c%c%c "
|
||||
, esc("32"), add_suffix(i->down_speed, "/s").c_str()
|
||||
, add_suffix(i->total_download).c_str(), add_suffix(i->download_rate_peak, "/s").c_str()
|
||||
, esc("31"), add_suffix(i->up_speed, "/s").c_str(), add_suffix(i->total_upload).c_str()
|
||||
|
@ -456,6 +457,8 @@ void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const&
|
|||
#else
|
||||
, '.'
|
||||
#endif
|
||||
, (i->flags & peer_info::holepunched)?'h':'.'
|
||||
|
||||
, (i->source & peer_info::tracker)?'T':'_'
|
||||
, (i->source & peer_info::pex)?'P':'_'
|
||||
, (i->source & peer_info::dht)?'D':'_'
|
||||
|
@ -833,9 +836,12 @@ int main(int argc, char* argv[])
|
|||
" -L <user:passwd> Use the specified username and password for the\n"
|
||||
" proxy specified by -P\n"
|
||||
" -H Don't start DHT\n"
|
||||
" -M Disable TCP/uTP bandwidth balancing\n"
|
||||
" -W <num peers> Set the max number of peers to keep in the peer list\n"
|
||||
" -N Do not attempt to use UPnP and NAT-PMP to forward ports\n"
|
||||
" -Y Rate limit local peers\n"
|
||||
" -y Disable TCP connections (disable outgoing TCP and reject\n"
|
||||
" incoming TCP connections)\n"
|
||||
" -q <num loops> automatically quit the client after <num loops> of refreshes\n"
|
||||
" this is useful for scripting tests\n"
|
||||
" "
|
||||
|
@ -903,8 +909,6 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
if (argv[i][0] != '-')
|
||||
{
|
||||
// interpret this as a torrent
|
||||
|
||||
// match it against the <hash>@<tracker> format
|
||||
if (strlen(argv[i]) > 45
|
||||
&& is_hex(argv[i], 40)
|
||||
|
@ -955,7 +959,7 @@ int main(int argc, char* argv[])
|
|||
case 'U': torrent_upload_limit = atoi(arg) * 1000; break;
|
||||
case 'D': torrent_download_limit = atoi(arg) * 1000; break;
|
||||
case 'm': monitor_dir = arg; break;
|
||||
case 'M': share_mode = true; --i; break;
|
||||
case 'Q': share_mode = true; --i; break;
|
||||
case 'b': bind_to_interface = arg; break;
|
||||
case 'w': settings.urlseed_wait_retry = atoi(arg); break;
|
||||
case 't': poll_interval = atoi(arg); break;
|
||||
|
@ -1006,6 +1010,8 @@ int main(int argc, char* argv[])
|
|||
case 'A': settings.allowed_fast_set_size = atoi(arg); break;
|
||||
case 'R': settings.read_cache_line_size = atoi(arg); break;
|
||||
case 'O': settings.allow_reordered_disk_operations = false; --i; break;
|
||||
case 'M': settings.mixed_mode_algorithm = session_settings::prefer_tcp; --i; break;
|
||||
case 'y': settings.enable_outgoing_tcp = false; settings.enable_incoming_tcp = false; --i; break;
|
||||
case 'P':
|
||||
{
|
||||
char* port = (char*) strrchr(arg, ':');
|
||||
|
@ -1297,6 +1303,7 @@ int main(int argc, char* argv[])
|
|||
if (c == 'h') show_pad_files = !show_pad_files;
|
||||
if (c == 'a') print_piece_bar = !print_piece_bar;
|
||||
if (c == 'g') show_dht_status = !show_dht_status;
|
||||
if (c == 'u') print_utp_stats = !print_utp_stats;
|
||||
// toggle columns
|
||||
if (c == '1') print_ip = !print_ip;
|
||||
if (c == '2') print_as = !print_as;
|
||||
|
@ -1566,6 +1573,15 @@ int main(int argc, char* argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
if (print_utp_stats)
|
||||
{
|
||||
snprintf(str, sizeof(str), "uTP idle: %d syn: %d est: %d fin: %d wait: %d\n"
|
||||
, sess_stat.utp_stats.num_idle, sess_stat.utp_stats.num_syn_sent
|
||||
, sess_stat.utp_stats.num_connected, sess_stat.utp_stats.num_fin_sent
|
||||
, sess_stat.utp_stats.num_close_wait);
|
||||
out += str;
|
||||
}
|
||||
|
||||
if (active_handle.is_valid())
|
||||
{
|
||||
torrent_handle h = active_handle;
|
||||
|
|
|
@ -61,15 +61,16 @@ int main()
|
|||
return 1;
|
||||
}
|
||||
|
||||
printf("%-18s%-18s%-35sinterface name\n", "destination", "network", "gateway");
|
||||
printf("%-18s%-18s%-35s%-7sinterface\n", "destination", "network", "gateway", "mtu");
|
||||
|
||||
for (std::vector<ip_route>::const_iterator i = routes.begin()
|
||||
, end(routes.end()); i != end; ++i)
|
||||
{
|
||||
printf("%-18s%-18s%-35s%s\n"
|
||||
printf("%-18s%-18s%-35s%-7d%s\n"
|
||||
, i->destination.to_string(ec).c_str()
|
||||
, i->netmask.to_string(ec).c_str()
|
||||
, i->gateway.to_string(ec).c_str()
|
||||
, i->mtu
|
||||
, i->name);
|
||||
}
|
||||
|
||||
|
@ -82,15 +83,16 @@ int main()
|
|||
return 1;
|
||||
}
|
||||
|
||||
printf("%-18s%-18s%-35sflags\n", "address", "netmask", "name");
|
||||
printf("%-35s%-18s%-40s%-8sflags\n", "address", "netmask", "name", "mtu");
|
||||
|
||||
for (std::vector<ip_interface>::const_iterator i = net.begin()
|
||||
, end(net.end()); i != end; ++i)
|
||||
{
|
||||
printf("%-18s%-18s%-35s%s%s%s\n"
|
||||
printf("%-35s%-18s%-40s%-8d%s%s%s\n"
|
||||
, i->interface_address.to_string(ec).c_str()
|
||||
, i->netmask.to_string(ec).c_str()
|
||||
, i->name
|
||||
, i->mtu
|
||||
, (is_multicast(i->interface_address)?"multicast ":"")
|
||||
, (is_local(i->interface_address)?"local ":"")
|
||||
, (is_loopback(i->interface_address)?"loopback ":"")
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#include "libtorrent/error_code.hpp"
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/utp_socket_manager.hpp"
|
||||
#include "libtorrent/utp_stream.hpp"
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
void on_connect(error_code const& e)
|
||||
{
|
||||
}
|
||||
|
||||
void on_udp_receive(error_code const& e, udp::endpoint const& ep
|
||||
, char const* buf, int size)
|
||||
{
|
||||
}
|
||||
|
||||
void on_utp_incoming(void* userdata
|
||||
, boost::shared_ptr<utp_stream> const& utp_sock)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
//int rtt, rtt_var;
|
||||
//int max_window, cur_window;
|
||||
//int delay_factor, window_factor, scaled_gain;
|
||||
|
||||
/*session s;
|
||||
s.listen_on(std::make_pair(6881, 6889));*/
|
||||
|
||||
io_service ios;
|
||||
connection_queue cc(ios);
|
||||
udp_socket udp_sock(ios, boost::bind(&on_udp_receive, _1, _2, _3, _4), cc);
|
||||
|
||||
void* userdata;
|
||||
utp_socket_manager utp_sockets(udp_sock, boost::bind(&on_utp_incoming, _1, _2), userdata);
|
||||
|
||||
/*error_code ec;
|
||||
utp_stream sock(ios, cc);
|
||||
sock.bind(udp::endpoint(address_v4::any(), 0), ec);
|
||||
|
||||
tcp::endpoint ep(address_v4::from_string("239.192.152.143", ec), 6771);
|
||||
|
||||
sock.async_connect(ep, boost::bind(on_connect, _1));*/
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -94,6 +94,7 @@ nobase_include_HEADERS = \
|
|||
storage_defs.hpp \
|
||||
thread.hpp \
|
||||
time.hpp \
|
||||
timestamp_history.hpp \
|
||||
torrent_handle.hpp \
|
||||
torrent.hpp \
|
||||
torrent_info.hpp \
|
||||
|
@ -102,6 +103,8 @@ nobase_include_HEADERS = \
|
|||
udp_tracker_connection.hpp \
|
||||
union_endpoint.hpp \
|
||||
upnp.hpp \
|
||||
utp_socket_manager.hpp \
|
||||
utp_stream.hpp \
|
||||
utf8.hpp \
|
||||
version.hpp \
|
||||
web_peer_connection.hpp \
|
||||
|
|
|
@ -51,11 +51,17 @@ std::string demangle(char const* name);
|
|||
|
||||
#if (defined __linux__ || defined __MACH__) && defined __GNUC__
|
||||
|
||||
#if TORRENT_USE_IOSTREAM
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
TORRENT_EXPORT void assert_fail(const char* expr, int line, char const* file, char const* function, char const* val);
|
||||
#define TORRENT_ASSERT(x) do { if (x) {} else assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, 0); } while (false)
|
||||
#if TORRENT_USE_IOSTREAM
|
||||
#define TORRENT_ASSERT_VAL(x, y) do { if (x) {} else { std::stringstream __s__; __s__ << #y ": " << y; assert_fail(#x, __LINE__, __FILE__, __PRETTY_FUNCTION__, __s__.str().c_str()); } } while (false)
|
||||
#else
|
||||
#define TORRENT_ASSERT_VAL(x, y) TORRENT_ASSERT(x)
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <cassert>
|
||||
|
|
|
@ -82,6 +82,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/deadline_timer.hpp"
|
||||
#include "libtorrent/socket_io.hpp" // for print_address
|
||||
#include "libtorrent/address.hpp"
|
||||
#include "libtorrent/utp_socket_manager.hpp"
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
#include <fstream>
|
||||
|
@ -522,6 +523,15 @@ namespace libtorrent
|
|||
bandwidth_channel m_local_download_channel;
|
||||
bandwidth_channel m_local_upload_channel;
|
||||
|
||||
// all tcp peer connections are subject to these
|
||||
// bandwidth limits. Local peers are excempted
|
||||
// from this limit. The purpose is to be able to
|
||||
// throttle TCP that passes over the internet
|
||||
// bottleneck (i.e. modem) to avoid starving out
|
||||
// uTP connections.
|
||||
bandwidth_channel m_tcp_download_channel;
|
||||
bandwidth_channel m_tcp_upload_channel;
|
||||
|
||||
bandwidth_channel* m_bandwidth_channel[2];
|
||||
|
||||
tracker_manager m_tracker_manager;
|
||||
|
@ -725,6 +735,8 @@ namespace libtorrent
|
|||
|
||||
rate_limited_udp_socket m_udp_socket;
|
||||
|
||||
utp_socket_manager m_utp_socket_manager;
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
pe_settings m_pe_settings;
|
||||
#endif
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace libtorrent
|
|||
TORRENT_EXPORT bool is_loopback(address const& addr);
|
||||
TORRENT_EXPORT bool is_multicast(address const& addr);
|
||||
TORRENT_EXPORT bool is_any(address const& addr);
|
||||
TORRENT_EXPORT bool is_teredo(address const& addr);
|
||||
TORRENT_EXPORT int cidr_distance(address const& a1, address const& a2);
|
||||
|
||||
// determines if the operating system supports IPv6
|
||||
|
|
|
@ -102,7 +102,12 @@ namespace libtorrent
|
|||
|
||||
void start();
|
||||
|
||||
enum { upload_only_msg = 2, share_mode_msg = 3 };
|
||||
enum
|
||||
{
|
||||
upload_only_msg = 2,
|
||||
holepunch_msg = 3,
|
||||
share_mode_msg = 4
|
||||
};
|
||||
|
||||
~bt_peer_connection();
|
||||
|
||||
|
@ -140,6 +145,20 @@ namespace libtorrent
|
|||
num_supported_messages
|
||||
};
|
||||
|
||||
enum hp_message_t
|
||||
{
|
||||
// msg_types
|
||||
hp_rendezvous = 0,
|
||||
hp_connect = 1,
|
||||
hp_failed = 2,
|
||||
|
||||
// error codes
|
||||
hp_no_such_peer = 1,
|
||||
hp_not_connected = 2,
|
||||
hp_no_support = 3,
|
||||
hp_no_self = 4
|
||||
};
|
||||
|
||||
// called from the main loop when this connection has any
|
||||
// work to do.
|
||||
|
||||
|
@ -151,6 +170,9 @@ namespace libtorrent
|
|||
virtual void get_specific_peer_info(peer_info& p) const;
|
||||
virtual bool in_handshake() const;
|
||||
|
||||
bool supports_holepunch() const { return m_holepunch_id != 0; }
|
||||
void write_holepunch_msg(int type, tcp::endpoint const& ep, int error);
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
bool support_extensions() const { return m_supports_extensions; }
|
||||
#endif
|
||||
|
@ -183,6 +205,7 @@ namespace libtorrent
|
|||
void on_have_none(int received);
|
||||
void on_reject_request(int received);
|
||||
void on_allowed_fast(int received);
|
||||
void on_holepunch();
|
||||
|
||||
void on_extended(int received);
|
||||
|
||||
|
@ -283,7 +306,7 @@ public:
|
|||
#endif
|
||||
}
|
||||
#endif
|
||||
peer_connection::append_send_buffer(buffer, size, destructor);
|
||||
peer_connection::append_send_buffer(buffer, size, destructor, true);
|
||||
}
|
||||
void setup_send();
|
||||
|
||||
|
@ -366,6 +389,9 @@ private:
|
|||
// 0 if not supported
|
||||
int m_upload_only_id;
|
||||
|
||||
// the message ID for holepunch messages
|
||||
int m_holepunch_id;
|
||||
|
||||
// the message ID for share mode message
|
||||
// 0 if not supported
|
||||
int m_share_mode_id;
|
||||
|
|
|
@ -42,11 +42,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace libtorrent
|
||||
{
|
||||
|
||||
// the interface should not have a netmask
|
||||
struct ip_interface
|
||||
{
|
||||
address interface_address;
|
||||
address netmask;
|
||||
char name[64];
|
||||
int mtu;
|
||||
};
|
||||
|
||||
struct ip_route
|
||||
|
@ -55,6 +57,7 @@ namespace libtorrent
|
|||
address netmask;
|
||||
address gateway;
|
||||
char name[64];
|
||||
int mtu;
|
||||
};
|
||||
|
||||
// returns a list of the configured IP interfaces
|
||||
|
@ -64,9 +67,8 @@ namespace libtorrent
|
|||
|
||||
TORRENT_EXPORT std::vector<ip_route> enum_routes(io_service& ios, error_code& ec);
|
||||
|
||||
// returns true if the specified address is on the same
|
||||
// local network as the specified interface
|
||||
TORRENT_EXPORT bool in_subnet(address const& addr, ip_interface const& iface);
|
||||
// return (a1 & mask) == (a2 & mask)
|
||||
TORRENT_EXPORT bool match_addr_mask(address const& a1, address const& a2, address const& mask);
|
||||
|
||||
// returns true if the specified address is on the same
|
||||
// local network as us
|
||||
|
|
|
@ -165,7 +165,7 @@ namespace libtorrent
|
|||
pex_message_too_large,
|
||||
invalid_pex_message,
|
||||
invalid_lt_tracker_message,
|
||||
reserved108,
|
||||
too_frequent_pex,
|
||||
reserved109,
|
||||
reserved110,
|
||||
reserved111,
|
||||
|
|
|
@ -90,6 +90,8 @@ namespace libtorrent
|
|||
{
|
||||
virtual ~peer_plugin() {}
|
||||
|
||||
virtual char const* type() const { return ""; }
|
||||
|
||||
// can add entries to the extension handshake
|
||||
// this is not called for web seeds
|
||||
virtual void add_handshake(entry&) {}
|
||||
|
|
|
@ -39,10 +39,12 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace libtorrent
|
||||
{
|
||||
struct proxy_settings;
|
||||
struct utp_socket_manager;
|
||||
|
||||
bool instantiate_connection(io_service& ios
|
||||
, proxy_settings const& ps, socket_type& s
|
||||
, void* ssl_context = 0);
|
||||
, void* ssl_context = 0
|
||||
, utp_socket_manager* sm = 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -93,6 +93,18 @@ namespace libtorrent
|
|||
value = max3<temp1, temp2, temp3>::value
|
||||
};
|
||||
};
|
||||
|
||||
template<int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8>
|
||||
struct max8
|
||||
{
|
||||
enum
|
||||
{
|
||||
temp1 = max<v1,v2>::value,
|
||||
temp2 = max3<v3,v4,v5>::value,
|
||||
temp3 = max3<v6,v7,v8>::value,
|
||||
value = max3<temp1, temp2, temp3>::value
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2010, 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_PACKET_BUFFER_HPP_INCLUDED
|
||||
#define TORRENT_PACKET_BUFFER_HPP_INCLUDED
|
||||
|
||||
#include "boost/cstdint.hpp"
|
||||
#include <cstddef>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
// this is a circular buffer that automatically resizes
|
||||
// itself as elements are inserted. Elements are indexed
|
||||
// by integers and are assumed to be sequential. Unless the
|
||||
// old elements are removed when new elements are inserted,
|
||||
// the buffer will be resized.
|
||||
|
||||
// if m_mask is 0xf, m_array has 16 elements
|
||||
// m_cursor is the lowest index that has an element
|
||||
// it also determines which indices the other slots
|
||||
// refers to. Since it's a circular buffer, it wraps
|
||||
// around. For example
|
||||
|
||||
// m_cursor = 9
|
||||
// | refers to index 14
|
||||
// | |
|
||||
// V V
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | | | | | | | | | | | | | | | | | m_mask = 0xf
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// ^
|
||||
// |
|
||||
// refers to index 15
|
||||
|
||||
// whenever the element at the cursor is removed, the
|
||||
// cursor is bumped to the next occupied element
|
||||
|
||||
class packet_buffer
|
||||
{
|
||||
public:
|
||||
typedef boost::uint32_t index_type;
|
||||
|
||||
packet_buffer();
|
||||
~packet_buffer();
|
||||
|
||||
void* insert(index_type idx, void* value);
|
||||
|
||||
std::size_t size() const
|
||||
{ return m_size; }
|
||||
|
||||
std::size_t capacity() const
|
||||
{ return m_capacity; }
|
||||
|
||||
void* at(index_type idx) const;
|
||||
|
||||
void* remove(index_type idx);
|
||||
|
||||
void reserve(std::size_t size);
|
||||
|
||||
index_type cursor() const
|
||||
{ return m_first; }
|
||||
|
||||
index_type span() const
|
||||
{ return (m_last - m_first) & 0xffff; }
|
||||
|
||||
private:
|
||||
void** m_storage;
|
||||
std::size_t m_capacity;
|
||||
std::size_t m_size;
|
||||
|
||||
// This defines the first index that is part of the m_storage.
|
||||
// The last index is (m_first + (m_capacity - 1)) & 0xffff.
|
||||
index_type m_first;
|
||||
index_type m_last;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TORRENT_PACKET_BUFFER_HPP_INCLUDED
|
||||
|
|
@ -78,6 +78,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/bandwidth_socket.hpp"
|
||||
#include "libtorrent/socket_type_fwd.hpp"
|
||||
#include "libtorrent/error_code.hpp"
|
||||
#include "libtorrent/sliding_average.hpp"
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
#include "libtorrent/aux_/session_impl.hpp"
|
||||
|
@ -86,9 +87,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace libtorrent
|
||||
{
|
||||
class torrent;
|
||||
struct peer_plugin;
|
||||
struct peer_info;
|
||||
struct disk_io_job;
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
struct peer_plugin;
|
||||
#endif
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
@ -202,6 +205,7 @@ namespace libtorrent
|
|||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
void add_extension(boost::shared_ptr<peer_plugin>);
|
||||
peer_plugin const* find_plugin(char const* type);
|
||||
#endif
|
||||
|
||||
// this function is called once the torrent associated
|
||||
|
@ -284,6 +288,14 @@ namespace libtorrent
|
|||
void set_upload_only(bool u);
|
||||
bool upload_only() const { return m_upload_only; }
|
||||
|
||||
void set_holepunch_mode()
|
||||
{
|
||||
m_holepunch_mode = true;
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string() << "*** HOLEPUNCH MODE ***\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
// will send a keep-alive message to the peer
|
||||
void keep_alive();
|
||||
|
||||
|
@ -341,6 +353,9 @@ namespace libtorrent
|
|||
void on_timeout();
|
||||
// this will cause this peer_connection to be disconnected.
|
||||
virtual void disconnect(error_code const& ec, int error = 0);
|
||||
// called when a connect attempt fails (not when an
|
||||
// established connection fails)
|
||||
void connect_failed(error_code const& e);
|
||||
bool is_disconnecting() const { return m_disconnecting; }
|
||||
|
||||
// this is called when the connection attempt has succeeded
|
||||
|
@ -522,11 +537,17 @@ namespace libtorrent
|
|||
#endif
|
||||
|
||||
template <class Destructor>
|
||||
void append_send_buffer(char* buffer, int size, Destructor const& destructor)
|
||||
void append_send_buffer(char* buffer, int size, Destructor const& destructor
|
||||
, bool encrypted = false)
|
||||
{
|
||||
#if defined TORRENT_STATS && defined TORRENT_DISK_STATS
|
||||
log_buffer_usage(buffer, size, "queued send buffer");
|
||||
#endif
|
||||
// bittorrent connections should never use this function, since
|
||||
// they might be encrypted and this would circumvent the actual
|
||||
// encryption. bt_peer_connection overrides this function with
|
||||
// its own version.
|
||||
TORRENT_ASSERT(encrypted || type() != bittorrent_connection);
|
||||
m_send_buffer.append_buffer(buffer, size, size, destructor);
|
||||
}
|
||||
|
||||
|
@ -634,6 +655,8 @@ namespace libtorrent
|
|||
|
||||
bool verify_piece(peer_request const& p) const;
|
||||
|
||||
void update_desired_queue_size();
|
||||
|
||||
// the bandwidth channels, upload and download
|
||||
// keeps track of the current quotas
|
||||
bandwidth_channel m_bandwidth_channel[num_channels];
|
||||
|
@ -665,6 +688,10 @@ namespace libtorrent
|
|||
// web seeds also has a limit on the queue size.
|
||||
int m_max_out_request_queue;
|
||||
|
||||
// the average rate of receiving complete piece messages
|
||||
sliding_average<20> m_piece_rate;
|
||||
sliding_average<20> m_send_rate;
|
||||
|
||||
void set_timeout(int s) { m_timeout = s; }
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
|
@ -711,6 +738,7 @@ namespace libtorrent
|
|||
// the time when we last got a part of a
|
||||
// piece packet from this peer
|
||||
ptime m_last_piece;
|
||||
|
||||
// the time we sent a request to
|
||||
// this peer the last time
|
||||
ptime m_last_request;
|
||||
|
@ -1076,6 +1104,9 @@ namespace libtorrent
|
|||
// set to true when we've sent the first round of suggests
|
||||
bool m_sent_suggests:1;
|
||||
|
||||
// set to true while we're trying to holepunch
|
||||
bool m_holepunch_mode:1;
|
||||
|
||||
// when this is set, the transfer stats for this connection
|
||||
// is not included in the torrent or session stats
|
||||
bool m_ignore_stats:1;
|
||||
|
|
|
@ -59,7 +59,8 @@ namespace libtorrent
|
|||
seed = 0x400,
|
||||
optimistic_unchoke = 0x800,
|
||||
snubbed = 0x1000,
|
||||
upload_only = 0x2000
|
||||
upload_only = 0x2000,
|
||||
holepunched = 0x4000
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
, rc4_encrypted = 0x100000,
|
||||
plaintext_encrypted = 0x200000
|
||||
|
@ -186,7 +187,8 @@ namespace libtorrent
|
|||
{
|
||||
standard_bittorrent = 0,
|
||||
web_seed = 1,
|
||||
http_seed = 2
|
||||
http_seed = 2,
|
||||
bittorrent_utp = 3
|
||||
};
|
||||
int connection_type;
|
||||
|
||||
|
|
|
@ -164,7 +164,8 @@ namespace libtorrent
|
|||
// 43 1 1 failcount, connectable, optimistically_unchoked, seed
|
||||
// 44 1 1 fast_reconnects, trust_points
|
||||
// 45 1 1 source, pe_support, is_v6_addr
|
||||
// 46 1 1 on_parole, banned, added_to_dht
|
||||
// 46 1 1 on_parole, banned, added_to_dht, supports_utp,
|
||||
// supports_holepunch
|
||||
// 47 1 1 <padding>
|
||||
// 48
|
||||
struct TORRENT_EXPORT peer
|
||||
|
@ -311,6 +312,11 @@ namespace libtorrent
|
|||
// pinged by the DHT
|
||||
bool added_to_dht:1;
|
||||
#endif
|
||||
// we think this peer supports uTP
|
||||
bool supports_utp:1;
|
||||
// we have been connected via uTP at least once
|
||||
bool confirmed_supports_utp:1;
|
||||
bool supports_holepunch:1;
|
||||
#ifdef TORRENT_DEBUG
|
||||
bool in_use:1;
|
||||
#endif
|
||||
|
|
|
@ -222,6 +222,11 @@ namespace libtorrent
|
|||
, default_peer_upload_rate(0)
|
||||
, default_peer_download_rate(0)
|
||||
, broadcast_lsd(false)
|
||||
, enable_outgoing_utp(true)
|
||||
, enable_incoming_utp(true)
|
||||
, enable_outgoing_tcp(true)
|
||||
, enable_incoming_tcp(true)
|
||||
, max_pex_peers(200)
|
||||
, ignore_resume_timestamps(false)
|
||||
, anonymous_mode(false)
|
||||
, tick_interval(100)
|
||||
|
@ -234,6 +239,17 @@ namespace libtorrent
|
|||
, unchoke_slots_limit(8)
|
||||
, half_open_limit(0)
|
||||
, connections_limit(200)
|
||||
, utp_target_delay(75) // milliseconds
|
||||
, utp_gain_factor(1500) // bytes per rtt
|
||||
, utp_min_timeout(500) // milliseconds
|
||||
, utp_syn_resends(2)
|
||||
, utp_fin_resends(2)
|
||||
, utp_num_resends(6)
|
||||
, utp_connect_timeout(3000) // milliseconds
|
||||
, utp_delayed_ack(0) // milliseconds
|
||||
, utp_dynamic_sock_buf(true)
|
||||
, mixed_mode_algorithm(peer_proportional)
|
||||
, rate_limit_utp(false)
|
||||
, listen_queue_size(5)
|
||||
{}
|
||||
|
||||
|
@ -856,6 +872,24 @@ namespace libtorrent
|
|||
// a network is known not to support multicast, this can be enabled
|
||||
bool broadcast_lsd;
|
||||
|
||||
// when set to true, libtorrent will try to make outgoing utp connections
|
||||
bool enable_outgoing_utp;
|
||||
|
||||
// if set to false, libtorrent will reject incoming utp connections
|
||||
bool enable_incoming_utp;
|
||||
|
||||
// when set to false, no outgoing TCP connections will be made
|
||||
bool enable_outgoing_tcp;
|
||||
|
||||
// if set to false, libtorrent will reject incoming tcp connections
|
||||
bool enable_incoming_tcp;
|
||||
|
||||
// the max number of peers we accept from pex messages from a single peer.
|
||||
// this limits the number of concurrent peers any of our peers claims to
|
||||
// be connected to. If they clain to be connected to more than this, we'll
|
||||
// ignore any peer that exceeds this limit
|
||||
int max_pex_peers;
|
||||
|
||||
// when set to true, the file modification time is ignored when loading
|
||||
// resume data. The resume data includes the expected timestamp of each
|
||||
// file and is typically compared to make sure the files haven't changed
|
||||
|
@ -903,6 +937,57 @@ namespace libtorrent
|
|||
// the max number of connections in the session
|
||||
int connections_limit;
|
||||
|
||||
// target delay, milliseconds
|
||||
int utp_target_delay;
|
||||
|
||||
// max number of bytes to increase cwnd per rtt in uTP
|
||||
// congestion controller
|
||||
int utp_gain_factor;
|
||||
|
||||
// the shortest allowed uTP connection timeout in milliseconds
|
||||
// defaults to 500 milliseconds. The shorter timeout, the
|
||||
// faster the connection recovers from a loss of an entire window
|
||||
int utp_min_timeout;
|
||||
|
||||
// the number of SYN packets that are sent before giving up
|
||||
int utp_syn_resends;
|
||||
|
||||
// the number of resent packets sent on a closed socket before giving up
|
||||
int utp_fin_resends;
|
||||
|
||||
// the number of times to send a packet before giving up
|
||||
int utp_num_resends;
|
||||
|
||||
// initial timeout for uTP SYN packets
|
||||
int utp_connect_timeout;
|
||||
|
||||
// number of milliseconds of delaying ACKing packets the most
|
||||
int utp_delayed_ack;
|
||||
|
||||
// set to true if the uTP socket buffer size is allowed to increase
|
||||
// dynamically based on the NIC MTU setting. This is true by default
|
||||
// and improves uTP performance for networks with larger frame sizes
|
||||
// including loopback
|
||||
bool utp_dynamic_sock_buf;
|
||||
|
||||
enum bandwidth_mixed_algo_t
|
||||
{
|
||||
// disables the mixed mode bandwidth balancing
|
||||
prefer_tcp = 0,
|
||||
|
||||
// does not throttle uTP, throttles TCP to the same proportion
|
||||
// of throughput as there are TCP connections
|
||||
peer_proportional = 1
|
||||
|
||||
};
|
||||
// the algorithm to use to balance bandwidth between tcp
|
||||
// connections and uTP connections
|
||||
int mixed_mode_algorithm;
|
||||
|
||||
// set to true if uTP connections should be rate limited
|
||||
// defaults to false
|
||||
bool rate_limit_utp;
|
||||
|
||||
// this is the number passed in to listen(). i.e.
|
||||
// the number of connections to accept while we're
|
||||
// not waiting in an accept() call.
|
||||
|
@ -915,7 +1000,9 @@ namespace libtorrent
|
|||
dht_settings()
|
||||
: max_peers_reply(100)
|
||||
, search_branching(5)
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
, service_port(0)
|
||||
#endif
|
||||
, max_fail_count(20)
|
||||
, max_torrent_search_reply(20)
|
||||
{}
|
||||
|
@ -928,9 +1015,11 @@ namespace libtorrent
|
|||
// searching the DHT.
|
||||
int search_branching;
|
||||
|
||||
#ifndef TORRENT_NO_DEPRECATE
|
||||
// the listen port for the dht. This is a UDP port.
|
||||
// zero means use the same as the tcp interface
|
||||
int service_port;
|
||||
#endif
|
||||
|
||||
// the maximum number of times a node can fail
|
||||
// in a row before it is removed from the table.
|
||||
|
|
|
@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "libtorrent/config.hpp"
|
||||
#include "libtorrent/size_type.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -53,6 +54,15 @@ namespace libtorrent
|
|||
|
||||
#endif
|
||||
|
||||
struct utp_status
|
||||
{
|
||||
int num_idle;
|
||||
int num_syn_sent;
|
||||
int num_connected;
|
||||
int num_fin_sent;
|
||||
int num_close_wait;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT session_status
|
||||
{
|
||||
bool has_incoming_connections;
|
||||
|
@ -107,6 +117,8 @@ namespace libtorrent
|
|||
int dht_total_allocations;
|
||||
#endif
|
||||
|
||||
utp_status utp_stats;
|
||||
|
||||
int peerlist_size;
|
||||
};
|
||||
|
||||
|
|
|
@ -30,6 +30,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
*/
|
||||
|
||||
#ifndef TORRENT_SLIDING_AVERAGE_HPP_INCLUDED
|
||||
#define TORRENT_SLIDING_AVERAGE_HPP_INCLUDED
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
// a sliding average accumulator. Add samples to it and it
|
||||
|
@ -70,3 +73,5 @@ private:
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -146,6 +146,40 @@ namespace libtorrent
|
|||
size_t size(Protocol const&) const { return sizeof(m_value); }
|
||||
char m_value;
|
||||
};
|
||||
|
||||
#if defined IP_DONTFRAG || defined IP_MTU_DISCOVER || defined IP_DONTFRAGMENT
|
||||
#define TORRENT_HAS_DONT_FRAGMENT
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_HAS_DONT_FRAGMENT
|
||||
struct dont_fragment
|
||||
{
|
||||
dont_fragment(bool val)
|
||||
#ifdef IP_PMTUDISCOVER_DO
|
||||
: m_value(val ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT) {}
|
||||
#else
|
||||
: m_value(val) {}
|
||||
#endif
|
||||
template<class Protocol>
|
||||
int level(Protocol const&) const { return IPPROTO_IP; }
|
||||
template<class Protocol>
|
||||
int name(Protocol const&) const
|
||||
#if defined IP_DONTFRAG
|
||||
{ return IP_DONTFRAG; }
|
||||
#elif defined IP_MTU_DISCOVER
|
||||
{ return IP_MTU_DISCOVER; }
|
||||
#elif defined IP_DONTFRAGMENT
|
||||
{ return IP_DONTFRAGMENT; }
|
||||
#else
|
||||
{}
|
||||
#endif
|
||||
template<class Protocol>
|
||||
int const* data(Protocol const&) const { return &m_value; }
|
||||
template<class Protocol>
|
||||
size_t size(Protocol const&) const { return sizeof(m_value); }
|
||||
int m_value;
|
||||
};
|
||||
#endif // TORRENT_HAS_DONT_FRAGMENT
|
||||
}
|
||||
|
||||
#endif // TORRENT_SOCKET_HPP_INCLUDED
|
||||
|
|
|
@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/socks5_stream.hpp"
|
||||
#include "libtorrent/http_stream.hpp"
|
||||
#include "libtorrent/i2p_stream.hpp"
|
||||
#include "libtorrent/utp_stream.hpp"
|
||||
#include "libtorrent/io_service.hpp"
|
||||
#include "libtorrent/max.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
@ -96,6 +97,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
get<socks5_stream>()->x; break; \
|
||||
case socket_type_int_impl<http_stream>::value: \
|
||||
get<http_stream>()->x; break; \
|
||||
case socket_type_int_impl<utp_stream>::value: \
|
||||
get<utp_stream>()->x; break; \
|
||||
TORRENT_SOCKTYPE_I2P_FORWARD(x) \
|
||||
TORRENT_SOCKTYPE_SSL_FORWARD(x) \
|
||||
default: TORRENT_ASSERT(false); \
|
||||
|
@ -109,6 +112,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
return get<socks5_stream>()->x; \
|
||||
case socket_type_int_impl<http_stream>::value: \
|
||||
return get<http_stream>()->x; \
|
||||
case socket_type_int_impl<utp_stream>::value: \
|
||||
return get<utp_stream>()->x; \
|
||||
TORRENT_SOCKTYPE_I2P_FORWARD_RET(x, def) \
|
||||
TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \
|
||||
default: TORRENT_ASSERT(false); return def; \
|
||||
|
@ -133,36 +138,38 @@ namespace libtorrent
|
|||
struct socket_type_int_impl<http_stream>
|
||||
{ enum { value = 3 }; };
|
||||
|
||||
template <>
|
||||
struct socket_type_int_impl<utp_stream>
|
||||
{ enum { value = 4 }; };
|
||||
|
||||
#if TORRENT_USE_I2P
|
||||
template <>
|
||||
struct socket_type_int_impl<i2p_stream>
|
||||
{ enum { value = 4 }; };
|
||||
{ enum { value = 5 }; };
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
template <>
|
||||
struct socket_type_int_impl<ssl_stream<stream_socket> >
|
||||
{ enum { value = 5 }; };
|
||||
|
||||
template <>
|
||||
struct socket_type_int_impl<ssl_stream<socks5_stream> >
|
||||
{ enum { value = 6 }; };
|
||||
|
||||
template <>
|
||||
struct socket_type_int_impl<ssl_stream<http_stream> >
|
||||
struct socket_type_int_impl<ssl_stream<socks5_stream> >
|
||||
{ enum { value = 7 }; };
|
||||
|
||||
template <>
|
||||
struct socket_type_int_impl<ssl_stream<http_stream> >
|
||||
{ enum { value = 8 }; };
|
||||
#endif
|
||||
|
||||
struct TORRENT_EXPORT socket_type
|
||||
{
|
||||
typedef stream_socket::lowest_layer_type lowest_layer_type;
|
||||
typedef stream_socket::endpoint_type endpoint_type;
|
||||
typedef stream_socket::protocol_type protocol_type;
|
||||
|
||||
explicit socket_type(io_service& ios): m_io_service(ios), m_type(0) {}
|
||||
~socket_type();
|
||||
|
||||
lowest_layer_type& lowest_layer();
|
||||
io_service& get_io_service() const;
|
||||
bool is_open() const;
|
||||
|
||||
|
@ -253,10 +260,11 @@ namespace libtorrent
|
|||
|
||||
io_service& m_io_service;
|
||||
int m_type;
|
||||
enum { storage_size = max7<
|
||||
enum { storage_size = max8<
|
||||
sizeof(stream_socket)
|
||||
, sizeof(socks5_stream)
|
||||
, sizeof(http_stream)
|
||||
, sizeof(utp_stream)
|
||||
#if TORRENT_USE_I2P
|
||||
, sizeof(i2p_stream)
|
||||
#else
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2009, 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 TIMESTAMP_HISTORY_HPP
|
||||
#define TIMESTAMP_HISTORY_HPP
|
||||
|
||||
#include "boost/cstdint.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
// timestamp history keeps a history of the lowest timestamps we've
|
||||
// seen in the last 20 minutes
|
||||
struct timestamp_history
|
||||
{
|
||||
enum { history_size = 20 };
|
||||
|
||||
timestamp_history() : m_index(0), m_initialized(false), m_base(0), m_num_samples(0) {}
|
||||
bool initialized() const { return m_initialized; }
|
||||
|
||||
// add a sample to the timestamp history. If step is true, it's been
|
||||
// a minute since the last step
|
||||
boost::uint32_t add_sample(boost::uint32_t sample, bool step);
|
||||
boost::uint32_t base() const { TORRENT_ASSERT(m_initialized); return m_base; }
|
||||
void adjust_base(int change);
|
||||
|
||||
private:
|
||||
|
||||
// this is a circular buffer
|
||||
boost::uint32_t m_history[history_size];
|
||||
|
||||
// and this is the index we're currently at
|
||||
// in the circular buffer
|
||||
boost::uint16_t m_index;
|
||||
|
||||
bool m_initialized:1;
|
||||
|
||||
// this is the lowest sample seen in the
|
||||
// last 'history_size' minutes
|
||||
boost::uint32_t m_base;
|
||||
|
||||
// this is the number of samples since the
|
||||
// last time we stepped one minute. If we
|
||||
// don't have enough samples, we won't step
|
||||
int m_num_samples;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -91,6 +91,7 @@ namespace libtorrent
|
|||
struct tracker_request;
|
||||
struct add_torrent_params;
|
||||
struct storage_interface;
|
||||
struct bt_peer_connection;
|
||||
|
||||
namespace aux
|
||||
{
|
||||
|
@ -136,6 +137,15 @@ namespace libtorrent
|
|||
// it will initialize the storage and the piece-picker
|
||||
void init();
|
||||
|
||||
// find the peer that introduced us to the given endpoint. This is
|
||||
// used when trying to holepunch. We need the introducer so that we
|
||||
// can send a rendezvous connect message
|
||||
bt_peer_connection* find_introducer(tcp::endpoint const& ep) const;
|
||||
|
||||
// if we're connected to a peer at ep, return its peer connection
|
||||
// only count BitTorrent peers
|
||||
bt_peer_connection* find_peer(tcp::endpoint const& ep) const;
|
||||
|
||||
void on_resume_data_checked(int ret, disk_io_job const& j);
|
||||
void on_force_recheck(int ret, disk_io_job const& j);
|
||||
void on_piece_checked(int ret, disk_io_job const& j);
|
||||
|
@ -291,7 +301,7 @@ namespace libtorrent
|
|||
tcp::endpoint get_interface() const;
|
||||
|
||||
void connect_to_url_seed(std::list<web_seed_entry>::iterator url);
|
||||
bool connect_to_peer(policy::peer* peerinfo);
|
||||
bool connect_to_peer(policy::peer* peerinfo, bool ignore_limit = false);
|
||||
|
||||
void set_ratio(float r)
|
||||
{ TORRENT_ASSERT(r >= 0.0f); m_ratio = r; }
|
||||
|
|
|
@ -82,13 +82,31 @@ namespace libtorrent
|
|||
proxy_settings const& get_proxy_settings() { return m_proxy_settings; }
|
||||
|
||||
bool is_closed() const { return m_abort; }
|
||||
tcp::endpoint local_endpoint() const
|
||||
tcp::endpoint local_endpoint(error_code& ec) const
|
||||
{
|
||||
error_code ec;
|
||||
udp::endpoint ep = m_ipv4_sock.local_endpoint(ec);
|
||||
return tcp::endpoint(ep.address(), ep.port());
|
||||
}
|
||||
|
||||
void set_buf_size(int s);
|
||||
|
||||
template <class SocketOption>
|
||||
void set_option(SocketOption const& opt, error_code& ec)
|
||||
{
|
||||
m_ipv4_sock.set_option(opt, ec);
|
||||
#if TORRENT_USE_IPV6
|
||||
m_ipv6_sock.set_option(opt, ec);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class SocketOption>
|
||||
void get_option(SocketOption& opt, error_code& ec)
|
||||
{
|
||||
m_ipv4_sock.get_option(opt, ec);
|
||||
}
|
||||
|
||||
udp::endpoint proxy_addr() const { return m_proxy_addr; }
|
||||
|
||||
protected:
|
||||
|
||||
struct queued_packet
|
||||
|
@ -129,6 +147,8 @@ namespace libtorrent
|
|||
void wrap(char const* hostname, int port, char const* p, int len, error_code& ec);
|
||||
void unwrap(error_code const& e, char const* buf, int size);
|
||||
|
||||
void maybe_realloc_buffers();
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
#if defined BOOST_HAS_PTHREADS
|
||||
mutable pthread_t m_thread;
|
||||
|
@ -146,12 +166,14 @@ namespace libtorrent
|
|||
|
||||
udp::socket m_ipv4_sock;
|
||||
udp::endpoint m_v4_ep;
|
||||
char m_v4_buf[1600];
|
||||
int m_v4_buf_size;
|
||||
char* m_v4_buf;
|
||||
|
||||
#if TORRENT_USE_IPV6
|
||||
udp::socket m_ipv6_sock;
|
||||
udp::endpoint m_v6_ep;
|
||||
char m_v6_buf[1600];
|
||||
int m_v6_buf_size;
|
||||
char* m_v6_buf;
|
||||
#endif
|
||||
|
||||
int m_bind_port;
|
||||
|
@ -166,6 +188,11 @@ namespace libtorrent
|
|||
bool m_queue_packets;
|
||||
bool m_tunnel_packets;
|
||||
bool m_abort;
|
||||
// this is set to true to indicate that the m_v4_buf
|
||||
// and m_v6_buf should be reallocated to the size
|
||||
// of the buffer size members the next time their
|
||||
// read handler gets triggered
|
||||
bool m_reallocate_buffers;
|
||||
udp::endpoint m_proxy_addr;
|
||||
// while we're connecting to the proxy
|
||||
// we have to queue the packets, we'll flush
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2009, 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_UTP_SOCKET_MANAGER_HPP_INCLUDED
|
||||
#define TORRENT_UTP_SOCKET_MANAGER_HPP_INCLUDED
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/session_status.hpp"
|
||||
#include "libtorrent/enum_net.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class udp_socket;
|
||||
class utp_stream;
|
||||
struct utp_socket_impl;
|
||||
|
||||
typedef boost::function<void(boost::shared_ptr<socket_type> const&)> incoming_utp_callback_t;
|
||||
|
||||
struct utp_socket_manager
|
||||
{
|
||||
utp_socket_manager(session_settings const& sett, udp_socket& s, incoming_utp_callback_t cb);
|
||||
~utp_socket_manager();
|
||||
|
||||
void get_status(utp_status& s) const;
|
||||
|
||||
// return false if this is not a uTP packet
|
||||
bool incoming_packet(char const* p, int size, udp::endpoint const& ep);
|
||||
|
||||
void tick(ptime now);
|
||||
|
||||
tcp::endpoint local_endpoint(error_code& ec) const;
|
||||
|
||||
// flags for send_packet
|
||||
enum { dont_fragment = 1 };
|
||||
void send_packet(udp::endpoint const& ep, char const* p, int len
|
||||
, error_code& ec, int flags = 0);
|
||||
|
||||
// internal, used by utp_stream
|
||||
void remove_socket(boost::uint16_t id);
|
||||
|
||||
utp_socket_impl* new_utp_socket(utp_stream* str);
|
||||
int gain_factor() const { return m_sett.utp_gain_factor; }
|
||||
int target_delay() const { return m_sett.utp_target_delay * 1000; }
|
||||
int syn_resends() const { return m_sett.utp_syn_resends; }
|
||||
int fin_resends() const { return m_sett.utp_fin_resends; }
|
||||
int num_resends() const { return m_sett.utp_num_resends; }
|
||||
int connect_timeout() const { return m_sett.utp_connect_timeout; }
|
||||
int delayed_ack() const { return m_sett.utp_delayed_ack; }
|
||||
int min_timeout() const { return m_sett.utp_min_timeout; }
|
||||
bool allow_dynamic_sock_buf() const { return m_sett.utp_dynamic_sock_buf; }
|
||||
|
||||
void mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu);
|
||||
void set_sock_buf(int size);
|
||||
|
||||
private:
|
||||
udp_socket& m_sock;
|
||||
incoming_utp_callback_t m_cb;
|
||||
|
||||
// replace with a hash-map
|
||||
typedef std::multimap<boost::uint16_t, utp_socket_impl*> socket_map_t;
|
||||
socket_map_t m_utp_sockets;
|
||||
|
||||
// the last socket we received a packet on
|
||||
utp_socket_impl* m_last_socket;
|
||||
|
||||
int m_new_connection;
|
||||
|
||||
session_settings const& m_sett;
|
||||
|
||||
// this is a copy of the routing table, used
|
||||
// to initialize MTU sizes of uTP sockets
|
||||
std::vector<ip_route> m_routes;
|
||||
|
||||
// the timestamp for the last time we updated
|
||||
// the routing table
|
||||
ptime m_last_route_update;
|
||||
|
||||
// the buffer size of the socket. This is used
|
||||
// to now lower the buffer size
|
||||
int m_sock_buf_size;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2009, 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_UTP_STREAM_HPP_INCLUDED
|
||||
#define TORRENT_UTP_STREAM_HPP_INCLUDED
|
||||
|
||||
#include "libtorrent/connection_queue.hpp"
|
||||
#include "libtorrent/proxy_base.hpp"
|
||||
#include "libtorrent/udp_socket.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/packet_buffer.hpp"
|
||||
#include "libtorrent/error_code.hpp"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function/function1.hpp>
|
||||
#include <boost/function/function2.hpp>
|
||||
|
||||
#define CCONTROL_TARGET 100
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
struct utp_socket_manager;
|
||||
|
||||
// some MTU and protocol header sizes constants
|
||||
enum
|
||||
{
|
||||
TORRENT_IPV4_HEADER = 20,
|
||||
TORRENT_IPV6_HEADER = 40,
|
||||
TORRENT_UDP_HEADER = 8,
|
||||
TORRENT_SOCKS5_HEADER = 6, // plus the size of the destination address
|
||||
|
||||
TORRENT_ETHERNET_MTU = 1500,
|
||||
TORRENT_TEREDO_MTU = 1280,
|
||||
TORRENT_INET_MIN_MTU = 576,
|
||||
TORRENT_INET_MAX_MTU = 0xffff
|
||||
};
|
||||
|
||||
// the point of the bif_endian_int is two-fold
|
||||
// one purpuse is to not have any alignment requirements
|
||||
// so that any byffer received from the network can be cast
|
||||
// to it and read as an integer of various sizes without
|
||||
// triggering a bus error. The other purpose is to convert
|
||||
// from network byte order to host byte order when read and
|
||||
// written, to offer a convenient interface to both interpreting
|
||||
// and writing network packets
|
||||
template <class T> struct big_endian_int
|
||||
{
|
||||
big_endian_int& operator=(T v)
|
||||
{
|
||||
char* p = m_storage;
|
||||
detail::write_impl(v, p);
|
||||
return *this;
|
||||
}
|
||||
operator T() const
|
||||
{
|
||||
const char* p = m_storage;
|
||||
return detail::read_impl(p, detail::type<T>());
|
||||
}
|
||||
private:
|
||||
char m_storage[sizeof(T)];
|
||||
};
|
||||
|
||||
typedef big_endian_int<boost::uint64_t> be_uint64;
|
||||
typedef big_endian_int<boost::uint32_t> be_uint32;
|
||||
typedef big_endian_int<boost::uint16_t> be_uint16;
|
||||
typedef big_endian_int<boost::int64_t> be_int64;
|
||||
typedef big_endian_int<boost::int32_t> be_int32;
|
||||
typedef big_endian_int<boost::int16_t> be_int16;
|
||||
|
||||
/*
|
||||
uTP header from BEP 29
|
||||
|
||||
0 4 8 16 24 32
|
||||
+-------+-------+---------------+---------------+---------------+
|
||||
| type | ver | extension | connection_id |
|
||||
+-------+-------+---------------+---------------+---------------+
|
||||
| timestamp_microseconds |
|
||||
+---------------+---------------+---------------+---------------+
|
||||
| timestamp_difference_microseconds |
|
||||
+---------------+---------------+---------------+---------------+
|
||||
| wnd_size |
|
||||
+---------------+---------------+---------------+---------------+
|
||||
| seq_nr | ack_nr |
|
||||
+---------------+---------------+---------------+---------------+
|
||||
|
||||
*/
|
||||
|
||||
enum type { ST_DATA = 0, ST_FIN, ST_STATE, ST_RESET, ST_SYN, NUM_TYPES };
|
||||
|
||||
struct utp_header
|
||||
{
|
||||
unsigned char type_ver;
|
||||
unsigned char extension;
|
||||
be_uint16 connection_id;
|
||||
be_uint32 timestamp_microseconds;
|
||||
be_uint32 timestamp_difference_microseconds;
|
||||
be_uint32 wnd_size;
|
||||
be_uint16 seq_nr;
|
||||
be_uint16 ack_nr;
|
||||
|
||||
int get_type() const { return type_ver >> 4; }
|
||||
int get_version() const { return type_ver & 0xf; }
|
||||
};
|
||||
|
||||
struct utp_socket_impl;
|
||||
|
||||
utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id
|
||||
, boost::uint16_t send_id, void* userdata
|
||||
, utp_socket_manager* sm);
|
||||
void detach_utp_impl(utp_socket_impl* s);
|
||||
void delete_utp_impl(utp_socket_impl* s);
|
||||
bool should_delete(utp_socket_impl* s);
|
||||
void tick_utp_impl(utp_socket_impl* s, ptime const& now);
|
||||
void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu);
|
||||
bool utp_incoming_packet(utp_socket_impl* s, char const* p
|
||||
, int size, udp::endpoint const& ep, ptime receive_time);
|
||||
bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id);
|
||||
udp::endpoint utp_remote_endpoint(utp_socket_impl* s);
|
||||
boost::uint16_t utp_receive_id(utp_socket_impl* s);
|
||||
int utp_socket_state(utp_socket_impl const* s);
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
int socket_impl_size();
|
||||
#endif
|
||||
|
||||
// this is the user-level stream interface to utp sockets.
|
||||
// the reason why it's split up in a utp_stream class and
|
||||
// an implementation class is because the socket state has
|
||||
// to be able to out-live the user level socket. For instance
|
||||
// when sending data on a stream and then closing it, the
|
||||
// state holding the send buffer has to be kept around until
|
||||
// it has been flushed, which may be longer than the client
|
||||
// will keep the utp_stream object around for.
|
||||
// for more details, see utp_socket_impl, which is analogous
|
||||
// to the kernel state for a socket. It's defined in utp_stream.cpp
|
||||
class utp_stream
|
||||
{
|
||||
public:
|
||||
|
||||
typedef stream_socket::endpoint_type endpoint_type;
|
||||
typedef stream_socket::protocol_type protocol_type;
|
||||
|
||||
explicit utp_stream(asio::io_service& io_service);
|
||||
~utp_stream();
|
||||
|
||||
// used for incoming connections
|
||||
void set_impl(utp_socket_impl* s);
|
||||
utp_socket_impl* get_impl();
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
template <class IO_Control_Command>
|
||||
void io_control(IO_Control_Command& ioc) {}
|
||||
#endif
|
||||
|
||||
template <class IO_Control_Command>
|
||||
void io_control(IO_Control_Command& ioc, error_code& ec) {}
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
void bind(endpoint_type const& endpoint) {}
|
||||
#endif
|
||||
|
||||
void bind(endpoint_type const& endpoint, error_code& ec);
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
template <class SettableSocketOption>
|
||||
void set_option(SettableSocketOption const& opt) {}
|
||||
#endif
|
||||
|
||||
template <class SettableSocketOption>
|
||||
error_code set_option(SettableSocketOption const& opt, error_code& ec) { return ec; }
|
||||
|
||||
void close();
|
||||
void close(error_code const& ec) { close(); }
|
||||
bool is_open() const { return m_open; }
|
||||
|
||||
int read_buffer_size() const;
|
||||
static void on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill);
|
||||
static void on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill);
|
||||
static void on_connect(void* self, error_code const& ec, bool kill);
|
||||
|
||||
typedef void(*handler_t)(void*, size_t, error_code const&, bool);
|
||||
typedef void(*connect_handler_t)(void*, error_code const&, bool);
|
||||
|
||||
void add_read_buffer(void* buf, size_t len);
|
||||
void set_read_handler(handler_t h);
|
||||
void add_write_buffer(void const* buf, size_t len);
|
||||
void set_write_handler(handler_t h);
|
||||
size_t read_some(bool clear_buffers);
|
||||
|
||||
void do_connect(tcp::endpoint const& ep, connect_handler_t h);
|
||||
|
||||
endpoint_type local_endpoint() const
|
||||
{
|
||||
error_code ec;
|
||||
return local_endpoint(ec);
|
||||
}
|
||||
|
||||
endpoint_type local_endpoint(error_code& ec) const;
|
||||
|
||||
endpoint_type remote_endpoint() const
|
||||
{
|
||||
error_code ec;
|
||||
return remote_endpoint(ec);
|
||||
}
|
||||
|
||||
endpoint_type remote_endpoint(error_code& ec) const;
|
||||
|
||||
std::size_t available() const;
|
||||
std::size_t available(error_code& ec) const { return available(); }
|
||||
|
||||
asio::io_service& io_service()
|
||||
{ return m_io_service; }
|
||||
|
||||
template <class Handler>
|
||||
void async_connect(endpoint_type const& endpoint, Handler const& handler)
|
||||
{
|
||||
if (!endpoint.address().is_v4())
|
||||
{
|
||||
error_code ec = asio::error::operation_not_supported;
|
||||
m_io_service.post(boost::bind<void>(handler, asio::error::operation_not_supported, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_impl == 0)
|
||||
{
|
||||
m_io_service.post(boost::bind<void>(handler, asio::error::not_connected, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
m_connect_handler = handler;
|
||||
do_connect(endpoint, &utp_stream::on_connect);
|
||||
}
|
||||
|
||||
template <class Mutable_Buffers, class Handler>
|
||||
void async_read_some(Mutable_Buffers const& buffers, Handler const& handler)
|
||||
{
|
||||
if (m_impl == 0)
|
||||
{
|
||||
m_io_service.post(boost::bind<void>(handler, asio::error::not_connected, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(!m_read_handler);
|
||||
if (m_read_handler)
|
||||
{
|
||||
m_io_service.post(boost::bind<void>(handler, asio::error::operation_not_supported, 0));
|
||||
return;
|
||||
}
|
||||
for (typename Mutable_Buffers::const_iterator i = buffers.begin()
|
||||
, end(buffers.end()); i != end; ++i)
|
||||
{
|
||||
TORRENT_ASSERT(buffer_size(*i) > 0);
|
||||
using asio::buffer_cast;
|
||||
using asio::buffer_size;
|
||||
add_read_buffer(buffer_cast<void*>(*i), buffer_size(*i));
|
||||
}
|
||||
m_read_handler = handler;
|
||||
set_read_handler(&utp_stream::on_read);
|
||||
}
|
||||
|
||||
void do_async_connect(endpoint_type const& ep
|
||||
, boost::function<void(error_code const&)> const& handler);
|
||||
|
||||
template <class Protocol>
|
||||
void open(Protocol const& p, error_code& ec)
|
||||
{ m_open = true; }
|
||||
|
||||
template <class Protocol>
|
||||
void open(Protocol const& p)
|
||||
{ m_open = true; }
|
||||
|
||||
template <class Mutable_Buffers>
|
||||
std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec)
|
||||
{
|
||||
TORRENT_ASSERT(!m_read_handler);
|
||||
if (m_impl == 0)
|
||||
{
|
||||
ec = asio::error::not_connected;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read_buffer_size() == 0)
|
||||
{
|
||||
ec = asio::error::would_block;
|
||||
return 0;
|
||||
}
|
||||
#ifdef TORRENT_DEBUG
|
||||
int buf_size = 0;
|
||||
#endif
|
||||
|
||||
for (typename Mutable_Buffers::const_iterator i = buffers.begin()
|
||||
, end(buffers.end()); i != end; ++i)
|
||||
{
|
||||
using asio::buffer_cast;
|
||||
using asio::buffer_size;
|
||||
add_read_buffer(buffer_cast<void*>(*i), buffer_size(*i));
|
||||
#ifdef TORRENT_DEBUG
|
||||
buf_size += buffer_size(*i);
|
||||
#endif
|
||||
}
|
||||
std::size_t ret = read_some(true);
|
||||
TORRENT_ASSERT(int(ret) <= buf_size);
|
||||
TORRENT_ASSERT(ret > 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class Const_Buffers>
|
||||
std::size_t write_some(Const_Buffers const& buffers, error_code& ec)
|
||||
{
|
||||
// TODO: implement
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Const_Buffers, class Handler>
|
||||
void async_write_some(Const_Buffers const& buffers, Handler const& handler)
|
||||
{
|
||||
if (m_impl == 0)
|
||||
{
|
||||
m_io_service.post(boost::bind<void>(handler, asio::error::not_connected, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(!m_write_handler);
|
||||
if (m_write_handler)
|
||||
{
|
||||
m_io_service.post(boost::bind<void>(handler, asio::error::operation_not_supported, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
for (typename Const_Buffers::const_iterator i = buffers.begin()
|
||||
, end(buffers.end()); i != end; ++i)
|
||||
{
|
||||
TORRENT_ASSERT(buffer_size(*i) > 0);
|
||||
using asio::buffer_cast;
|
||||
using asio::buffer_size;
|
||||
add_write_buffer((void*)buffer_cast<void const*>(*i), buffer_size(*i));
|
||||
}
|
||||
m_write_handler = handler;
|
||||
set_write_handler(&utp_stream::on_write);
|
||||
}
|
||||
|
||||
//private:
|
||||
|
||||
void cancel_handlers(error_code const&);
|
||||
|
||||
boost::function1<void, error_code const&> m_connect_handler;
|
||||
boost::function2<void, error_code const&, std::size_t> m_read_handler;
|
||||
boost::function2<void, error_code const&, std::size_t> m_write_handler;
|
||||
|
||||
asio::io_service& m_io_service;
|
||||
utp_socket_impl* m_impl;
|
||||
bool m_open;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -83,6 +83,12 @@ for l in f:
|
|||
if 'std::string::append' in fun: fold = indentation
|
||||
if 'getipnodebyname' == fun: fold = indentation
|
||||
if '__gnu_debug::_Safe_iterator<std::' in fun: fold = indentation
|
||||
if 'fflush' == fun: fold = indentation
|
||||
if 'vfprintf' == fun: fold = indentation
|
||||
if 'fprintf' == fun: fold = indentation
|
||||
if 'BN_mod_exp' == fun: fold = indentation
|
||||
if 'BN_CTX_free' == fun: fold = indentation
|
||||
if 'cerror' == fun: fold = indentation
|
||||
|
||||
list = []
|
||||
for k in fun_samples:
|
||||
|
|
|
@ -38,6 +38,8 @@ substitute_file('docs/manual.rst')
|
|||
substitute_file('docs/building.rst')
|
||||
substitute_file('docs/features.rst')
|
||||
substitute_file('docs/contributing.rst')
|
||||
substitute_file('docs/utp.rst')
|
||||
substitute_file('docs/make_torrent.rst')
|
||||
substitute_file('docs/tuning.rst')
|
||||
substitute_file('Jamfile')
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ libtorrent_rasterbar_la_SOURCES = \
|
|||
pe_crypto.cpp \
|
||||
peer_connection.cpp \
|
||||
piece_picker.cpp \
|
||||
packet_buffer.cpp \
|
||||
policy.cpp \
|
||||
puff.cpp \
|
||||
session.cpp \
|
||||
|
@ -76,12 +77,15 @@ libtorrent_rasterbar_la_SOURCES = \
|
|||
torrent_handle.cpp \
|
||||
torrent_info.cpp \
|
||||
time.cpp \
|
||||
timestamp_history.cpp \
|
||||
tracker_manager.cpp \
|
||||
udp_socket.cpp \
|
||||
udp_tracker_connection.cpp \
|
||||
upnp.cpp \
|
||||
ut_metadata.cpp \
|
||||
ut_pex.cpp \
|
||||
utp_socket_manager.cpp \
|
||||
utp_stream.cpp \
|
||||
web_peer_connection.cpp \
|
||||
\
|
||||
$(KADEMLIA_SOURCES) \
|
||||
|
|
|
@ -108,6 +108,18 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
TORRENT_EXPORT bool is_teredo(address const& addr)
|
||||
{
|
||||
#if TORRENT_USE_IPV6
|
||||
if (!addr.is_v6()) return false;
|
||||
boost::uint8_t teredo_prefix[] = {0x20, 0x01, 0, 0};
|
||||
address_v6::bytes_type b = addr.to_v6().to_bytes();
|
||||
return memcmp(&b[0], teredo_prefix, 4) == 0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool supports_ipv6()
|
||||
{
|
||||
#if TORRENT_USE_IPV6
|
||||
|
|
|
@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/invariant_check.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
#include "libtorrent/socket_io.hpp"
|
||||
#include "libtorrent/version.hpp"
|
||||
#include "libtorrent/extensions.hpp"
|
||||
#include "libtorrent/aux_/session_impl.hpp"
|
||||
|
@ -258,7 +259,7 @@ namespace libtorrent
|
|||
write_bitfield();
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
if (m_supports_dht_port && m_ses.m_dht)
|
||||
write_dht_port(m_ses.get_dht_settings().service_port);
|
||||
write_dht_port(m_ses.m_external_udp_port);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -390,8 +391,9 @@ namespace libtorrent
|
|||
if (is_queued()) p.flags |= peer_info::queued;
|
||||
|
||||
p.client = m_client_version;
|
||||
p.connection_type = peer_info::standard_bittorrent;
|
||||
|
||||
p.connection_type = get_socket()->get<utp_stream>()
|
||||
? peer_info::bittorrent_utp
|
||||
: peer_info::standard_bittorrent;
|
||||
}
|
||||
|
||||
bool bt_peer_connection::in_handshake() const
|
||||
|
@ -657,6 +659,7 @@ namespace libtorrent
|
|||
|
||||
void bt_peer_connection::append_const_send_buffer(char const* buffer, int size)
|
||||
{
|
||||
TORRENT_ASSERT(!m_rc4_encrypted || send_buffer_size() == m_encrypted_bytes);
|
||||
// if we're encrypting this buffer, we need to make a copy
|
||||
// since we'll mutate it
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
|
@ -1349,7 +1352,7 @@ namespace libtorrent
|
|||
m_supports_dht_port = true;
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
if (m_supports_dht_port && m_ses.m_dht)
|
||||
write_dht_port(m_ses.get_dht_settings().service_port);
|
||||
write_dht_port(m_ses.m_external_udp_port);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1443,6 +1446,203 @@ namespace libtorrent
|
|||
incoming_allowed_fast(index);
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// -------- RENDEZVOUS ---------
|
||||
// -----------------------------
|
||||
|
||||
void bt_peer_connection::on_holepunch()
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
if (!packet_finished()) return;
|
||||
|
||||
// we can't accept holepunch messages from peers
|
||||
// that don't support the holepunch extension
|
||||
// because we wouldn't be able to respond
|
||||
if (m_holepunch_id == 0) return;
|
||||
|
||||
buffer::const_interval recv_buffer = receive_buffer();
|
||||
TORRENT_ASSERT(*recv_buffer.begin == msg_extended);
|
||||
++recv_buffer.begin;
|
||||
TORRENT_ASSERT(*recv_buffer.begin == holepunch_msg);
|
||||
++recv_buffer.begin;
|
||||
|
||||
const char* ptr = recv_buffer.begin;
|
||||
|
||||
// ignore invalid messages
|
||||
if (recv_buffer.left() < 2) return;
|
||||
|
||||
int msg_type = detail::read_uint8(ptr);
|
||||
int addr_type = detail::read_uint8(ptr);
|
||||
|
||||
tcp::endpoint ep;
|
||||
|
||||
if (addr_type == 0)
|
||||
{
|
||||
if (recv_buffer.left() < 2 + 4 + 2) return;
|
||||
// IPv4 address
|
||||
ep = detail::read_v4_endpoint<tcp::endpoint>(ptr);
|
||||
}
|
||||
#if TORRENT_USE_IPV6
|
||||
else if (addr_type == 1)
|
||||
{
|
||||
// IPv6 address
|
||||
if (recv_buffer.left() < 2 + 18 + 2) return;
|
||||
ep = detail::read_v6_endpoint<tcp::endpoint>(ptr);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
error_code ec;
|
||||
static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"};
|
||||
(*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:"
|
||||
<< (msg_type >= 0 && msg_type < 3 ? hp_msg_name[msg_type] : "unknown message type")
|
||||
<< " from:" << remote().address().to_string(ec)
|
||||
<< " to: unknown address type ]\n";
|
||||
#endif
|
||||
|
||||
return; // unknown address type
|
||||
}
|
||||
|
||||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
if (!t) return;
|
||||
|
||||
switch (msg_type)
|
||||
{
|
||||
case hp_rendezvous: // rendezvous
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
error_code ec;
|
||||
(*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:rendezvous"
|
||||
<< " to:" << ep.address().to_string(ec) << " ]\n";
|
||||
#endif
|
||||
// this peer is asking us to introduce it to
|
||||
// the peer at 'ep'. We need to find which of
|
||||
// our connections points to that endpoint
|
||||
bt_peer_connection* p = t->find_peer(ep);
|
||||
if (p == 0)
|
||||
{
|
||||
// we're not connected to this peer
|
||||
write_holepunch_msg(hp_failed, ep, hp_not_connected);
|
||||
break;
|
||||
}
|
||||
if (!p->supports_holepunch())
|
||||
{
|
||||
write_holepunch_msg(hp_failed, ep, hp_no_support);
|
||||
break;
|
||||
}
|
||||
if (p == this)
|
||||
{
|
||||
write_holepunch_msg(hp_failed, ep, hp_no_self);
|
||||
break;
|
||||
}
|
||||
|
||||
write_holepunch_msg(hp_connect, ep, 0);
|
||||
p->write_holepunch_msg(hp_connect, remote(), 0);
|
||||
} break;
|
||||
case hp_connect:
|
||||
{
|
||||
// add or find the peer with this endpoint
|
||||
policy::peer* p = t->get_policy().add_peer(ep, peer_id(0), peer_info::pex, 0);
|
||||
if (p == 0 || p->connection)
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
error_code ec;
|
||||
(*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:connect"
|
||||
<< " to:" << ep.address().to_string(ec) << " error:failed to add peer ]\n";
|
||||
#endif
|
||||
// we either couldn't add this peer, or it's
|
||||
// already connected. Just ignore the connect message
|
||||
break;
|
||||
}
|
||||
if (p->banned)
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
error_code ec;
|
||||
(*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:connect"
|
||||
<< " to:" << ep.address().to_string(ec) << " error:peer banned ]\n";
|
||||
#endif
|
||||
// this peer is banned, don't connect to it
|
||||
break;
|
||||
|
||||
}
|
||||
// to make sure we use the uTP protocol
|
||||
p->supports_utp = true;
|
||||
// #error make sure we make this a connection candidate
|
||||
// in case it has too many failures for instance
|
||||
t->connect_to_peer(p, true);
|
||||
// mark this connection to be in holepunch mode
|
||||
// so that it will retry faster and stick to uTP while it's
|
||||
// retrying
|
||||
if (p->connection)
|
||||
p->connection->set_holepunch_mode();
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
error_code ec;
|
||||
(*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:connect"
|
||||
<< " to:" << ep.address().to_string(ec) << " ]\n";
|
||||
#endif
|
||||
} break;
|
||||
case hp_failed:
|
||||
{
|
||||
boost::uint32_t error = detail::read_uint32(ptr);
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
error_code ec;
|
||||
char const* err_msg[] = {"no such peer", "not connected", "no support", "no self"};
|
||||
(*m_logger) << time_now_string() << " <== HOLEPUNCH [ msg:failed"
|
||||
" error:" << error <<
|
||||
" msg:" << ((error >= 0 && error < 4)?err_msg[error]:"unknown message id") <<
|
||||
" ]\n";
|
||||
#endif
|
||||
// #error deal with holepunch errors
|
||||
} break;
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
default:
|
||||
{
|
||||
error_code ec;
|
||||
(*m_logger) << time_now_string() << " <== HOLEPUNCH ["
|
||||
" msg:unknown message type (" << msg_type << ")"
|
||||
<< " to:" << ep.address().to_string(ec) << " ]\n";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void bt_peer_connection::write_holepunch_msg(int type, tcp::endpoint const& ep, int error)
|
||||
{
|
||||
char buf[35];
|
||||
char* ptr = buf + 6;
|
||||
detail::write_uint8(type, ptr);
|
||||
if (ep.address().is_v4()) detail::write_uint8(0, ptr);
|
||||
else detail::write_uint8(1, ptr);
|
||||
detail::write_endpoint(ep, ptr);
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
error_code ec;
|
||||
static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"};
|
||||
static const char* hp_error_string[] = {"", "no such peer", "not connected", "no support", "no self"};
|
||||
(*m_logger) << time_now_string() << " ==> HOLEPUNCH [ msg:"
|
||||
<< (type >= 0 && type < 3 ? hp_msg_name[type] : "unknown message type")
|
||||
<< " to:" << ep.address().to_string(ec)
|
||||
<< " error:" << hp_error_string[error]
|
||||
<< " ]\n";
|
||||
#endif
|
||||
if (type == hp_failed)
|
||||
{
|
||||
detail::write_uint32(error, ptr);
|
||||
}
|
||||
|
||||
// write the packet length and type
|
||||
char* hdr = buf;
|
||||
detail::write_uint32(ptr - buf - 4, hdr);
|
||||
detail::write_uint8(msg_extended, hdr);
|
||||
detail::write_uint8(m_holepunch_id, hdr);
|
||||
|
||||
TORRENT_ASSERT(ptr <= buf + sizeof(buf));
|
||||
|
||||
send_buffer(buf, ptr - buf);
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// --------- EXTENDED ----------
|
||||
// -----------------------------
|
||||
|
@ -1479,6 +1679,31 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
|
||||
if (extended_id == upload_only_msg)
|
||||
{
|
||||
if (!packet_finished()) return;
|
||||
bool ul = detail::read_uint8(recv_buffer.begin);
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string() << " <== UPLOAD_ONLY [ " << (ul?"true":"false") << " ]\n";
|
||||
#endif
|
||||
set_upload_only(ul);
|
||||
return;
|
||||
}
|
||||
|
||||
if (extended_id == holepunch_msg)
|
||||
{
|
||||
if (!packet_finished()) return;
|
||||
on_holepunch();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
if (packet_finished())
|
||||
(*m_logger) << time_now_string() << " <== EXTENSION MESSAGE ["
|
||||
" msg:" << extended_id <<
|
||||
" size:" << packet_size() << " ]\n";
|
||||
#endif
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
for (extension_list_t::iterator i = m_extensions.begin()
|
||||
, end(m_extensions.end()); i != end; ++i)
|
||||
|
@ -1549,7 +1774,10 @@ namespace libtorrent
|
|||
|
||||
// upload_only
|
||||
if (lazy_entry const* m = root.dict_find_dict("m"))
|
||||
{
|
||||
m_upload_only_id = m->dict_find_int_value("upload_only", 0);
|
||||
m_holepunch_id = m->dict_find_int_value("ut_holepunch", 0);
|
||||
}
|
||||
|
||||
// there is supposed to be a remote listen port
|
||||
int listen_port = root.dict_find_int_value("p");
|
||||
|
@ -1929,7 +2157,9 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(t);
|
||||
|
||||
m["upload_only"] = upload_only_msg;
|
||||
m["ut_holepunch"] = holepunch_msg;
|
||||
m["share_mode"] = share_mode_msg;
|
||||
|
||||
int complete_ago = -1;
|
||||
if (t->last_seen_complete() > 0) complete_ago = t->time_since_complete();
|
||||
handshake["complete_ago"] = complete_ago;
|
||||
|
@ -1968,6 +2198,19 @@ namespace libtorrent
|
|||
(*i)->add_handshake(handshake);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// make sure there are not conflicting extensions
|
||||
std::set<int> ext;
|
||||
for (entry::dictionary_type::const_iterator i = m.begin()
|
||||
, end(m.end()); i != end; ++i)
|
||||
{
|
||||
if (i->second.type() != entry::int_t) continue;
|
||||
int val = i->second.integer();
|
||||
TORRENT_ASSERT(ext.find(val) == ext.end());
|
||||
ext.insert(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<char> msg;
|
||||
bencode(std::back_inserter(msg), handshake);
|
||||
|
||||
|
@ -1986,9 +2229,9 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(i.begin == i.end);
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING && TORRENT_USE_IOSTREAM
|
||||
std::stringstream ext;
|
||||
handshake.print(ext);
|
||||
(*m_logger) << time_now_string() << " ==> EXTENDED HANDSHAKE: \n" << ext.str();
|
||||
std::stringstream handshake_str;
|
||||
handshake.print(handshake_str);
|
||||
(*m_logger) << time_now_string() << " ==> EXTENDED HANDSHAKE: \n" << handshake_str.str();
|
||||
#endif
|
||||
|
||||
setup_send();
|
||||
|
@ -2198,6 +2441,8 @@ namespace libtorrent
|
|||
(*m_logger) << time_now_string() << " received DH key\n";
|
||||
#endif
|
||||
|
||||
TORRENT_ASSERT(!m_rc4_encrypted || send_buffer_size() == m_encrypted_bytes);
|
||||
|
||||
// PadA/B can be a max of 512 bytes, and 20 bytes more for
|
||||
// the sync hash (if incoming), or 8 bytes more for the
|
||||
// encrypted verification constant (if outgoing). Instead
|
||||
|
@ -2981,7 +3226,7 @@ namespace libtorrent
|
|||
write_bitfield();
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
if (m_supports_dht_port && m_ses.m_dht)
|
||||
write_dht_port(m_ses.get_dht_settings().service_port);
|
||||
write_dht_port(m_ses.m_external_udp_port);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
368
src/enum_net.cpp
368
src/enum_net.cpp
|
@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/config.hpp"
|
||||
#include <boost/bind.hpp>
|
||||
#include <vector>
|
||||
#include <stdlib.h> // for wcstombscstombs
|
||||
#include "libtorrent/enum_net.hpp"
|
||||
#include "libtorrent/broadcast_socket.hpp"
|
||||
#include "libtorrent/error_code.hpp"
|
||||
|
@ -84,31 +85,44 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace libtorrent { namespace
|
||||
{
|
||||
|
||||
address inaddr_to_address(in_addr const* ina)
|
||||
address inaddr_to_address(in_addr const* ina, int len = 4)
|
||||
{
|
||||
typedef asio::ip::address_v4::bytes_type bytes_t;
|
||||
bytes_t b;
|
||||
std::memcpy(&b[0], ina, b.size());
|
||||
std::memset(&b[0], 0, b.size());
|
||||
if (len > 0) std::memcpy(&b[0], ina, (std::min)(len, int(b.size())));
|
||||
return address_v4(b);
|
||||
}
|
||||
|
||||
#if TORRENT_USE_IPV6
|
||||
address inaddr6_to_address(in6_addr const* ina6)
|
||||
address inaddr6_to_address(in6_addr const* ina6, int len = 16)
|
||||
{
|
||||
typedef asio::ip::address_v6::bytes_type bytes_t;
|
||||
bytes_t b;
|
||||
std::memcpy(&b[0], ina6, b.size());
|
||||
std::memset(&b[0], 0, b.size());
|
||||
if (len > 0) std::memcpy(&b[0], ina6, (std::min)(len, int(b.size())));
|
||||
return address_v6(b);
|
||||
}
|
||||
#endif
|
||||
|
||||
address sockaddr_to_address(sockaddr const* sin)
|
||||
int sockaddr_len(sockaddr const* sin)
|
||||
{
|
||||
if (sin->sa_family == AF_INET)
|
||||
return inaddr_to_address(&((sockaddr_in const*)sin)->sin_addr);
|
||||
#if defined TORRENT_WINDOWS || TORRENT_MINGW || defined TORRENT_LINUX
|
||||
return sin->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
|
||||
#else
|
||||
return sin->sa_len;
|
||||
#endif
|
||||
}
|
||||
|
||||
address sockaddr_to_address(sockaddr const* sin, int assume_family = -1)
|
||||
{
|
||||
if (sin->sa_family == AF_INET || assume_family == AF_INET)
|
||||
return inaddr_to_address(&((sockaddr_in const*)sin)->sin_addr
|
||||
, sockaddr_len(sin) - offsetof(sockaddr, sa_data));
|
||||
#if TORRENT_USE_IPV6
|
||||
else if (sin->sa_family == AF_INET6)
|
||||
return inaddr6_to_address(&((sockaddr_in6 const*)sin)->sin6_addr);
|
||||
else if (sin->sa_family == AF_INET6 || assume_family == AF_INET6)
|
||||
return inaddr6_to_address(&((sockaddr_in6 const*)sin)->sin6_addr
|
||||
, sockaddr_len(sin) - offsetof(sockaddr, sa_data));
|
||||
#endif
|
||||
return address();
|
||||
}
|
||||
|
@ -142,13 +156,15 @@ namespace libtorrent { namespace
|
|||
return msg_len;
|
||||
}
|
||||
|
||||
bool parse_route(nlmsghdr* nl_hdr, ip_route* rt_info)
|
||||
bool parse_route(int s, nlmsghdr* nl_hdr, ip_route* rt_info)
|
||||
{
|
||||
rtmsg* rt_msg = (rtmsg*)NLMSG_DATA(nl_hdr);
|
||||
|
||||
if((rt_msg->rtm_family != AF_INET) || (rt_msg->rtm_table != RT_TABLE_MAIN))
|
||||
if((rt_msg->rtm_family != AF_INET && rt_msg->rtm_family != AF_INET6) || (rt_msg->rtm_table != RT_TABLE_MAIN
|
||||
&& rt_msg->rtm_table != RT_TABLE_LOCAL))
|
||||
return false;
|
||||
|
||||
int if_index = 0;
|
||||
int rt_len = RTM_PAYLOAD(nl_hdr);
|
||||
for (rtattr* rt_attr = (rtattr*)RTM_RTA(rt_msg);
|
||||
RTA_OK(rt_attr,rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len))
|
||||
|
@ -156,23 +172,52 @@ namespace libtorrent { namespace
|
|||
switch(rt_attr->rta_type)
|
||||
{
|
||||
case RTA_OIF:
|
||||
if_indextoname(*(int*)RTA_DATA(rt_attr), rt_info->name);
|
||||
if_index = *(int*)RTA_DATA(rt_attr);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
rt_info->gateway = address_v4(ntohl(*(u_int*)RTA_DATA(rt_attr)));
|
||||
#if TORRENT_USE_IPV6
|
||||
if (rt_msg->rtm_family == AF_INET6)
|
||||
{
|
||||
rt_info->gateway = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
rt_info->gateway = inaddr_to_address((in_addr*)RTA_DATA(rt_attr));
|
||||
}
|
||||
break;
|
||||
case RTA_DST:
|
||||
rt_info->destination = address_v4(ntohl(*(u_int*)RTA_DATA(rt_attr)));
|
||||
#if TORRENT_USE_IPV6
|
||||
if (rt_msg->rtm_family == AF_INET6)
|
||||
{
|
||||
rt_info->destination = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
rt_info->destination = inaddr_to_address((in_addr*)RTA_DATA(rt_attr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if_indextoname(if_index, rt_info->name);
|
||||
ifreq req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
if_indextoname(if_index, req.ifr_name);
|
||||
ioctl(s, SIOCGIFMTU, &req);
|
||||
rt_info->mtu = req.ifr_mtu;
|
||||
// obviously this doesn't work correctly. How do you get the netmask for a route?
|
||||
// if (ioctl(s, SIOCGIFNETMASK, &req) == 0) {
|
||||
// rt_info->netmask = sockaddr_to_address(&req.ifr_addr, req.ifr_addr.sa_family);
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined TORRENT_BSD
|
||||
|
||||
bool parse_route(rt_msghdr* rtm, ip_route* rt_info)
|
||||
bool parse_route(int s, rt_msghdr* rtm, ip_route* rt_info)
|
||||
{
|
||||
sockaddr* rti_info[RTAX_MAX];
|
||||
sockaddr* sa = (sockaddr*)(rtm + 1);
|
||||
|
@ -205,9 +250,17 @@ namespace libtorrent { namespace
|
|||
return false;
|
||||
|
||||
rt_info->gateway = sockaddr_to_address(rti_info[RTAX_GATEWAY]);
|
||||
rt_info->netmask = sockaddr_to_address(rti_info[RTAX_NETMASK]);
|
||||
rt_info->destination = sockaddr_to_address(rti_info[RTAX_DST]);
|
||||
rt_info->netmask = sockaddr_to_address(rti_info[RTAX_NETMASK]
|
||||
, rt_info->destination.is_v4() ? AF_INET : AF_INET6);
|
||||
if_indextoname(rtm->rtm_index, rt_info->name);
|
||||
|
||||
ifreq req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
if_indextoname(rtm->rtm_index, req.ifr_name);
|
||||
if (ioctl(s, SIOCGIFMTU, &req) < 0) return false;
|
||||
rt_info->mtu = req.ifr_mtu;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
@ -231,17 +284,29 @@ namespace libtorrent { namespace
|
|||
namespace libtorrent
|
||||
{
|
||||
|
||||
bool in_subnet(address const& addr, ip_interface const& iface)
|
||||
// return (a1 & mask) == (a2 & mask)
|
||||
bool match_addr_mask(address const& a1, address const& a2, address const& mask)
|
||||
{
|
||||
if (addr.is_v4() != iface.interface_address.is_v4()) return false;
|
||||
// since netmasks seems unreliable for IPv6 interfaces
|
||||
// (MacOS X returns AF_INET addresses as bitmasks) assume
|
||||
// that any IPv6 address belongs to the subnet of any
|
||||
// interface with an IPv6 address
|
||||
if (addr.is_v6()) return true;
|
||||
// all 3 addresses needs to belong to the same family
|
||||
if (a1.is_v4() != a2.is_v4()) return false;
|
||||
if (a1.is_v4() != mask.is_v4()) return false;
|
||||
|
||||
return (addr.to_v4().to_ulong() & iface.netmask.to_v4().to_ulong())
|
||||
== (iface.interface_address.to_v4().to_ulong() & iface.netmask.to_v4().to_ulong());
|
||||
#if TORRENT_USE_IPV6
|
||||
if (a1.is_v6())
|
||||
{
|
||||
address_v6::bytes_type b1;
|
||||
address_v6::bytes_type b2;
|
||||
address_v6::bytes_type m;
|
||||
b1 = a1.to_v6().to_bytes();
|
||||
b2 = a2.to_v6().to_bytes();
|
||||
m = mask.to_v6().to_bytes();
|
||||
for (int i = 0; i < b1.size(); ++i)
|
||||
b1[i] &= m[i];
|
||||
return memcmp(&b1[0], &b2[0], b1.size());
|
||||
}
|
||||
#endif
|
||||
return (a1.to_v4().to_ulong() & mask.to_v4().to_ulong())
|
||||
== (a2.to_v4().to_ulong() & mask.to_v4().to_ulong());
|
||||
}
|
||||
|
||||
bool in_local_network(io_service& ios, address const& addr, error_code& ec)
|
||||
|
@ -251,10 +316,56 @@ namespace libtorrent
|
|||
for (std::vector<ip_interface>::iterator i = net.begin()
|
||||
, end(net.end()); i != end; ++i)
|
||||
{
|
||||
if (in_subnet(addr, *i)) return true;
|
||||
if (match_addr_mask(addr, i->interface_address, i->netmask)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined TORRENT_WINDOWS || defined TORRENT_MINGW
|
||||
address build_netmask(int bits, int family)
|
||||
{
|
||||
if (family == AF_INET)
|
||||
{
|
||||
typedef asio::ip::address_v4::bytes_type bytes_t;
|
||||
bytes_t b;
|
||||
std::memset(&b[0], 0xff, b.size());
|
||||
for (int i = sizeof(bytes_t)/8-1; i > 0; --i)
|
||||
{
|
||||
if (bits < 8)
|
||||
{
|
||||
b[i] <<= bits;
|
||||
break;
|
||||
}
|
||||
b[i] = 0;
|
||||
bits -= 8;
|
||||
}
|
||||
return address_v4(b);
|
||||
}
|
||||
#if TORRENT_USE_IPV6
|
||||
else if (family == AF_INET6)
|
||||
{
|
||||
typedef asio::ip::address_v6::bytes_type bytes_t;
|
||||
bytes_t b;
|
||||
std::memset(&b[0], 0xff, b.size());
|
||||
for (int i = sizeof(bytes_t)/8-1; i > 0; --i)
|
||||
{
|
||||
if (bits < 8)
|
||||
{
|
||||
b[i] <<= bits;
|
||||
break;
|
||||
}
|
||||
b[i] = 0;
|
||||
bits -= 8;
|
||||
}
|
||||
return address_v6(b);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
return address();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<ip_interface> enum_net_interfaces(io_service& ios, error_code& ec)
|
||||
{
|
||||
|
@ -295,8 +406,20 @@ namespace libtorrent
|
|||
iface.interface_address = sockaddr_to_address(&item.ifr_addr);
|
||||
strcpy(iface.name, item.ifr_name);
|
||||
|
||||
ifreq netmask = item;
|
||||
if (ioctl(s, SIOCGIFNETMASK, &netmask) < 0)
|
||||
ifreq req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE);
|
||||
if (ioctl(s, SIOCGIFMTU, &req) < 0)
|
||||
{
|
||||
ec = error_code(errno, asio::error::system_category);
|
||||
close(s);
|
||||
return ret;
|
||||
}
|
||||
iface.mtu = req.ifr_mtu;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE);
|
||||
if (ioctl(s, SIOCGIFNETMASK, &req) < 0)
|
||||
{
|
||||
#if TORRENT_USE_IPV6
|
||||
if (iface.interface_address.is_v6())
|
||||
|
@ -314,7 +437,7 @@ namespace libtorrent
|
|||
}
|
||||
else
|
||||
{
|
||||
iface.netmask = sockaddr_to_address(&netmask.ifr_addr);
|
||||
iface.netmask = sockaddr_to_address(&req.ifr_addr, item.ifr_addr.sa_family);
|
||||
}
|
||||
ret.push_back(iface);
|
||||
}
|
||||
|
@ -331,6 +454,65 @@ namespace libtorrent
|
|||
|
||||
#elif defined TORRENT_WINDOWS || defined TORRENT_MINGW
|
||||
|
||||
// Load Iphlpapi library
|
||||
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
|
||||
if (iphlp)
|
||||
{
|
||||
// Get GetAdaptersAddresses() pointer
|
||||
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG,ULONG,PVOID,PIP_ADAPTER_ADDRESSES,PULONG);
|
||||
GetAdaptersAddresses_t GetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(
|
||||
iphlp, "GetAdaptersAddresses");
|
||||
|
||||
if (GetAdaptersAddresses)
|
||||
{
|
||||
PIP_ADAPTER_ADDRESSES adapter_addresses = 0;
|
||||
ULONG out_buf_size = 0;
|
||||
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER
|
||||
| GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) != ERROR_BUFFER_OVERFLOW)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::operation_not_supported;
|
||||
return std::vector<ip_interface>();
|
||||
}
|
||||
|
||||
adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(out_buf_size);
|
||||
if (!adapter_addresses)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::no_memory;
|
||||
return std::vector<ip_interface>();
|
||||
}
|
||||
|
||||
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER
|
||||
| GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) == NO_ERROR)
|
||||
{
|
||||
for (PIP_ADAPTER_ADDRESSES adapter = adapter_addresses;
|
||||
adapter != 0; adapter = adapter->Next)
|
||||
{
|
||||
ip_interface r;
|
||||
strncpy(r.name, adapter->AdapterName, sizeof(r.name));
|
||||
r.name[sizeof(r.name)-1] = 0;
|
||||
r.mtu = adapter->Mtu;
|
||||
IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress;
|
||||
while (unicast)
|
||||
{
|
||||
r.interface_address = sockaddr_to_address(unicast->Address.lpSockaddr);
|
||||
|
||||
ret.push_back(r);
|
||||
|
||||
unicast = unicast->Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free memory
|
||||
free(adapter_addresses);
|
||||
FreeLibrary(iphlp);
|
||||
return ret;
|
||||
}
|
||||
FreeLibrary(iphlp);
|
||||
}
|
||||
|
||||
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s == SOCKET_ERROR)
|
||||
{
|
||||
|
@ -356,9 +538,11 @@ namespace libtorrent
|
|||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
iface.interface_address = sockaddr_to_address(&buffer[i].iiAddress.Address);
|
||||
iface.netmask = sockaddr_to_address(&buffer[i].iiNetmask.Address);
|
||||
iface.name[0] = 0;
|
||||
if (iface.interface_address == address_v4::any()) continue;
|
||||
iface.netmask = sockaddr_to_address(&buffer[i].iiNetmask.Address
|
||||
, iface.interface_address.is_v4() ? AF_INET : AF_INET6);
|
||||
iface.name[0] = 0;
|
||||
iface.mtu = 1500; // how to get the MTU?
|
||||
ret.push_back(iface);
|
||||
}
|
||||
|
||||
|
@ -372,6 +556,7 @@ namespace libtorrent
|
|||
for (;i != udp::resolver_iterator(); ++i)
|
||||
{
|
||||
iface.interface_address = i->endpoint().address();
|
||||
iface.mtu = 1500;
|
||||
if (iface.interface_address.is_v4())
|
||||
iface.netmask = address_v4::netmask(iface.interface_address.to_v4());
|
||||
ret.push_back(iface);
|
||||
|
@ -528,6 +713,12 @@ namespace libtorrent
|
|||
|
||||
char* end = buf.get() + needed;
|
||||
|
||||
int s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s < 0)
|
||||
{
|
||||
ec = error_code(errno, asio::error::system_category);
|
||||
return std::vector<ip_route>();
|
||||
}
|
||||
rt_msghdr* rtm;
|
||||
for (char* next = buf.get(); next < end; next += rtm->rtm_msglen)
|
||||
{
|
||||
|
@ -536,11 +727,13 @@ namespace libtorrent
|
|||
continue;
|
||||
|
||||
ip_route r;
|
||||
if (parse_route(rtm, &r)) ret.push_back(r);
|
||||
if (parse_route(s, rtm, &r)) ret.push_back(r);
|
||||
}
|
||||
close(s);
|
||||
|
||||
#elif defined TORRENT_WINDOWS || defined TORRENT_MINGW
|
||||
|
||||
/*
|
||||
move this to enum_net_interfaces
|
||||
// Load Iphlpapi library
|
||||
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
|
||||
if (!iphlp)
|
||||
|
@ -596,11 +789,109 @@ namespace libtorrent
|
|||
ret.push_back(r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Free memory
|
||||
free(adapter_info);
|
||||
FreeLibrary(iphlp);
|
||||
*/
|
||||
|
||||
// Load Iphlpapi library
|
||||
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
|
||||
if (!iphlp)
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return std::vector<ip_route>();
|
||||
}
|
||||
|
||||
typedef DWORD (WINAPI *GetIpForwardTable2_t)(
|
||||
ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2*);
|
||||
typedef void (WINAPI *FreeMibTable_t)(PVOID Memory);
|
||||
|
||||
GetIpForwardTable2_t GetIpForwardTable2 = (GetIpForwardTable2_t)GetProcAddress(
|
||||
iphlp, "GetIpForwardTable2");
|
||||
FreeMibTable_t FreeMibTable = (FreeMibTable_t)GetProcAddress(
|
||||
iphlp, "FreeMibTable");
|
||||
if (GetIpForwardTable2 && FreeMibTable)
|
||||
{
|
||||
MIB_IPFORWARD_TABLE2* routes = NULL;
|
||||
int res = GetIpForwardTable2(AF_UNSPEC, &routes);
|
||||
if (res == NO_ERROR)
|
||||
{
|
||||
for (int i = 0; i < routes->NumEntries; ++i)
|
||||
{
|
||||
ip_route r;
|
||||
r.gateway = sockaddr_to_address((const sockaddr*)&routes->Table[i].NextHop);
|
||||
r.destination = sockaddr_to_address(
|
||||
(const sockaddr*)&routes->Table[i].DestinationPrefix.Prefix);
|
||||
r.netmask = build_netmask(routes->Table[i].SitePrefixLength
|
||||
, routes->Table[i].DestinationPrefix.Prefix.si_family);
|
||||
MIB_IFROW ifentry;
|
||||
ifentry.dwIndex = routes->Table[i].InterfaceIndex;
|
||||
if (GetIfEntry(&ifentry) == NO_ERROR)
|
||||
{
|
||||
wcstombs(r.name, ifentry.wszName, sizeof(r.name));
|
||||
r.mtu = ifentry.dwMtu;
|
||||
ret.push_back(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (routes) FreeMibTable(routes);
|
||||
FreeLibrary(iphlp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get GetIpForwardTable() pointer
|
||||
typedef DWORD (WINAPI *GetIpForwardTable_t)(PMIB_IPFORWARDTABLE pIpForwardTable,PULONG pdwSize,BOOL bOrder);
|
||||
|
||||
GetIpForwardTable_t GetIpForwardTable = (GetIpForwardTable_t)GetProcAddress(
|
||||
iphlp, "GetIpForwardTable");
|
||||
if (!GetIpForwardTable)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::operation_not_supported;
|
||||
return std::vector<ip_route>();
|
||||
}
|
||||
|
||||
MIB_IPFORWARDTABLE* routes = NULL;
|
||||
ULONG out_buf_size = 0;
|
||||
if (GetIpForwardTable(routes, &out_buf_size, FALSE) != ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::operation_not_supported;
|
||||
return std::vector<ip_route>();
|
||||
}
|
||||
|
||||
routes = (MIB_IPFORWARDTABLE*)malloc(out_buf_size);
|
||||
if (!routes)
|
||||
{
|
||||
FreeLibrary(iphlp);
|
||||
ec = asio::error::no_memory;
|
||||
return std::vector<ip_route>();
|
||||
}
|
||||
|
||||
if (GetIpForwardTable(routes, &out_buf_size, FALSE) == NO_ERROR)
|
||||
{
|
||||
for (int i = 0; i < routes->dwNumEntries; ++i)
|
||||
{
|
||||
ip_route r;
|
||||
r.destination = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardDest);
|
||||
r.netmask = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardMask);
|
||||
r.gateway = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardNextHop);
|
||||
MIB_IFROW ifentry;
|
||||
ifentry.dwIndex = routes->table[i].dwForwardIfIndex;
|
||||
if (GetIfEntry(&ifentry) == NO_ERROR)
|
||||
{
|
||||
wcstombs(r.name, ifentry.wszName, sizeof(r.name));
|
||||
r.name[sizeof(r.name)-1] = 0;
|
||||
r.mtu = ifentry.dwMtu;
|
||||
ret.push_back(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free memory
|
||||
free(routes);
|
||||
FreeLibrary(iphlp);
|
||||
#elif defined TORRENT_LINUX
|
||||
|
||||
enum { BUFSIZE = 8192 };
|
||||
|
@ -639,11 +930,18 @@ namespace libtorrent
|
|||
return std::vector<ip_route>();
|
||||
}
|
||||
|
||||
int s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s < 0)
|
||||
{
|
||||
ec = error_code(errno, asio::error::system_category);
|
||||
return std::vector<ip_route>();
|
||||
}
|
||||
for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len))
|
||||
{
|
||||
ip_route r;
|
||||
if (parse_route(nl_msg, &r)) ret.push_back(r);
|
||||
if (parse_route(s, nl_msg, &r)) ret.push_back(r);
|
||||
}
|
||||
close(s);
|
||||
close(sock);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -156,7 +156,7 @@ namespace libtorrent
|
|||
"pex message too large",
|
||||
"invalid pex message",
|
||||
"invalid lt_tracker message",
|
||||
"",
|
||||
"pex messages sent too frequent (possible attack)",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
|
|
|
@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/socket.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
#include "libtorrent/socket_type.hpp"
|
||||
#include "libtorrent/utp_socket_manager.hpp"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
|
@ -43,9 +44,15 @@ namespace libtorrent
|
|||
|
||||
bool instantiate_connection(io_service& ios
|
||||
, proxy_settings const& ps, socket_type& s
|
||||
, void* ssl_context)
|
||||
, void* ssl_context
|
||||
, utp_socket_manager* sm)
|
||||
{
|
||||
if (ps.type == proxy_settings::none)
|
||||
if (sm)
|
||||
{
|
||||
s.instantiate<utp_stream>(ios);
|
||||
s.get<utp_stream>()->set_impl(sm->new_utp_socket(s.get<utp_stream>()));
|
||||
}
|
||||
else if (ps.type == proxy_settings::none)
|
||||
{
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (ssl_context)
|
||||
|
|
|
@ -248,6 +248,8 @@ namespace libtorrent { namespace
|
|||
, m_tp(tp)
|
||||
{}
|
||||
|
||||
virtual char const* type() const { return "LT_metadata"; }
|
||||
|
||||
// can add entries to the extension handshake
|
||||
virtual void add_handshake(entry& h)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2010, 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 <stdlib.h> // free and calloc
|
||||
#include "libtorrent/packet_buffer.hpp"
|
||||
#include "libtorrent/assert.hpp"
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs
|
||||
, boost::uint32_t mask);
|
||||
|
||||
packet_buffer::packet_buffer()
|
||||
: m_storage(0)
|
||||
, m_capacity(0)
|
||||
, m_size(0)
|
||||
, m_first(0)
|
||||
, m_last(0)
|
||||
{}
|
||||
|
||||
packet_buffer::~packet_buffer()
|
||||
{
|
||||
free(m_storage);
|
||||
}
|
||||
|
||||
void* packet_buffer::insert(index_type idx, void* value)
|
||||
{
|
||||
TORRENT_ASSERT_VAL(idx <= 0xffff, idx);
|
||||
// you're not allowed to insert NULLs!
|
||||
TORRENT_ASSERT(value);
|
||||
|
||||
if (m_size != 0)
|
||||
{
|
||||
if (compare_less_wrap(idx, m_first, 0xffff))
|
||||
{
|
||||
// Index comes before m_first. If we have room, we can simply
|
||||
// adjust m_first backward.
|
||||
|
||||
std::size_t free_space = 0;
|
||||
|
||||
for (index_type i = (m_first - 1) & (m_capacity - 1);
|
||||
i != (m_first & (m_capacity - 1)); i = (i - 1) & (m_capacity - 1))
|
||||
{
|
||||
if (m_storage[i & (m_capacity - 1)])
|
||||
break;
|
||||
++free_space;
|
||||
}
|
||||
|
||||
if (((m_first - idx) & 0xffff) > free_space)
|
||||
reserve(((m_first - idx) & 0xffff) + m_capacity - free_space);
|
||||
|
||||
m_first = idx;
|
||||
}
|
||||
else if (idx >= m_first + m_capacity)
|
||||
{
|
||||
reserve(idx - m_first + 1);
|
||||
}
|
||||
else if (idx < m_first)
|
||||
{
|
||||
// We have wrapped.
|
||||
if (idx > ((m_first + m_capacity) & 0xffff) && m_capacity < 0xffff)
|
||||
{
|
||||
reserve(m_capacity + (idx - ((m_first + m_capacity) & 0xffff)));
|
||||
}
|
||||
}
|
||||
if (compare_less_wrap(m_last, (idx + 1) & 0xffff, 0xffff))
|
||||
m_last = (idx + 1) & 0xffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_first = idx;
|
||||
m_last = (idx + 1) & 0xffff;
|
||||
}
|
||||
|
||||
if (m_capacity == 0) reserve(16);
|
||||
|
||||
void* old_value = m_storage[idx & (m_capacity - 1)];
|
||||
m_storage[idx & (m_capacity - 1)] = value;
|
||||
|
||||
if (m_size++ == 0)
|
||||
{
|
||||
m_first = idx;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
void* packet_buffer::at(index_type idx) const
|
||||
{
|
||||
if (idx >= m_first + m_capacity)
|
||||
return 0;
|
||||
|
||||
if (compare_less_wrap(idx, m_first, 0xffff))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_storage[idx & (m_capacity - 1)];
|
||||
}
|
||||
|
||||
void packet_buffer::reserve(std::size_t size)
|
||||
{
|
||||
TORRENT_ASSERT_VAL(size <= 0xffff, size);
|
||||
std::size_t new_size = m_capacity == 0 ? 16 : m_capacity;
|
||||
|
||||
while (new_size < size)
|
||||
new_size <<= 1;
|
||||
|
||||
void** new_storage = (void**)malloc(sizeof(void*) * new_size);
|
||||
|
||||
for (index_type i = 0; i < new_size; ++i)
|
||||
new_storage[i] = 0;
|
||||
|
||||
for (index_type i = m_first; i < (m_first + m_capacity); ++i)
|
||||
new_storage[i & (new_size - 1)] = m_storage[i & (m_capacity - 1)];
|
||||
|
||||
free(m_storage);
|
||||
|
||||
m_storage = new_storage;
|
||||
m_capacity = new_size;
|
||||
}
|
||||
|
||||
void* packet_buffer::remove(index_type idx)
|
||||
{
|
||||
// TODO: use compare_less_wrap for this comparison as well
|
||||
if (idx >= m_first + m_capacity)
|
||||
return 0;
|
||||
|
||||
if (compare_less_wrap(idx, m_first, 0xffff))
|
||||
return 0;
|
||||
|
||||
void* old_value = m_storage[idx & (m_capacity - 1)];
|
||||
m_storage[idx & (m_capacity - 1)] = 0;
|
||||
|
||||
if (old_value)
|
||||
{
|
||||
--m_size;
|
||||
if (m_size == 0) m_last = m_first;
|
||||
}
|
||||
|
||||
if (idx == m_first && m_size != 0)
|
||||
{
|
||||
while (!m_storage[++m_first & (m_capacity - 1)]);
|
||||
m_first &= 0xffff;
|
||||
}
|
||||
|
||||
if (((idx + 1) & 0xffff) == m_last && m_size != 0)
|
||||
{
|
||||
while (!m_storage[--m_last & (m_capacity - 1)]);
|
||||
++m_last;
|
||||
m_last &= 0xffff;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT_VAL(m_first <= 0xffff, m_first);
|
||||
return old_value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/broadcast_socket.hpp"
|
||||
#include "libtorrent/torrent.hpp"
|
||||
#include "libtorrent/peer_info.hpp"
|
||||
#include "libtorrent/bt_peer_connection.hpp"
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
#include <set>
|
||||
|
@ -150,6 +151,7 @@ namespace libtorrent
|
|||
, m_bitfield_received(false)
|
||||
, m_no_download(false)
|
||||
, m_sent_suggests(false)
|
||||
, m_holepunch_mode(false)
|
||||
, m_ignore_stats(false)
|
||||
#ifdef TORRENT_DEBUG
|
||||
, m_in_constructor(true)
|
||||
|
@ -195,6 +197,8 @@ namespace libtorrent
|
|||
+ to_string(m_remote.port()).elems, m_ses.listen_port());
|
||||
(*m_logger) << time_now_string() << " *** OUTGOING CONNECTION: "
|
||||
<< print_endpoint(m_remote) << "\n";
|
||||
if (m_socket->get<utp_stream>()) (*m_logger) << "uTP connection\n";
|
||||
else (*m_logger) << "TCP connection\n";
|
||||
#endif
|
||||
#ifdef TORRENT_DEBUG
|
||||
piece_failed = false;
|
||||
|
@ -289,6 +293,7 @@ namespace libtorrent
|
|||
, m_bitfield_received(false)
|
||||
, m_no_download(false)
|
||||
, m_sent_suggests(false)
|
||||
, m_holepunch_mode(false)
|
||||
, m_ignore_stats(false)
|
||||
#ifdef TORRENT_DEBUG
|
||||
, m_in_constructor(true)
|
||||
|
@ -335,6 +340,8 @@ namespace libtorrent
|
|||
+ to_string(remote().port()).elems, m_ses.listen_port());
|
||||
(*m_logger) << time_now_string() << " *** INCOMING CONNECTION: "
|
||||
<< print_endpoint(m_remote) << "\n";
|
||||
if (m_socket->get<utp_stream>()) (*m_logger) << "uTP connection\n";
|
||||
else (*m_logger) << "TCP connection\n";
|
||||
#endif
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
|
@ -557,6 +564,7 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
m_remote = m_socket->remote_endpoint(ec);
|
||||
TORRENT_ASSERT(m_remote.address() != address_v4::any());
|
||||
if (ec)
|
||||
{
|
||||
disconnect(ec);
|
||||
|
@ -626,6 +634,16 @@ namespace libtorrent
|
|||
{
|
||||
m_extensions.push_back(ext);
|
||||
}
|
||||
|
||||
peer_plugin const* peer_connection::find_plugin(char const* type)
|
||||
{
|
||||
for (extension_list_t::iterator i = m_extensions.begin()
|
||||
, end(m_extensions.end()); i != end; ++i)
|
||||
{
|
||||
if (strcmp((*i)->type(), type) == 0) return (*i).get();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void peer_connection::send_allowed_set()
|
||||
|
@ -2215,6 +2233,8 @@ namespace libtorrent
|
|||
if (!m_bitfield_received) incoming_have_none();
|
||||
if (is_disconnecting()) return;
|
||||
|
||||
update_desired_queue_size();
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
for (extension_list_t::iterator i = m_extensions.begin()
|
||||
, end(m_extensions.end()); i != end; ++i)
|
||||
|
@ -3238,13 +3258,62 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_ses.is_network_thread());
|
||||
|
||||
TORRENT_ASSERT(m_connecting);
|
||||
connect_failed(errors::timed_out);
|
||||
}
|
||||
|
||||
void peer_connection::connect_failed(error_code const& e)
|
||||
{
|
||||
TORRENT_ASSERT(m_connecting);
|
||||
TORRENT_ASSERT(e);
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
peer_log("CONNECTION TIMED OUT: %s", print_endpoint(m_remote).c_str());
|
||||
peer_log("CONNECTION FAILED: %s", print_endpoint(m_remote).c_str());
|
||||
#endif
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
(*m_ses.m_logger) << "CONNECTION TIMED OUT: " << print_endpoint(m_remote) << "\n";
|
||||
(*m_ses.m_logger) << "CONNECTION FAILED: " << print_endpoint(m_remote) << "\n";
|
||||
#endif
|
||||
disconnect(errors::timed_out, 1);
|
||||
|
||||
if (m_connection_ticket != -1)
|
||||
{
|
||||
m_ses.m_half_open.done(m_connection_ticket);
|
||||
m_connecting = false;
|
||||
}
|
||||
|
||||
// a connection attempt using uTP just failed
|
||||
// mark this peer as not supporting uTP
|
||||
// we'll never try it again (unless we're trying holepunch)
|
||||
if (m_socket->get<utp_stream>()
|
||||
&& m_peer_info
|
||||
&& m_peer_info->supports_utp
|
||||
&& !m_holepunch_mode)
|
||||
{
|
||||
m_peer_info->supports_utp = false;
|
||||
// reconnect immediately using TCP
|
||||
policy::peer* pi = peer_info_struct();
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
fast_reconnect(true);
|
||||
disconnect(e, 0);
|
||||
if (t && pi) t->connect_to_peer(pi, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_holepunch_mode)
|
||||
fast_reconnect(true);
|
||||
|
||||
if ((!m_socket->get<utp_stream>() || !m_ses.m_settings.enable_outgoing_tcp)
|
||||
&& m_peer_info
|
||||
&& m_peer_info->supports_holepunch
|
||||
&& !m_holepunch_mode)
|
||||
{
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
// see if we can try a holepunch
|
||||
bt_peer_connection* p = t->find_introducer(remote());
|
||||
if (p)
|
||||
p->write_holepunch_msg(bt_peer_connection::hp_rendezvous, remote(), 0);
|
||||
}
|
||||
|
||||
disconnect(e, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// the error argument defaults to 0, which means deliberate disconnect
|
||||
|
@ -3524,6 +3593,7 @@ namespace libtorrent
|
|||
p.flags |= is_seed() ? peer_info::seed : 0;
|
||||
p.flags |= m_snubbed ? peer_info::snubbed : 0;
|
||||
p.flags |= m_upload_only ? peer_info::upload_only : 0;
|
||||
p.flags |= m_holepunch_mode ? peer_info::holepunched : 0;
|
||||
if (peer_info_struct())
|
||||
{
|
||||
policy::peer* pi = peer_info_struct();
|
||||
|
@ -3680,6 +3750,37 @@ namespace libtorrent
|
|||
m_superseed_piece = index;
|
||||
}
|
||||
|
||||
void peer_connection::update_desired_queue_size()
|
||||
{
|
||||
if (m_snubbed)
|
||||
{
|
||||
m_desired_queue_size = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
int download_rate = statistics().download_rate();
|
||||
|
||||
// calculate the desired download queue size
|
||||
const int queue_time = m_ses.settings().request_queue_time;
|
||||
// (if the latency is more than this, the download will stall)
|
||||
// so, the queue size is queue_time * down_rate / 16 kiB
|
||||
// (16 kB is the size of each request)
|
||||
// the minimum number of requests is 2 and the maximum is 48
|
||||
// the block size doesn't have to be 16. So we first query the
|
||||
// torrent for it
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
const int block_size = t->block_size();
|
||||
|
||||
TORRENT_ASSERT(block_size > 0);
|
||||
|
||||
m_desired_queue_size = queue_time * download_rate / block_size;
|
||||
|
||||
if (m_desired_queue_size > m_max_out_request_queue)
|
||||
m_desired_queue_size = m_max_out_request_queue;
|
||||
if (m_desired_queue_size < min_request_queue)
|
||||
m_desired_queue_size = min_request_queue;
|
||||
}
|
||||
|
||||
void peer_connection::second_tick(int tick_interval_ms)
|
||||
{
|
||||
ptime now = time_now();
|
||||
|
@ -3880,36 +3981,13 @@ namespace libtorrent
|
|||
|
||||
if (!t->ready_for_connections()) return;
|
||||
|
||||
// calculate the desired download queue size
|
||||
const int queue_time = m_ses.settings().request_queue_time;
|
||||
// (if the latency is more than this, the download will stall)
|
||||
// so, the queue size is queue_time * down_rate / 16 kiB
|
||||
// (16 kB is the size of each request)
|
||||
// the minimum number of requests is 2 and the maximum is 48
|
||||
// the block size doesn't have to be 16. So we first query the
|
||||
// torrent for it
|
||||
const int block_size = t->block_size();
|
||||
TORRENT_ASSERT(block_size > 0);
|
||||
|
||||
if (m_snubbed)
|
||||
{
|
||||
m_desired_queue_size = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_desired_queue_size = queue_time
|
||||
* statistics().download_rate() / block_size;
|
||||
if (m_desired_queue_size > m_max_out_request_queue)
|
||||
m_desired_queue_size = m_max_out_request_queue;
|
||||
if (m_desired_queue_size < min_request_queue)
|
||||
m_desired_queue_size = min_request_queue;
|
||||
update_desired_queue_size();
|
||||
|
||||
if (m_desired_queue_size == m_max_out_request_queue
|
||||
if (m_desired_queue_size == m_max_out_request_queue
|
||||
&& t->alerts().should_post<performance_alert>())
|
||||
{
|
||||
t->alerts().post_alert(performance_alert(t->get_handle()
|
||||
, performance_alert::outstanding_request_limit_reached));
|
||||
}
|
||||
{
|
||||
t->alerts().post_alert(performance_alert(t->get_handle()
|
||||
, performance_alert::outstanding_request_limit_reached));
|
||||
}
|
||||
|
||||
int piece_timeout = m_ses.settings().piece_timeout;
|
||||
|
@ -3926,6 +4004,7 @@ namespace libtorrent
|
|||
// allowed to download. If it is impossible to beat the piece
|
||||
// timeout at this rate, adjust it to be realistic
|
||||
|
||||
const int block_size = t->block_size();
|
||||
int rate_limit_timeout = rate_limit / block_size;
|
||||
if (piece_timeout < rate_limit_timeout) piece_timeout = rate_limit_timeout;
|
||||
|
||||
|
@ -4131,8 +4210,11 @@ namespace libtorrent
|
|||
// only add new piece-chunks if the send buffer is small enough
|
||||
// otherwise there will be no end to how large it will be!
|
||||
|
||||
int buffer_size_watermark = int(m_statistics.upload_rate())
|
||||
int upload_rate = int(m_statistics.upload_rate());
|
||||
|
||||
int buffer_size_watermark = upload_rate
|
||||
* m_ses.settings().send_buffer_watermark_factor;
|
||||
|
||||
if (buffer_size_watermark < 512) buffer_size_watermark = 512;
|
||||
else if (buffer_size_watermark > m_ses.settings().send_buffer_watermark)
|
||||
{
|
||||
|
@ -4351,13 +4433,16 @@ namespace libtorrent
|
|||
{
|
||||
if (!m_ignore_bandwidth_limits)
|
||||
{
|
||||
bool utp = m_socket->get<utp_stream>();
|
||||
|
||||
// in this case, we have data to send, but no
|
||||
// bandwidth. So, we simply request bandwidth
|
||||
// from the bandwidth manager
|
||||
request_upload_bandwidth(
|
||||
&m_ses.m_upload_channel
|
||||
(m_ses.m_settings.rate_limit_utp || !utp) ? &m_ses.m_upload_channel : 0
|
||||
, &t->m_bandwidth_channel[upload_channel]
|
||||
, &m_bandwidth_channel[upload_channel]);
|
||||
, &m_bandwidth_channel[upload_channel]
|
||||
, !utp ? &m_ses.m_tcp_upload_channel : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4461,13 +4546,16 @@ namespace libtorrent
|
|||
{
|
||||
if (!m_ignore_bandwidth_limits)
|
||||
{
|
||||
bool utp = m_socket->get<utp_stream>();
|
||||
|
||||
// in this case, we have outstanding data to
|
||||
// receive, but no bandwidth quota. So, we simply
|
||||
// request bandwidth from the bandwidth manager
|
||||
request_download_bandwidth(
|
||||
&m_ses.m_download_channel
|
||||
(m_ses.m_settings.rate_limit_utp || !utp) ? &m_ses.m_download_channel : 0
|
||||
, &t->m_bandwidth_channel[download_channel]
|
||||
, &m_bandwidth_channel[download_channel]);
|
||||
, &m_bandwidth_channel[download_channel]
|
||||
, !utp ? &m_ses.m_tcp_download_channel : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4785,6 +4873,12 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_ses.is_network_thread());
|
||||
INVARIANT_CHECK;
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string() << " *** ON_RECEIVE_DATA ["
|
||||
" bytes: " << bytes_transferred <<
|
||||
" error: " << error.message() <<
|
||||
" ]\n";
|
||||
#endif
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
complete_async("peer_connection::on_receive_data");
|
||||
#endif
|
||||
|
@ -4816,6 +4910,7 @@ namespace libtorrent
|
|||
int num_loops = 0;
|
||||
do
|
||||
{
|
||||
TORRENT_ASSERT(m_recv_pos + bytes_transferred <= m_packet_size);
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
peer_log("<<< read %d bytes", int(bytes_transferred));
|
||||
#endif
|
||||
|
@ -4869,6 +4964,7 @@ namespace libtorrent
|
|||
|
||||
error_code ec;
|
||||
bytes_transferred = try_read(read_sync, ec);
|
||||
TORRENT_ASSERT(bytes_transferred > 0 || ec);
|
||||
if (ec && ec != asio::error::would_block)
|
||||
{
|
||||
m_statistics.trancieve_ip_packet(bytes_in_loop, m_remote.address().is_v6());
|
||||
|
@ -5036,27 +5132,25 @@ namespace libtorrent
|
|||
|
||||
if (m_disconnecting) return;
|
||||
|
||||
m_connecting = false;
|
||||
m_ses.m_half_open.done(m_connection_ticket);
|
||||
|
||||
error_code ec;
|
||||
if (e)
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
(*m_ses.m_logger) << time_now_string() << " CONNECTION FAILED: " << print_endpoint(m_remote)
|
||||
<< ": " << e.message() << "\n";
|
||||
#endif
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
(*m_logger) << time_now_string() << " CONNECTION FAILED: " << print_endpoint(m_remote)
|
||||
<< ": " << e.message() << "\n";
|
||||
#endif
|
||||
disconnect(e, 1);
|
||||
connect_failed(e);
|
||||
return;
|
||||
}
|
||||
|
||||
m_connecting = false;
|
||||
m_ses.m_half_open.done(m_connection_ticket);
|
||||
|
||||
if (m_disconnecting) return;
|
||||
m_last_receive = time_now();
|
||||
|
||||
if (m_socket->get<utp_stream>() && m_peer_info)
|
||||
{
|
||||
m_peer_info->confirmed_supports_utp = true;
|
||||
m_peer_info->supports_utp = false;
|
||||
}
|
||||
|
||||
// this means the connection just succeeded
|
||||
|
||||
m_statistics.received_synack(m_remote.address().is_v6());
|
||||
|
@ -5110,6 +5204,13 @@ namespace libtorrent
|
|||
{
|
||||
TORRENT_ASSERT(m_ses.is_network_thread());
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_logger) << time_now_string() << " *** ON_SEND_DATA ["
|
||||
" bytes: " << bytes_transferred <<
|
||||
" error: " << error.message() <<
|
||||
" ]\n";
|
||||
#endif
|
||||
|
||||
INVARIANT_CHECK;
|
||||
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
|
|
|
@ -108,7 +108,7 @@ namespace libtorrent
|
|||
{
|
||||
// returns the rank of a peer's source. We have an affinity
|
||||
// to connecting to peers with higher rank. This is to avoid
|
||||
// problems when out peer list is diluted by stale peers from
|
||||
// problems when our peer list is diluted by stale peers from
|
||||
// the resume data for instance
|
||||
int source_rank(int source_bitmask)
|
||||
{
|
||||
|
@ -1008,6 +1008,10 @@ namespace libtorrent
|
|||
p->seed = true;
|
||||
++m_num_seeds;
|
||||
}
|
||||
if (flags & 0x04)
|
||||
p->supports_utp = true;
|
||||
if (flags & 0x08)
|
||||
p->supports_holepunch = true;
|
||||
|
||||
#ifndef TORRENT_DISABLE_GEO_IP
|
||||
int as = m_torrent->session().as_for_ip(p->address());
|
||||
|
@ -1048,6 +1052,10 @@ namespace libtorrent
|
|||
if (!p->seed) ++m_num_seeds;
|
||||
p->seed = true;
|
||||
}
|
||||
if (flags & 0x04)
|
||||
p->supports_utp = true;
|
||||
if (flags & 0x08)
|
||||
p->supports_holepunch = true;
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
||||
if (p->connection)
|
||||
|
@ -1362,7 +1370,13 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(c);
|
||||
error_code ec;
|
||||
TORRENT_ASSERT(c->remote() == c->get_socket()->remote_endpoint(ec) || ec);
|
||||
if (c->remote() != c->get_socket()->remote_endpoint(ec) && !ec)
|
||||
{
|
||||
fprintf(stderr, "c->remote: %s\nc->get_socket()->remote_endpoint: %s\n"
|
||||
, print_endpoint(c->remote()).c_str()
|
||||
, print_endpoint(c->get_socket()->remote_endpoint(ec)).c_str());
|
||||
TORRENT_ASSERT(false);
|
||||
}
|
||||
|
||||
return std::find_if(
|
||||
m_peers.begin()
|
||||
|
@ -1525,6 +1539,9 @@ namespace libtorrent
|
|||
#ifndef TORRENT_DISABLE_DHT
|
||||
, added_to_dht(false)
|
||||
#endif
|
||||
, supports_utp(true) // assume peers support utp
|
||||
, confirmed_supports_utp(false)
|
||||
, supports_holepunch(false)
|
||||
{
|
||||
TORRENT_ASSERT((src & 0xff) == src);
|
||||
}
|
||||
|
|
|
@ -154,6 +154,9 @@ namespace libtorrent
|
|||
set.coalesce_reads = false;
|
||||
set.coalesce_writes = false;
|
||||
|
||||
// disallow the buffer size to grow for the uTP socket
|
||||
set.utp_dynamic_sock_buf = false;
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
|
@ -226,6 +229,9 @@ namespace libtorrent
|
|||
// connect to us if they want to
|
||||
set.max_failcount = 1;
|
||||
|
||||
// allow the buffer size to grow for the uTP socket
|
||||
set.utp_dynamic_sock_buf = true;
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
|
|
|
@ -319,6 +319,11 @@ namespace aux {
|
|||
TORRENT_SETTING(integer, default_peer_upload_rate)
|
||||
TORRENT_SETTING(integer, default_peer_download_rate)
|
||||
TORRENT_SETTING(boolean, broadcast_lsd)
|
||||
TORRENT_SETTING(boolean, enable_outgoing_utp)
|
||||
TORRENT_SETTING(boolean, enable_incoming_utp)
|
||||
TORRENT_SETTING(boolean, enable_outgoing_tcp)
|
||||
TORRENT_SETTING(boolean, enable_incoming_tcp)
|
||||
TORRENT_SETTING(integer, max_pex_peers)
|
||||
TORRENT_SETTING(boolean, ignore_resume_timestamps)
|
||||
TORRENT_SETTING(boolean, anonymous_mode)
|
||||
TORRENT_SETTING(integer, tick_interval)
|
||||
|
@ -329,6 +334,16 @@ namespace aux {
|
|||
TORRENT_SETTING(integer, unchoke_slots_limit)
|
||||
TORRENT_SETTING(integer, half_open_limit)
|
||||
TORRENT_SETTING(integer, connections_limit)
|
||||
TORRENT_SETTING(integer, utp_target_delay)
|
||||
TORRENT_SETTING(integer, utp_gain_factor)
|
||||
TORRENT_SETTING(integer, utp_syn_resends)
|
||||
TORRENT_SETTING(integer, utp_fin_resends)
|
||||
TORRENT_SETTING(integer, utp_num_resends)
|
||||
TORRENT_SETTING(integer, utp_connect_timeout)
|
||||
TORRENT_SETTING(integer, utp_delayed_ack)
|
||||
TORRENT_SETTING(boolean, utp_dynamic_sock_buf)
|
||||
TORRENT_SETTING(integer, mixed_mode_algorithm)
|
||||
TORRENT_SETTING(boolean, rate_limit_utp)
|
||||
TORRENT_SETTING(integer, listen_queue_size)
|
||||
};
|
||||
|
||||
|
@ -482,6 +497,8 @@ namespace aux {
|
|||
, boost::bind(&session_impl::on_receive_udp, this, _1, _2, _3, _4)
|
||||
, boost::bind(&session_impl::on_receive_udp_hostname, this, _1, _2, _3, _4)
|
||||
, m_half_open)
|
||||
, m_utp_socket_manager(m_settings, m_udp_socket
|
||||
, boost::bind(&session_impl::incoming_connection, this, _1))
|
||||
, m_timer(m_io_service)
|
||||
, m_lsd_announce_timer(m_io_service)
|
||||
, m_host_resolver(m_io_service)
|
||||
|
@ -645,6 +662,7 @@ namespace aux {
|
|||
PRINT_SIZEOF(stat)
|
||||
PRINT_SIZEOF(bandwidth_channel)
|
||||
PRINT_SIZEOF(policy)
|
||||
(*m_logger) << "sizeof(utp_socket_impl): " << socket_impl_size() << "\n";
|
||||
|
||||
PRINT_SIZEOF(file_entry)
|
||||
|
||||
|
@ -1792,12 +1810,15 @@ namespace aux {
|
|||
{
|
||||
// this is probably a dht message
|
||||
m_dht->on_receive(ep, buf, len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_utp_socket_manager.incoming_packet(buf, len, ep))
|
||||
return;
|
||||
|
||||
// maybe it's a udp tracker response
|
||||
else if (m_tracker_manager.incoming_udp(e, ep, buf, len))
|
||||
{
|
||||
if (m_tracker_manager.incoming_udp(e, ep, buf, len))
|
||||
m_stat.received_tracker_bytes(len + 28);
|
||||
}
|
||||
}
|
||||
|
||||
void session_impl::on_receive_udp_hostname(error_code const& e
|
||||
|
@ -1901,10 +1922,34 @@ namespace aux {
|
|||
return;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(endp.address() != address_v4::any());
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
(*m_logger) << time_now_string() << " <== INCOMING CONNECTION " << endp << "\n";
|
||||
#endif
|
||||
|
||||
if (!m_settings.enable_incoming_utp
|
||||
&& s->get<utp_stream>())
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
(*m_logger) << " rejected uTP connection\n";
|
||||
#endif
|
||||
if (m_alerts.should_post<peer_blocked_alert>())
|
||||
m_alerts.post_alert(peer_blocked_alert(torrent_handle(), endp.address()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_settings.enable_incoming_tcp
|
||||
&& s->get<stream_socket>())
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
(*m_logger) << " rejected TCP connection\n";
|
||||
#endif
|
||||
if (m_alerts.should_post<peer_blocked_alert>())
|
||||
m_alerts.post_alert(peer_blocked_alert(torrent_handle(), endp.address()));
|
||||
return;
|
||||
}
|
||||
|
||||
// local addresses do not count, since it's likely
|
||||
// coming from our own client through local service discovery
|
||||
// and it does not reflect whether or not a router is open
|
||||
|
@ -2184,6 +2229,8 @@ namespace aux {
|
|||
|
||||
m_last_tick = now;
|
||||
|
||||
m_utp_socket_manager.tick(now);
|
||||
|
||||
// only tick the following once per second
|
||||
if (now - m_last_second_tick < seconds(1)) return;
|
||||
|
||||
|
@ -2225,6 +2272,45 @@ namespace aux {
|
|||
}
|
||||
}
|
||||
|
||||
switch (m_settings.mixed_mode_algorithm)
|
||||
{
|
||||
case session_settings::prefer_tcp:
|
||||
m_tcp_upload_channel.throttle(0);
|
||||
m_tcp_download_channel.throttle(0);
|
||||
break;
|
||||
case session_settings::peer_proportional:
|
||||
{
|
||||
int num_tcp_peers = 0;
|
||||
int num_peers = 0;
|
||||
for (connection_map::iterator i = m_connections.begin()
|
||||
, end(m_connections.end());i != end; ++i)
|
||||
{
|
||||
peer_connection& p = *(*i);
|
||||
if (p.in_handshake()) continue;
|
||||
if (!p.get_socket()->get<utp_stream>()) ++num_tcp_peers;
|
||||
++num_peers;
|
||||
}
|
||||
|
||||
if (num_peers == 0)
|
||||
{
|
||||
m_tcp_upload_channel.throttle(0);
|
||||
m_tcp_download_channel.throttle(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (num_tcp_peers == 0) num_tcp_peers = 1;
|
||||
int upload_rate = (std::max)(m_stat.upload_rate(), 5000);
|
||||
int download_rate = (std::max)(m_stat.download_rate(), 5000);
|
||||
if (m_upload_channel.throttle()) upload_rate = m_upload_channel.throttle();
|
||||
if (m_download_channel.throttle()) download_rate = m_download_channel.throttle();
|
||||
|
||||
m_tcp_upload_channel.throttle(upload_rate * num_tcp_peers / num_peers);
|
||||
m_tcp_download_channel.throttle(download_rate * num_tcp_peers / num_peers);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
++m_second_counter;
|
||||
int downloading_torrents = 0;
|
||||
|
@ -3682,6 +3768,8 @@ namespace aux {
|
|||
}
|
||||
#endif
|
||||
|
||||
m_utp_socket_manager.get_status(s.utp_stats);
|
||||
|
||||
int peerlist_size = 0;
|
||||
for (torrent_map::const_iterator i = m_torrents.begin()
|
||||
, end(m_torrents.end()); i != end; ++i)
|
||||
|
|
|
@ -53,6 +53,9 @@ namespace libtorrent
|
|||
case socket_type_int_impl<http_stream>::value:
|
||||
get<http_stream>()->~http_stream();
|
||||
break;
|
||||
case socket_type_int_impl<utp_stream>::value:
|
||||
get<utp_stream>()->~utp_stream();
|
||||
break;
|
||||
#if TORRENT_USE_I2P
|
||||
case socket_type_int_impl<i2p_stream>::value:
|
||||
get<i2p_stream>()->~i2p_stream();
|
||||
|
@ -69,6 +72,7 @@ namespace libtorrent
|
|||
get<ssl_stream<http_stream> >()->~ssl_stream();
|
||||
break;
|
||||
#endif
|
||||
default: TORRENT_ASSERT(false);
|
||||
}
|
||||
m_type = 0;
|
||||
}
|
||||
|
@ -88,6 +92,9 @@ namespace libtorrent
|
|||
case socket_type_int_impl<http_stream>::value:
|
||||
new ((http_stream*)m_data) http_stream(m_io_service);
|
||||
break;
|
||||
case socket_type_int_impl<utp_stream>::value:
|
||||
new ((utp_stream*)m_data) utp_stream(m_io_service);
|
||||
break;
|
||||
#if TORRENT_USE_I2P
|
||||
case socket_type_int_impl<i2p_stream>::value:
|
||||
new ((i2p_stream*)m_data) i2p_stream(m_io_service);
|
||||
|
@ -110,6 +117,7 @@ namespace libtorrent
|
|||
, *((boost::asio::ssl::context*)userdata));
|
||||
break;
|
||||
#endif
|
||||
default: TORRENT_ASSERT(false);
|
||||
}
|
||||
|
||||
m_type = type;
|
||||
|
@ -127,9 +135,6 @@ namespace libtorrent
|
|||
TORRENT_SOCKTYPE_FORWARD_RET(is_open(), false)
|
||||
}
|
||||
|
||||
socket_type::lowest_layer_type& socket_type::lowest_layer()
|
||||
{ TORRENT_SOCKTYPE_FORWARD_RET(lowest_layer(), *((lowest_layer_type*)m_data)) }
|
||||
|
||||
void socket_type::open(protocol_type const& p, error_code& ec)
|
||||
{ TORRENT_SOCKTYPE_FORWARD(open(p, ec)) }
|
||||
|
||||
|
|
|
@ -347,10 +347,10 @@ namespace libtorrent
|
|||
storage(file_storage const& fs, file_storage const* mapped, std::string const& path
|
||||
, file_pool& fp, std::vector<boost::uint8_t> const& file_prio)
|
||||
: m_files(fs)
|
||||
, m_file_priority(file_prio)
|
||||
, m_pool(fp)
|
||||
, m_page_size(page_size())
|
||||
, m_allocate_files(false)
|
||||
, m_file_priority(file_prio)
|
||||
{
|
||||
if (mapped) m_mapped_files.reset(new file_storage(*mapped));
|
||||
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2009, 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 "libtorrent/timestamp_history.hpp"
|
||||
|
||||
namespace libtorrent {
|
||||
|
||||
enum
|
||||
{
|
||||
TIME_MASK = 0xffffffff
|
||||
};
|
||||
// defined in utp_stream.cpp
|
||||
bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs
|
||||
, boost::uint32_t mask);
|
||||
|
||||
boost::uint32_t timestamp_history::add_sample(boost::uint32_t sample, bool step)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
for (int i = 0; i < history_size; ++i)
|
||||
m_history[i] = sample;
|
||||
m_base = sample;
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
++m_num_samples;
|
||||
|
||||
// if sample is less than base, update the base
|
||||
// and update the history entry (because it will
|
||||
// be less than that too)
|
||||
if (compare_less_wrap(sample, m_base, TIME_MASK))
|
||||
{
|
||||
m_base = sample;
|
||||
m_history[m_index] = sample;
|
||||
}
|
||||
// if sample is less than our history entry, update it
|
||||
else if (compare_less_wrap(sample, m_history[m_index], TIME_MASK))
|
||||
{
|
||||
m_history[m_index] = sample;
|
||||
}
|
||||
|
||||
boost::uint32_t ret = sample - m_base;
|
||||
|
||||
// don't step base delay history unless we have at least 120
|
||||
// samples. Anything less would suggest that the connection is
|
||||
// essentially idle and the samples are probably not very reliable
|
||||
if (step && m_num_samples > 120)
|
||||
{
|
||||
m_num_samples = 0;
|
||||
m_index = (m_index + 1) % history_size;
|
||||
|
||||
m_history[m_index] = sample;
|
||||
// update m_base
|
||||
m_base = sample;
|
||||
for (int i = 0; i < history_size; ++i)
|
||||
{
|
||||
if (compare_less_wrap(m_history[i], m_base, TIME_MASK))
|
||||
m_base = m_history[i];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void timestamp_history::adjust_base(int change)
|
||||
{
|
||||
m_base += change;
|
||||
// make sure this adjustment sticks by updating all history slots
|
||||
for (int i = 0; i < history_size; ++i)
|
||||
{
|
||||
if (compare_less_wrap(m_history[i], m_base, TIME_MASK))
|
||||
m_history[i] = m_base;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -313,6 +313,9 @@ namespace libtorrent
|
|||
return ret;
|
||||
}
|
||||
|
||||
// defined in ut_pex.cpp
|
||||
bool was_introduced_by(peer_plugin const*, tcp::endpoint const&);
|
||||
|
||||
torrent::torrent(
|
||||
session_impl& ses
|
||||
, tcp::endpoint const& net_interface
|
||||
|
@ -986,6 +989,33 @@ namespace libtorrent
|
|||
, shared_from_this(), _1, _2));
|
||||
}
|
||||
|
||||
bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
|
||||
{
|
||||
if ((*i)->type() != peer_connection::bittorrent_connection) continue;
|
||||
bt_peer_connection* p = (bt_peer_connection*)(*i);
|
||||
if (!p->supports_holepunch()) continue;
|
||||
peer_plugin const* pp = p->find_plugin("ut_pex");
|
||||
if (!pp) continue;
|
||||
if (was_introduced_by(pp, ep)) return (bt_peer_connection*)p;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
bt_peer_connection* torrent::find_peer(tcp::endpoint const& ep) const
|
||||
{
|
||||
for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
|
||||
{
|
||||
peer_connection* p = *i;
|
||||
if (p->type() != peer_connection::bittorrent_connection) continue;
|
||||
if (p->remote() == ep) return (bt_peer_connection*)p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void torrent::on_resume_data_checked(int ret, disk_io_job const& j)
|
||||
{
|
||||
TORRENT_ASSERT(m_ses.is_network_thread());
|
||||
|
@ -4366,7 +4396,7 @@ namespace libtorrent
|
|||
|
||||
}
|
||||
|
||||
bool torrent::connect_to_peer(policy::peer* peerinfo)
|
||||
bool torrent::connect_to_peer(policy::peer* peerinfo, bool ignore_limit)
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
|
||||
|
@ -4390,8 +4420,8 @@ namespace libtorrent
|
|||
#endif
|
||||
#endif
|
||||
|
||||
TORRENT_ASSERT(want_more_peers());
|
||||
TORRENT_ASSERT(m_ses.num_connections() < m_ses.settings().connections_limit);
|
||||
TORRENT_ASSERT(want_more_peers() || ignore_limit);
|
||||
TORRENT_ASSERT(m_ses.num_connections() < m_ses.settings().connections_limit || ignore_limit);
|
||||
|
||||
tcp::endpoint a(peerinfo->ip());
|
||||
TORRENT_ASSERT((m_ses.m_ip_filter.access(peerinfo->address()) & ip_filter::blocked) == 0);
|
||||
|
@ -4412,7 +4442,21 @@ namespace libtorrent
|
|||
else
|
||||
#endif
|
||||
{
|
||||
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s);
|
||||
// this is where we determine if we open a regular TCP connection
|
||||
// or a uTP connection. If the m_utp_socket_manager pointer is not passed in
|
||||
// we'll instantiate a TCP connection
|
||||
utp_socket_manager* sm = 0;
|
||||
|
||||
if (m_ses.m_settings.enable_outgoing_utp
|
||||
&& (!m_ses.m_settings.enable_outgoing_tcp
|
||||
|| peerinfo->supports_utp
|
||||
|| peerinfo->confirmed_supports_utp))
|
||||
sm = &m_ses.m_utp_socket_manager;
|
||||
|
||||
// don't make a TCP connection if it's disabled
|
||||
if (sm == 0 && !m_ses.m_settings.enable_outgoing_tcp) return false;
|
||||
|
||||
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, 0, sm);
|
||||
(void)ret;
|
||||
TORRENT_ASSERT(ret);
|
||||
}
|
||||
|
|
|
@ -59,8 +59,12 @@ udp_socket::udp_socket(asio::io_service& ios
|
|||
: m_callback(c)
|
||||
, m_callback2(c2)
|
||||
, m_ipv4_sock(ios)
|
||||
, m_v4_buf_size(0)
|
||||
, m_v4_buf(0)
|
||||
#if TORRENT_USE_IPV6
|
||||
, m_ipv6_sock(ios)
|
||||
, m_v6_buf_size(0)
|
||||
, m_v6_buf(0)
|
||||
#endif
|
||||
, m_bind_port(0)
|
||||
, m_outstanding(0)
|
||||
|
@ -71,6 +75,7 @@ udp_socket::udp_socket(asio::io_service& ios
|
|||
, m_queue_packets(false)
|
||||
, m_tunnel_packets(false)
|
||||
, m_abort(false)
|
||||
, m_reallocate_buffers(false)
|
||||
{
|
||||
#ifdef TORRENT_DEBUG
|
||||
m_magic = 0x1337;
|
||||
|
@ -79,11 +84,22 @@ udp_socket::udp_socket(asio::io_service& ios
|
|||
#if defined BOOST_HAS_PTHREADS
|
||||
m_thread = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
m_v4_buf_size = 1600;
|
||||
m_v4_buf = (char*)malloc(m_v4_buf_size);
|
||||
#if TORRENT_USE_IPV6
|
||||
m_v6_buf_size = 1600;
|
||||
m_v6_buf = (char*)malloc(m_v6_buf_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
udp_socket::~udp_socket()
|
||||
{
|
||||
free(m_v4_buf);
|
||||
#if TORRENT_USE_IPV6
|
||||
free(m_v6_buf);
|
||||
#endif
|
||||
#ifdef TORRENT_DEBUG
|
||||
TORRENT_ASSERT(m_magic == 0x1337);
|
||||
TORRENT_ASSERT(!m_callback || !m_started);
|
||||
|
@ -168,6 +184,18 @@ void udp_socket::send(udp::endpoint const& ep, char const* p, int len, error_cod
|
|||
#endif
|
||||
}
|
||||
|
||||
void udp_socket::maybe_realloc_buffers()
|
||||
{
|
||||
if (m_reallocate_buffers)
|
||||
{
|
||||
m_v4_buf = (char*)realloc(m_v4_buf, m_v4_buf_size);
|
||||
#if TORRENT_USE_IPV6
|
||||
m_v6_buf = (char*)realloc(m_v6_buf, m_v6_buf_size);
|
||||
#endif
|
||||
m_reallocate_buffers = false;
|
||||
}
|
||||
}
|
||||
|
||||
void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_transferred)
|
||||
{
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
|
@ -211,6 +239,7 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_
|
|||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch(std::exception&) {}
|
||||
#endif
|
||||
|
||||
// don't stop listening on recoverable errors
|
||||
if (e != asio::error::host_unreachable
|
||||
&& e != asio::error::fault
|
||||
|
@ -230,17 +259,19 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_
|
|||
|
||||
if (m_abort) return;
|
||||
|
||||
maybe_realloc_buffers();
|
||||
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("udp_socket::on_read");
|
||||
#endif
|
||||
#if TORRENT_USE_IPV6
|
||||
if (s == &m_ipv4_sock)
|
||||
#endif
|
||||
s->async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
s->async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size)
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
#if TORRENT_USE_IPV6
|
||||
else
|
||||
s->async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
s->async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size)
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
#endif
|
||||
|
||||
|
@ -277,10 +308,12 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_
|
|||
|
||||
if (m_abort) return;
|
||||
|
||||
maybe_realloc_buffers();
|
||||
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("udp_socket::on_read");
|
||||
#endif
|
||||
s->async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
s->async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size)
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
}
|
||||
#if TORRENT_USE_IPV6
|
||||
|
@ -307,10 +340,12 @@ void udp_socket::on_read(udp::socket* s, error_code const& e, std::size_t bytes_
|
|||
|
||||
if (m_abort) return;
|
||||
|
||||
maybe_realloc_buffers();
|
||||
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("udp_socket::on_read");
|
||||
#endif
|
||||
s->async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
s->async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size)
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, s, _1, _2));
|
||||
}
|
||||
#endif
|
||||
|
@ -421,6 +456,10 @@ void udp_socket::unwrap(error_code const& e, char const* buf, int size)
|
|||
m_callback(e, sender, p, size - (p - buf));
|
||||
}
|
||||
|
||||
#ifndef BOOST_ASIO_ENABLE_CANCELIO
|
||||
#error BOOST_ASIO_ENABLE_CANCELIO needs to be defined when building libtorrent to enable cancel() in asio on windows
|
||||
#endif
|
||||
|
||||
void udp_socket::close()
|
||||
{
|
||||
TORRENT_ASSERT(is_single_thread());
|
||||
|
@ -461,6 +500,18 @@ void udp_socket::close()
|
|||
}
|
||||
}
|
||||
|
||||
void udp_socket::set_buf_size(int s)
|
||||
{
|
||||
if (s > m_v4_buf_size)
|
||||
{
|
||||
m_v4_buf_size = s;
|
||||
#if TORRENT_USE_IPV6
|
||||
m_v6_buf_size = s;
|
||||
#endif
|
||||
m_reallocate_buffers = true;
|
||||
}
|
||||
}
|
||||
|
||||
void udp_socket::bind(udp::endpoint const& ep, error_code& ec)
|
||||
{
|
||||
CHECK_MAGIC;
|
||||
|
@ -474,6 +525,8 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec)
|
|||
if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec);
|
||||
#endif
|
||||
|
||||
maybe_realloc_buffers();
|
||||
|
||||
if (ep.address().is_v4())
|
||||
{
|
||||
m_ipv4_sock.open(udp::v4(), ec);
|
||||
|
@ -483,7 +536,7 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec)
|
|||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("udp_socket::on_read");
|
||||
#endif
|
||||
m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size)
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, &m_ipv4_sock, _1, _2));
|
||||
++m_outstanding;
|
||||
}
|
||||
|
@ -497,7 +550,7 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec)
|
|||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("udp_socket::on_read");
|
||||
#endif
|
||||
m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size)
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2));
|
||||
++m_outstanding;
|
||||
}
|
||||
|
@ -523,6 +576,8 @@ void udp_socket::bind(int port)
|
|||
if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec);
|
||||
#endif
|
||||
|
||||
maybe_realloc_buffers();
|
||||
|
||||
m_ipv4_sock.open(udp::v4(), ec);
|
||||
if (!ec)
|
||||
{
|
||||
|
@ -530,7 +585,7 @@ void udp_socket::bind(int port)
|
|||
add_outstanding_async("udp_socket::on_read");
|
||||
#endif
|
||||
m_ipv4_sock.bind(udp::endpoint(address_v4::any(), port), ec);
|
||||
m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, sizeof(m_v4_buf))
|
||||
m_ipv4_sock.async_receive_from(asio::buffer(m_v4_buf, m_v4_buf_size)
|
||||
, m_v4_ep, boost::bind(&udp_socket::on_read, this, &m_ipv4_sock, _1, _2));
|
||||
++m_outstanding;
|
||||
#ifdef TORRENT_DEBUG
|
||||
|
@ -546,7 +601,7 @@ void udp_socket::bind(int port)
|
|||
#endif
|
||||
m_ipv6_sock.set_option(v6only(true), ec);
|
||||
m_ipv6_sock.bind(udp::endpoint(address_v6::any(), port), ec);
|
||||
m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, sizeof(m_v6_buf))
|
||||
m_ipv6_sock.async_receive_from(asio::buffer(m_v6_buf, m_v6_buf_size)
|
||||
, m_v6_ep, boost::bind(&udp_socket::on_read, this, &m_ipv6_sock, _1, _2));
|
||||
++m_outstanding;
|
||||
#ifdef TORRENT_DEBUG
|
||||
|
|
|
@ -210,6 +210,8 @@ namespace libtorrent { namespace
|
|||
, m_tp(tp)
|
||||
{}
|
||||
|
||||
virtual char const* type() const { return "ut_metadata"; }
|
||||
|
||||
// can add entries to the extension handshake
|
||||
virtual void add_handshake(entry& h)
|
||||
{
|
||||
|
|
131
src/ut_pex.cpp
131
src/ut_pex.cpp
|
@ -145,10 +145,20 @@ namespace libtorrent { namespace
|
|||
// no supported flags to set yet
|
||||
// 0x01 - peer supports encryption
|
||||
// 0x02 - peer is a seed
|
||||
// 0x04 - supports uTP. This is only a positive flags
|
||||
// passing 0 doesn't mean the peer doesn't
|
||||
// support uTP
|
||||
// 0x08 - supports holepunching protocol. If this
|
||||
// flag is received from a peer, it can be
|
||||
// used as a rendezvous point in case direct
|
||||
// connections to the peer fail
|
||||
int flags = p->is_seed() ? 2 : 0;
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
flags |= p->supports_encryption() ? 1 : 0;
|
||||
#endif
|
||||
flags |= p->get_socket()->get<utp_stream>() ? 4 : 0;
|
||||
flags |= p->supports_holepunch() ? 8 : 0;
|
||||
|
||||
// i->first was added since the last time
|
||||
if (remote.address().is_v4())
|
||||
{
|
||||
|
@ -205,11 +215,14 @@ namespace libtorrent { namespace
|
|||
: m_torrent(t)
|
||||
, m_pc(pc)
|
||||
, m_tp(tp)
|
||||
, m_last_pex(min_time())
|
||||
, m_1_minute(55)
|
||||
, m_message_index(0)
|
||||
, m_first_time(true)
|
||||
{}
|
||||
|
||||
virtual char const* type() const { return "ut_pex"; }
|
||||
|
||||
virtual void add_handshake(entry& h)
|
||||
{
|
||||
entry& messages = h["m"];
|
||||
|
@ -239,9 +252,20 @@ namespace libtorrent { namespace
|
|||
m_pc.disconnect(errors::pex_message_too_large, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
ptime now = time_now();
|
||||
if (now - m_last_pex < seconds(10))
|
||||
{
|
||||
// this client appears to be trying to flood us
|
||||
// with pex messages. Don't allow that.
|
||||
m_pc.disconnect(errors::too_frequent_pex);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (body.left() < length) return true;
|
||||
|
||||
m_last_pex = now;
|
||||
|
||||
lazy_entry pex_msg;
|
||||
error_code ec;
|
||||
int ret = lazy_bdecode(body.begin, body.end, pex_msg, ec);
|
||||
|
@ -251,13 +275,34 @@ namespace libtorrent { namespace
|
|||
return true;
|
||||
}
|
||||
|
||||
lazy_entry const* p = pex_msg.dict_find("added");
|
||||
lazy_entry const* pf = pex_msg.dict_find("added.f");
|
||||
lazy_entry const* p = pex_msg.dict_find_string("dropped");
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_pc.m_logger) << time_now_string() << " <== PEX ["
|
||||
" dropped:" << (p?p->string_length():0);
|
||||
#endif
|
||||
if (p)
|
||||
{
|
||||
int num_peers = p->string_length() / 6;
|
||||
char const* in = p->string_ptr();
|
||||
|
||||
for (int i = 0; i < num_peers; ++i)
|
||||
{
|
||||
tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
|
||||
peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port());
|
||||
peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v);
|
||||
if (j != m_peers.end() && *j == v) m_peers.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
p = pex_msg.dict_find_string("added");
|
||||
lazy_entry const* pf = pex_msg.dict_find_string("added.f");
|
||||
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
(*m_pc.m_logger) << " added:" << (p?p->string_length():0) << " ]\n";
|
||||
#endif
|
||||
if (p != 0
|
||||
&& pf != 0
|
||||
&& p->type() == lazy_entry::string_t
|
||||
&& pf->type() == lazy_entry::string_t
|
||||
&& pf->string_length() == p->string_length() / 6)
|
||||
{
|
||||
int num_peers = pf->string_length();
|
||||
|
@ -270,14 +315,39 @@ namespace libtorrent { namespace
|
|||
{
|
||||
tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
|
||||
char flags = *fin++;
|
||||
|
||||
if (m_peers.size() >= m_torrent.settings().max_pex_peers) break;
|
||||
|
||||
// ignore local addresses unless the peer is local to us
|
||||
if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue;
|
||||
|
||||
peers4_t::value_type v(adr.address().to_v4().to_bytes(), adr.port());
|
||||
peers4_t::iterator j = std::lower_bound(m_peers.begin(), m_peers.end(), v);
|
||||
// do we already know about this peer?
|
||||
if (j != m_peers.end() && *j == v) continue;
|
||||
m_peers.insert(j, v);
|
||||
p.add_peer(adr, pid, peer_info::pex, flags);
|
||||
}
|
||||
}
|
||||
|
||||
#if TORRENT_USE_IPV6
|
||||
lazy_entry const* p6 = pex_msg.dict_find("added6");
|
||||
|
||||
lazy_entry const* p6 = pex_msg.dict_find("dropped6");
|
||||
if (p6 != 0 && p6->type() == lazy_entry::string_t)
|
||||
{
|
||||
int num_peers = p6->string_length() / 18;
|
||||
char const* in = p6->string_ptr();
|
||||
|
||||
for (int i = 0; i < num_peers; ++i)
|
||||
{
|
||||
tcp::endpoint adr = detail::read_v6_endpoint<tcp::endpoint>(in);
|
||||
peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port());
|
||||
peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v);
|
||||
if (j != m_peers6.end() && *j == v) m_peers6.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
p6 = pex_msg.dict_find("added6");
|
||||
lazy_entry const* p6f = pex_msg.dict_find("added6.f");
|
||||
if (p6 != 0
|
||||
&& p6f != 0
|
||||
|
@ -297,6 +367,13 @@ namespace libtorrent { namespace
|
|||
char flags = *fin++;
|
||||
// ignore local addresses unless the peer is local to us
|
||||
if (is_local(adr.address()) && !is_local(m_pc.remote().address())) continue;
|
||||
if (m_peers6.size() >= m_torrent.settings().max_pex_peers) break;
|
||||
|
||||
peers6_t::value_type v(adr.address().to_v6().to_bytes(), adr.port());
|
||||
peers6_t::iterator j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v);
|
||||
// do we already know about this peer?
|
||||
if (j != m_peers6.end() && *j == v) continue;
|
||||
m_peers6.insert(j, v);
|
||||
p.add_peer(adr, pid, peer_info::pex, flags);
|
||||
}
|
||||
}
|
||||
|
@ -323,8 +400,6 @@ namespace libtorrent { namespace
|
|||
m_1_minute = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void send_ut_peer_diff()
|
||||
{
|
||||
// if there's no change in out peer set, don't send anything
|
||||
|
@ -419,6 +494,23 @@ namespace libtorrent { namespace
|
|||
torrent& m_torrent;
|
||||
peer_connection& m_pc;
|
||||
ut_pex_plugin& m_tp;
|
||||
// stores all peers this this peer is connected to. These lists
|
||||
// are updated with each pex message and are limited in size
|
||||
// to protect against malicious clients. These lists are also
|
||||
// used for looking up which peer a peer that supports holepunch
|
||||
// came from.
|
||||
// these are vectors to save memory and keep the items close
|
||||
// together for performance. Inserting and removing is relatively
|
||||
// cheap since the lists' size is limited
|
||||
typedef std::vector<std::pair<address_v4::bytes_type, boost::uint16_t> > peers4_t;
|
||||
peers4_t m_peers;
|
||||
#if TORRENT_USE_IPV6
|
||||
typedef std::vector<std::pair<address_v6::bytes_type, boost::uint16_t> > peers6_t;
|
||||
peers6_t m_peers6;
|
||||
#endif
|
||||
// the last pex message we received
|
||||
ptime m_last_pex;
|
||||
|
||||
int m_1_minute;
|
||||
int m_message_index;
|
||||
|
||||
|
@ -438,11 +530,10 @@ namespace libtorrent { namespace
|
|||
return boost::shared_ptr<peer_plugin>(new ut_pex_peer_plugin(m_torrent
|
||||
, *pc, *this));
|
||||
}
|
||||
}}
|
||||
} }
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
|
||||
{
|
||||
if (t->torrent_file().priv() || (t->torrent_file().is_i2p()
|
||||
|
@ -453,6 +544,28 @@ namespace libtorrent
|
|||
return boost::shared_ptr<torrent_plugin>(new ut_pex_plugin(*t));
|
||||
}
|
||||
|
||||
bool was_introduced_by(peer_plugin const* pp, tcp::endpoint const& ep)
|
||||
{
|
||||
ut_pex_peer_plugin* p = (ut_pex_peer_plugin*)pp;
|
||||
#if TORRENT_USE_IPV6
|
||||
if (ep.address().is_v4())
|
||||
{
|
||||
#endif
|
||||
ut_pex_peer_plugin::peers4_t::value_type v(ep.address().to_v4().to_bytes(), ep.port());
|
||||
ut_pex_peer_plugin::peers4_t::const_iterator i
|
||||
= std::lower_bound(p->m_peers.begin(), p->m_peers.end(), v);
|
||||
return i != p->m_peers.end() && *i == v;
|
||||
#if TORRENT_USE_IPV6
|
||||
}
|
||||
else
|
||||
{
|
||||
ut_pex_peer_plugin::peers6_t::value_type v(ep.address().to_v6().to_bytes(), ep.port());
|
||||
ut_pex_peer_plugin::peers6_t::iterator i
|
||||
= std::lower_bound(p->m_peers6.begin(), p->m_peers6.end(), v);
|
||||
return i != p->m_peers6.end() && *i == v;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2009, 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 "libtorrent/utp_stream.hpp"
|
||||
#include "libtorrent/udp_socket.hpp"
|
||||
#include "libtorrent/utp_socket_manager.hpp"
|
||||
#include "libtorrent/instantiate_connection.hpp"
|
||||
#include "libtorrent/socket_io.hpp"
|
||||
#include "libtorrent/broadcast_socket.hpp" // for is_teredo
|
||||
|
||||
// #define TORRENT_DEBUG_MTU 1135
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
utp_socket_manager::utp_socket_manager(session_settings const& sett, udp_socket& s
|
||||
, incoming_utp_callback_t cb)
|
||||
: m_sock(s)
|
||||
, m_cb(cb)
|
||||
, m_last_socket(0)
|
||||
, m_new_connection(-1)
|
||||
, m_sett(sett)
|
||||
, m_sock_buf_size(0)
|
||||
{}
|
||||
|
||||
utp_socket_manager::~utp_socket_manager()
|
||||
{
|
||||
for (socket_map_t::iterator i = m_utp_sockets.begin()
|
||||
, end(m_utp_sockets.end()); i != end; ++i)
|
||||
{
|
||||
delete_utp_impl(i->second);
|
||||
}
|
||||
}
|
||||
|
||||
void utp_socket_manager::get_status(utp_status& s) const
|
||||
{
|
||||
s.num_idle = 0;
|
||||
s.num_syn_sent = 0;
|
||||
s.num_connected = 0;
|
||||
s.num_fin_sent = 0;
|
||||
s.num_close_wait = 0;
|
||||
|
||||
for (socket_map_t::const_iterator i = m_utp_sockets.begin()
|
||||
, end(m_utp_sockets.end()); i != end; ++i)
|
||||
{
|
||||
int state = utp_socket_state(i->second);
|
||||
switch (state)
|
||||
{
|
||||
case 0: ++s.num_idle; break;
|
||||
case 1: ++s.num_syn_sent; break;
|
||||
case 2: ++s.num_connected; break;
|
||||
case 3: ++s.num_fin_sent; break;
|
||||
case 4: ++s.num_close_wait; break;
|
||||
case 5: ++s.num_close_wait; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void utp_socket_manager::tick(ptime now)
|
||||
{
|
||||
for (socket_map_t::iterator i = m_utp_sockets.begin()
|
||||
, end(m_utp_sockets.end()); i != end;)
|
||||
{
|
||||
if (should_delete(i->second))
|
||||
{
|
||||
delete_utp_impl(i->second);
|
||||
if (m_last_socket == i->second) m_last_socket = 0;
|
||||
m_utp_sockets.erase(i++);
|
||||
continue;
|
||||
}
|
||||
tick_utp_impl(i->second, now);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void utp_socket_manager::mtu_for_dest(address const& addr, int& link_mtu, int& utp_mtu)
|
||||
{
|
||||
if (time_now() - m_last_route_update > seconds(60))
|
||||
{
|
||||
m_last_route_update = time_now();
|
||||
error_code ec;
|
||||
m_routes = enum_routes(m_sock.get_io_service(), ec);
|
||||
}
|
||||
|
||||
int mtu = 0;
|
||||
if (!m_routes.empty())
|
||||
{
|
||||
for (std::vector<ip_route>::iterator i = m_routes.begin()
|
||||
, end(m_routes.end()); i != end; ++i)
|
||||
{
|
||||
if (!match_addr_mask(addr, i->destination, i->netmask)) continue;
|
||||
|
||||
// assume that we'll actually use the route with the largest
|
||||
// MTU (seems like a reasonable assumption).
|
||||
// this could however be improved by using the route metrics
|
||||
// and the prefix length of the netmask to order the matches
|
||||
if (mtu < i->mtu) mtu = i->mtu;
|
||||
}
|
||||
}
|
||||
|
||||
if (mtu == 0)
|
||||
{
|
||||
if (is_teredo(addr)) mtu = TORRENT_TEREDO_MTU;
|
||||
else mtu = TORRENT_ETHERNET_MTU;
|
||||
}
|
||||
|
||||
// clamp the MTU within reasonable bounds
|
||||
if (mtu < TORRENT_INET_MIN_MTU) mtu = TORRENT_INET_MIN_MTU;
|
||||
else if (mtu > TORRENT_INET_MAX_MTU) mtu = TORRENT_INET_MAX_MTU;
|
||||
|
||||
link_mtu = mtu;
|
||||
|
||||
mtu -= TORRENT_UDP_HEADER;
|
||||
|
||||
if (m_sock.get_proxy_settings().type == proxy_settings::socks5
|
||||
|| m_sock.get_proxy_settings().type == proxy_settings::socks5_pw)
|
||||
{
|
||||
// this is for the IP layer
|
||||
address proxy_addr = m_sock.proxy_addr().address();
|
||||
if (proxy_addr.is_v4()) mtu -= TORRENT_IPV4_HEADER;
|
||||
else mtu -= TORRENT_IPV6_HEADER;
|
||||
|
||||
// this is for the SOCKS layer
|
||||
mtu -= TORRENT_SOCKS5_HEADER;
|
||||
|
||||
// the address field in the SOCKS header
|
||||
if (addr.is_v4()) mtu -= 4;
|
||||
else mtu -= 16;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (addr.is_v4()) mtu -= TORRENT_IPV4_HEADER;
|
||||
else mtu -= TORRENT_IPV6_HEADER;
|
||||
}
|
||||
|
||||
utp_mtu = mtu;
|
||||
}
|
||||
|
||||
void utp_socket_manager::send_packet(udp::endpoint const& ep, char const* p
|
||||
, int len, error_code& ec, int flags)
|
||||
{
|
||||
if (!m_sock.is_open())
|
||||
{
|
||||
ec = asio::error::operation_aborted;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TORRENT_DEBUG_MTU
|
||||
// drop packets that exceed the debug MTU
|
||||
if ((flags & dont_fragment) && len > TORRENT_DEBUG_MTU) return;
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_HAS_DONT_FRAGMENT
|
||||
error_code tmp;
|
||||
if (flags & utp_socket_manager::dont_fragment)
|
||||
m_sock.set_option(libtorrent::dont_fragment(true), tmp);
|
||||
#endif
|
||||
m_sock.send(ep, p, len, ec);
|
||||
#ifdef TORRENT_HAS_DONT_FRAGMENT
|
||||
if (flags & utp_socket_manager::dont_fragment)
|
||||
m_sock.set_option(libtorrent::dont_fragment(false), tmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
tcp::endpoint utp_socket_manager::local_endpoint(error_code& ec) const
|
||||
{
|
||||
return m_sock.local_endpoint(ec);
|
||||
}
|
||||
|
||||
bool utp_socket_manager::incoming_packet(char const* p, int size, udp::endpoint const& ep)
|
||||
{
|
||||
// UTP_LOGV("incoming packet size:%d\n", size);
|
||||
|
||||
if (size < sizeof(utp_header)) return false;
|
||||
|
||||
utp_header const* ph = (utp_header*)p;
|
||||
|
||||
// UTP_LOGV("incoming packet version:%d\n", int(ph->get_version()));
|
||||
|
||||
if (ph->get_version() != 1) return false;
|
||||
|
||||
const ptime receive_time = time_now_hires();
|
||||
|
||||
// parse out connection ID and look for existing
|
||||
// connections. If found, forward to the utp_stream.
|
||||
boost::uint16_t id = ph->connection_id;
|
||||
|
||||
// first test to see if it's the same socket as last time
|
||||
// in most cases it is
|
||||
if (m_last_socket
|
||||
&& utp_match(m_last_socket, ep, id))
|
||||
{
|
||||
return utp_incoming_packet(m_last_socket, p, size, ep, receive_time);
|
||||
}
|
||||
|
||||
socket_map_t::iterator i = m_utp_sockets.find(id);
|
||||
|
||||
std::pair<socket_map_t::iterator, socket_map_t::iterator> r =
|
||||
m_utp_sockets.equal_range(id);
|
||||
|
||||
for (; r.first != r.second; ++r.first)
|
||||
{
|
||||
if (!utp_match(r.first->second, ep, id)) continue;
|
||||
bool ret = utp_incoming_packet(r.first->second, p, size, ep, receive_time);
|
||||
if (ret) m_last_socket = r.first->second;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// UTP_LOGV("incoming packet id:%d source:%s\n", id, print_endpoint(ep).c_str());
|
||||
|
||||
if (!m_sett.enable_incoming_utp)
|
||||
return false;
|
||||
|
||||
// if not found, see if it's a SYN packet, if it is,
|
||||
// create a new utp_stream
|
||||
if (ph->get_type() == ST_SYN)
|
||||
{
|
||||
// create the new socket with this ID
|
||||
m_new_connection = id;
|
||||
|
||||
// UTP_LOGV("not found, new connection id:%d\n", m_new_connection);
|
||||
|
||||
boost::shared_ptr<socket_type> c(new (std::nothrow) socket_type(m_sock.get_io_service()));
|
||||
if (!c) return false;
|
||||
instantiate_connection(m_sock.get_io_service(), proxy_settings(), *c, 0, this);
|
||||
utp_stream* str = c->get<utp_stream>();
|
||||
TORRENT_ASSERT(str);
|
||||
int link_mtu, utp_mtu;
|
||||
mtu_for_dest(ep.address(), link_mtu, utp_mtu);
|
||||
utp_init_mtu(str->get_impl(), link_mtu, utp_mtu);
|
||||
bool ret = utp_incoming_packet(str->get_impl(), p, size, ep, receive_time);
|
||||
if (!ret) return false;
|
||||
m_cb(c);
|
||||
// the connection most likely changed its connection ID here
|
||||
// we need to move it to the correct ID
|
||||
return true;
|
||||
}
|
||||
|
||||
// #error send reset
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void utp_socket_manager::remove_socket(boost::uint16_t id)
|
||||
{
|
||||
socket_map_t::iterator i = m_utp_sockets.find(id);
|
||||
if (i == m_utp_sockets.end()) return;
|
||||
delete_utp_impl(i->second);
|
||||
if (m_last_socket == i->second) m_last_socket = 0;
|
||||
m_utp_sockets.erase(i);
|
||||
}
|
||||
|
||||
void utp_socket_manager::set_sock_buf(int size)
|
||||
{
|
||||
if (size < m_sock_buf_size) return;
|
||||
m_sock.set_buf_size(size);
|
||||
error_code ec;
|
||||
// add more socket buffer storage on the lower level socket
|
||||
// to avoid dropping packets because of a full receive buffer
|
||||
// while processing a packet
|
||||
|
||||
// only update the buffer size if it's bigger than
|
||||
// what we already have
|
||||
datagram_socket::receive_buffer_size recv_buf_size_opt;
|
||||
m_sock.get_option(recv_buf_size_opt, ec);
|
||||
if (recv_buf_size_opt.value() < size * 10)
|
||||
{
|
||||
m_sock.set_option(datagram_socket::receive_buffer_size(size * 10), ec);
|
||||
m_sock.set_option(datagram_socket::send_buffer_size(size * 3), ec);
|
||||
}
|
||||
m_sock_buf_size = size;
|
||||
}
|
||||
|
||||
utp_socket_impl* utp_socket_manager::new_utp_socket(utp_stream* str)
|
||||
{
|
||||
boost::uint16_t send_id = 0;
|
||||
boost::uint16_t recv_id = 0;
|
||||
if (m_new_connection != -1)
|
||||
{
|
||||
send_id = m_new_connection;
|
||||
recv_id = m_new_connection + 1;
|
||||
m_new_connection = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
send_id = rand();
|
||||
recv_id = send_id - 1;
|
||||
}
|
||||
utp_socket_impl* impl = construct_utp_impl(recv_id, send_id, str, this);
|
||||
m_utp_sockets.insert(std::make_pair(recv_id, impl));
|
||||
return impl;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -34,6 +34,7 @@ test-suite libtorrent :
|
|||
[ run test_bdecode_performance.cpp ]
|
||||
[ run test_pe_crypto.cpp ]
|
||||
|
||||
[ run test_utp.cpp ]
|
||||
[ run test_auto_unchoke.cpp ]
|
||||
[ run test_http_connection.cpp ]
|
||||
[ run test_torrent.cpp ]
|
||||
|
|
|
@ -241,9 +241,9 @@ setup_transfer(session* ses1, session* ses2, session* ses3
|
|||
ses1->set_settings(sess_set);
|
||||
ses2->set_settings(sess_set);
|
||||
if (ses3) ses3->set_settings(sess_set);
|
||||
ses1->set_alert_mask(~alert::progress_notification);
|
||||
ses2->set_alert_mask(~alert::progress_notification);
|
||||
if (ses3) ses3->set_alert_mask(~alert::progress_notification);
|
||||
ses1->set_alert_mask(~(alert::progress_notification | alert::stats_notification));
|
||||
ses2->set_alert_mask(~(alert::progress_notification | alert::stats_notification));
|
||||
if (ses3) ses3->set_alert_mask(~(alert::progress_notification | alert::stats_notification));
|
||||
|
||||
std::srand(time(0));
|
||||
peer_id pid;
|
||||
|
@ -289,9 +289,11 @@ setup_transfer(session* ses1, session* ses2, session* ses3
|
|||
if (p) param = *p;
|
||||
param.ti = clone_ptr(t);
|
||||
param.save_path = "./tmp1" + suffix;
|
||||
param.seed_mode = true;
|
||||
error_code ec;
|
||||
torrent_handle tor1 = ses1->add_torrent(param, ec);
|
||||
tor1.super_seeding(super_seeding);
|
||||
param.seed_mode = false;
|
||||
TEST_CHECK(!ses1->get_torrents().empty());
|
||||
torrent_handle tor2;
|
||||
torrent_handle tor3;
|
||||
|
@ -753,7 +755,7 @@ void web_server_thread(int* port, bool ssl, bool chunked)
|
|||
|
||||
while (!p.finished())
|
||||
{
|
||||
TORRENT_ASSERT(len < sizeof(buf));
|
||||
TORRENT_ASSERT(len < int(sizeof(buf)));
|
||||
size_t received = s.read_some(boost::asio::buffer(&buf[len]
|
||||
, sizeof(buf) - len), ec);
|
||||
// fprintf(stderr, "read: %d\n", int(received));
|
||||
|
|
|
@ -43,8 +43,11 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#include "libtorrent/broadcast_socket.hpp"
|
||||
#include "libtorrent/identify_client.hpp"
|
||||
#include "libtorrent/file.hpp"
|
||||
#include "libtorrent/packet_buffer.hpp"
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/timestamp_history.hpp"
|
||||
#include "libtorrent/enum_net.hpp"
|
||||
#ifndef TORRENT_DISABLE_DHT
|
||||
#include "libtorrent/kademlia/node_id.hpp"
|
||||
#include "libtorrent/kademlia/routing_table.hpp"
|
||||
|
@ -382,6 +385,98 @@ int test_main()
|
|||
error_code ec;
|
||||
int ret = 0;
|
||||
|
||||
// test timestamp_history
|
||||
{
|
||||
timestamp_history h;
|
||||
TEST_EQUAL(h.add_sample(0x32, false), 0);
|
||||
TEST_EQUAL(h.base(), 0x32);
|
||||
TEST_EQUAL(h.add_sample(0x33, false), 0x1);
|
||||
TEST_EQUAL(h.base(), 0x32);
|
||||
TEST_EQUAL(h.add_sample(0x3433, false), 0x3401);
|
||||
TEST_EQUAL(h.base(), 0x32);
|
||||
TEST_EQUAL(h.add_sample(0x30, false), 0);
|
||||
TEST_EQUAL(h.base(), 0x30);
|
||||
|
||||
// test that wrapping of the timestamp is properly handled
|
||||
h.add_sample(0xfffffff3, false);
|
||||
TEST_EQUAL(h.base(), 0xfffffff3);
|
||||
}
|
||||
|
||||
// test packet_buffer
|
||||
{
|
||||
packet_buffer pb;
|
||||
|
||||
TEST_EQUAL(pb.capacity(), 0);
|
||||
TEST_EQUAL(pb.size(), 0);
|
||||
TEST_EQUAL(pb.span(), 0);
|
||||
|
||||
pb.insert(123, (void*)123);
|
||||
TEST_EQUAL(pb.at(123 + 16), 0);
|
||||
|
||||
TEST_CHECK(pb.at(123) == (void*)123);
|
||||
TEST_CHECK(pb.capacity() > 0);
|
||||
TEST_EQUAL(pb.size(), 1);
|
||||
TEST_EQUAL(pb.span(), 1);
|
||||
TEST_EQUAL(pb.cursor(), 123);
|
||||
|
||||
pb.insert(125, (void*)125);
|
||||
|
||||
TEST_CHECK(pb.at(125) == (void*)125);
|
||||
TEST_EQUAL(pb.size(), 2);
|
||||
TEST_EQUAL(pb.span(), 3);
|
||||
TEST_EQUAL(pb.cursor(), 123);
|
||||
|
||||
pb.insert(500, (void*)500);
|
||||
TEST_EQUAL(pb.size(), 3);
|
||||
TEST_EQUAL(pb.span(), 501 - 123);
|
||||
TEST_EQUAL(pb.capacity(), 512);
|
||||
|
||||
TEST_CHECK(pb.remove(123) == (void*)123);
|
||||
TEST_EQUAL(pb.size(), 2);
|
||||
TEST_EQUAL(pb.span(), 501 - 125);
|
||||
TEST_EQUAL(pb.cursor(), 125);
|
||||
TEST_CHECK(pb.remove(125) == (void*)125);
|
||||
TEST_EQUAL(pb.size(), 1);
|
||||
TEST_EQUAL(pb.span(), 1);
|
||||
TEST_EQUAL(pb.cursor(), 500);
|
||||
|
||||
TEST_CHECK(pb.remove(500) == (void*)500);
|
||||
TEST_EQUAL(pb.size(), 0);
|
||||
TEST_EQUAL(pb.span(), 0);
|
||||
|
||||
for (int i = 0; i < 0xff; ++i)
|
||||
{
|
||||
int index = (i + 0xfff0) & 0xffff;
|
||||
pb.insert(index, (void*)(index + 1));
|
||||
fprintf(stderr, "insert: %u (mask: %x)\n", index, int(pb.capacity() - 1));
|
||||
TEST_EQUAL(pb.capacity(), 512);
|
||||
if (i >= 14)
|
||||
{
|
||||
index = (index - 14) & 0xffff;
|
||||
fprintf(stderr, "remove: %u\n", index);
|
||||
TEST_CHECK(pb.remove(index) == (void*)(index + 1));
|
||||
TEST_EQUAL(pb.size(), 14);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// test wrapping the indices
|
||||
packet_buffer pb;
|
||||
|
||||
TEST_EQUAL(pb.size(), 0);
|
||||
|
||||
pb.insert(0xfffe, (void*)1);
|
||||
TEST_CHECK(pb.at(0xfffe) == (void*)1);
|
||||
|
||||
pb.insert(2, (void*)2);
|
||||
TEST_CHECK(pb.at(2) == (void*)2);
|
||||
|
||||
pb.remove(0xfffe);
|
||||
TEST_CHECK(pb.at(0xfffe) == (void*)0);
|
||||
TEST_CHECK(pb.at(2) == (void*)2);
|
||||
}
|
||||
|
||||
TEST_CHECK(error_code(errors::http_error).message() == "HTTP error");
|
||||
TEST_CHECK(error_code(errors::missing_file_sizes).message() == "missing or invalid 'file sizes' entry");
|
||||
TEST_CHECK(error_code(errors::unsupported_protocol_version).message() == "unsupported protocol version");
|
||||
|
@ -391,7 +486,7 @@ int test_main()
|
|||
|
||||
TEST_CHECK(errors::reserved129 == 129);
|
||||
TEST_CHECK(errors::reserved159 == 159);
|
||||
TEST_CHECK(errors::reserved108 == 108);
|
||||
TEST_CHECK(errors::reserved109 == 109);
|
||||
|
||||
{
|
||||
// test session state load/restore
|
||||
|
@ -1062,6 +1157,16 @@ int test_main()
|
|||
#endif
|
||||
TEST_CHECK(is_any(address_v4::any()));
|
||||
TEST_CHECK(!is_any(address::from_string("31.53.21.64", ec)));
|
||||
|
||||
TEST_CHECK(match_addr_mask(
|
||||
address::from_string("10.0.1.3", ec),
|
||||
address::from_string("10.0.3.3", ec),
|
||||
address::from_string("255.255.0.0", ec)));
|
||||
|
||||
TEST_CHECK(!match_addr_mask(
|
||||
address::from_string("10.0.1.3", ec),
|
||||
address::from_string("10.1.3.3", ec),
|
||||
address::from_string("255.255.0.0", ec)));
|
||||
|
||||
// test torrent parsing
|
||||
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2008, 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 "libtorrent/session.hpp"
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
#include "libtorrent/hasher.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/bencode.hpp"
|
||||
#include "libtorrent/thread.hpp"
|
||||
#include "libtorrent/time.hpp"
|
||||
#include "libtorrent/file.hpp"
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "test.hpp"
|
||||
#include "setup_transfer.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace libtorrent;
|
||||
using boost::tuples::ignore;
|
||||
|
||||
void test_transfer()
|
||||
{
|
||||
// in case the previous run was terminated
|
||||
error_code ec;
|
||||
remove_all("./tmp1_utp", ec);
|
||||
remove_all("./tmp2_utp", ec);
|
||||
|
||||
session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48885, 49930), "0.0.0.0", 0);
|
||||
session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49885, 50930), "0.0.0.0", 0);
|
||||
|
||||
session_settings sett;
|
||||
|
||||
sett.enable_outgoing_tcp = false;
|
||||
sett.min_reconnect_time = 1;
|
||||
sett.announce_to_all_trackers = true;
|
||||
sett.announce_to_all_tiers = true;
|
||||
// make sure we announce to both http and udp trackers
|
||||
sett.prefer_udp_trackers = false;
|
||||
|
||||
// for performance testing
|
||||
// sett.disable_hash_checks = true;
|
||||
// sett.utp_delayed_ack = 0;
|
||||
|
||||
// disable this to use regular size packets over loopback
|
||||
// sett.utp_dynamic_sock_buf = false;
|
||||
|
||||
ses1.set_settings(sett);
|
||||
ses2.set_settings(sett);
|
||||
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
pe_settings pes;
|
||||
pes.out_enc_policy = pe_settings::disabled;
|
||||
pes.in_enc_policy = pe_settings::disabled;
|
||||
ses1.set_pe_settings(pes);
|
||||
ses2.set_pe_settings(pes);
|
||||
#endif
|
||||
|
||||
torrent_handle tor1;
|
||||
torrent_handle tor2;
|
||||
|
||||
create_directory("./tmp1_utp", ec);
|
||||
std::ofstream file("./tmp1_utp/temporary");
|
||||
boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 1000, false);
|
||||
file.close();
|
||||
|
||||
// for performance testing
|
||||
add_torrent_params atp;
|
||||
// atp.storage = &disabled_storage_constructor;
|
||||
|
||||
// test using piece sizes smaller than 16kB
|
||||
boost::tie(tor1, tor2, ignore) = setup_transfer(&ses1, &ses2, 0
|
||||
, true, false, true, "_utp", 8 * 1024, &t, false, &atp);
|
||||
|
||||
for (int i = 0; i < 300; ++i)
|
||||
{
|
||||
print_alerts(ses1, "ses1", true, true, true);
|
||||
print_alerts(ses2, "ses2", true, true, true);
|
||||
|
||||
torrent_status st1 = tor1.status();
|
||||
torrent_status st2 = tor2.status();
|
||||
|
||||
std::cerr
|
||||
<< "\033[32m" << int(st1.download_payload_rate / 1000.f) << "kB/s "
|
||||
<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
|
||||
<< "\033[0m" << int(st1.progress * 100) << "% "
|
||||
<< st1.num_peers
|
||||
<< ": "
|
||||
<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
|
||||
<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
|
||||
<< "\033[0m" << int(st2.progress * 100) << "% "
|
||||
<< st2.num_peers
|
||||
<< " cc: " << st2.connect_candidates
|
||||
<< std::endl;
|
||||
|
||||
if (st2.is_finished) break;
|
||||
|
||||
TEST_CHECK(st1.state == torrent_status::seeding
|
||||
|| st1.state == torrent_status::checking_files);
|
||||
TEST_CHECK(st2.state == torrent_status::downloading);
|
||||
|
||||
test_sleep(500);
|
||||
}
|
||||
|
||||
TEST_CHECK(tor1.status().is_finished);
|
||||
TEST_CHECK(tor2.status().is_finished);
|
||||
}
|
||||
|
||||
int test_main()
|
||||
{
|
||||
using namespace libtorrent;
|
||||
|
||||
test_transfer();
|
||||
|
||||
error_code ec;
|
||||
remove_all("./tmp1_utp", ec);
|
||||
remove_all("./tmp2_utp", ec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue