From e5f980d80d45261dc266f5c44fc17ef3b4f9cb72 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Mon, 29 Nov 2010 01:33:05 +0000 Subject: [PATCH] merged uTP branch into trunk (yay) --- CMakeLists.txt | 2 + ChangeLog | 3 + Jamfile | 10 +- configure.ac | 3 + docs/cwnd.png | Bin 0 -> 18998 bytes docs/cwnd_thumb.png | Bin 0 -> 30207 bytes docs/delays.png | Bin 0 -> 10875 bytes docs/delays_thumb.png | Bin 0 -> 10938 bytes docs/features.html | 29 +- docs/features.rst | 4 + docs/index.html | 1 + docs/index.rst | 2 + docs/make_torrent.html | 11 +- docs/make_torrent.rst | 1 + docs/makefile | 1 + docs/manual.html | 1063 +++--- docs/manual.rst | 133 +- docs/our_delay_base.png | Bin 0 -> 34999 bytes docs/our_delay_base_thumb.png | Bin 0 -> 55460 bytes docs/tuning.html | 10 + docs/utp.html | 342 ++ docs/utp.rst | 347 ++ examples/Makefile.am | 3 +- examples/client_test.cpp | 30 +- examples/enum_if.cpp | 10 +- examples/utp_test.cpp | 48 + include/libtorrent/Makefile.am | 3 + include/libtorrent/assert.hpp | 6 + include/libtorrent/aux_/session_impl.hpp | 12 + include/libtorrent/broadcast_socket.hpp | 1 + include/libtorrent/bt_peer_connection.hpp | 30 +- include/libtorrent/enum_net.hpp | 8 +- include/libtorrent/error_code.hpp | 2 +- include/libtorrent/extensions.hpp | 2 + include/libtorrent/instantiate_connection.hpp | 4 +- include/libtorrent/max.hpp | 12 + include/libtorrent/packet_buffer.hpp | 108 + include/libtorrent/peer_connection.hpp | 35 +- include/libtorrent/peer_info.hpp | 6 +- include/libtorrent/policy.hpp | 8 +- include/libtorrent/session_settings.hpp | 89 + include/libtorrent/session_status.hpp | 12 + include/libtorrent/sliding_average.hpp | 5 + include/libtorrent/socket.hpp | 34 + include/libtorrent/socket_type.hpp | 26 +- include/libtorrent/timestamp_history.hpp | 80 + include/libtorrent/torrent.hpp | 12 +- include/libtorrent/udp_socket.hpp | 35 +- include/libtorrent/utp_socket_manager.hpp | 116 + include/libtorrent/utp_stream.hpp | 386 +++ parse_sample.py | 6 + set_version.py | 2 + src/Makefile.am | 4 + src/broadcast_socket.cpp | 12 + src/bt_peer_connection.cpp | 261 +- src/enum_net.cpp | 368 ++- src/error_code.cpp | 2 +- src/instantiate_connection.cpp | 11 +- src/metadata_transfer.cpp | 2 + src/packet_buffer.cpp | 189 ++ src/peer_connection.cpp | 197 +- src/policy.cpp | 21 +- src/session.cpp | 6 + src/session_impl.cpp | 94 +- src/socket_type.cpp | 11 +- src/storage.cpp | 2 +- src/timestamp_history.cpp | 105 + src/torrent.cpp | 52 +- src/udp_socket.cpp | 71 +- src/ut_metadata.cpp | 2 + src/ut_pex.cpp | 131 +- src/utp_socket_manager.cpp | 324 ++ src/utp_stream.cpp | 2900 +++++++++++++++++ test/Jamfile | 1 + test/setup_transfer.cpp | 10 +- test/test_primitives.cpp | 107 +- test/test_utp.cpp | 151 + 77 files changed, 7497 insertions(+), 630 deletions(-) create mode 100644 docs/cwnd.png create mode 100644 docs/cwnd_thumb.png create mode 100644 docs/delays.png create mode 100644 docs/delays_thumb.png create mode 100644 docs/our_delay_base.png create mode 100644 docs/our_delay_base_thumb.png create mode 100644 docs/utp.html create mode 100644 docs/utp.rst create mode 100644 examples/utp_test.cpp create mode 100644 include/libtorrent/packet_buffer.hpp create mode 100644 include/libtorrent/timestamp_history.hpp create mode 100644 include/libtorrent/utp_socket_manager.hpp create mode 100644 include/libtorrent/utp_stream.hpp create mode 100644 src/packet_buffer.cpp create mode 100644 src/timestamp_history.cpp create mode 100644 src/utp_socket_manager.cpp create mode 100644 src/utp_stream.cpp create mode 100644 test/test_utp.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 427f3fac0..605f63e18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/ChangeLog b/ChangeLog index df8085e53..d29dc7d16 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/Jamfile b/Jamfile index ce6b967d2..78f792cd2 100755 --- a/Jamfile +++ b/Jamfile @@ -81,8 +81,9 @@ rule linking ( properties * ) { result += ws2_32 wsock32 + iphlpapi WIN32_LEAN_AND_MEAN - _WIN32_WINNT=0x0500 + _WIN32_WINNT=0x0600 __USE_W32_SOCKETS WIN32 _WIN32 @@ -353,6 +354,7 @@ lib GeoIP : : GeoIP shared ; # socket libraries on windows lib wsock32 : : wsock32 shared ; lib ws2_32 : : ws2_32 shared ; +lib iphlpapi : : iphlpapi 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 = debug:TORRENT_DEBUG _FILE_OFFSET_BITS=64 BOOST_EXCEPTION_DISABLE +# enable cancel support in asio + BOOST_ASIO_ENABLE_CANCELIO @linking # these compiler settings just makes the compiler standard conforming msvc:/Zc:wchar_t diff --git a/configure.ac b/configure.ac index aa4a788e2..f09a60faf 100644 --- a/configure.ac +++ b/configure.ac @@ -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"], diff --git a/docs/cwnd.png b/docs/cwnd.png new file mode 100644 index 0000000000000000000000000000000000000000..9f850caa7a84b54f896447625f57a8e54ff3d04e GIT binary patch literal 18998 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 literal 0 HcmV?d00001 diff --git a/docs/delays.png b/docs/delays.png new file mode 100644 index 0000000000000000000000000000000000000000..66db05e98bac6c83bcfc27448a7376d774492013 GIT binary patch literal 10875 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 gesbs>QD*ylh literal 0 HcmV?d00001 diff --git a/docs/delays_thumb.png b/docs/delays_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..84739446cfa90df6736fc1fe7f7aa367062351dd GIT binary patch literal 10938 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 performance disk subsystem
  • network buffers
  • piece picker
  • -
  • merkle hash tree torrents
  • -
  • customizable file storage
  • -
  • easy to use API
  • +
  • share mode
  • +
  • merkle hash tree torrents
  • +
  • customizable file storage
  • +
  • easy to use API
  • -
  • portability
  • +
  • portability
  • @@ -97,9 +98,12 @@ uTorrent interpretation).
  • supports the compact=1 tracker parameter.
  • super seeding/initial seeding (BEP 16).
  • private torrents (BEP 27).
  • +
  • upload-only extension (BEP 21).
  • support for IPv6, including BEP 7 and BEP 24.
  • support for merkle hash tree torrents. This makes the size of torrent files scale well with the size of the content.
  • +
  • share-mode. This is a special mode torrents can be put in to optimize share +ratio rather than downloading the torrent.
  • @@ -124,6 +128,8 @@ piece's hash is verified the first time it is requested.

    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). @@ -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.

      The piece picker can also be set to download pieces in sequential order.

    +
    +

    share mode

    +

    The share mode feature in libtorrent is intended for users who are only interested in +helping out swarms, not downloading the torrents.

    +

    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.

    +

    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.

    +

    merkle hash tree torrents

    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.

  • GCC 2.95.4
  • msvc6
  • +
    +
    +

    Docutils System Messages

    +
    +

    System Message: ERROR/3 (features.rst, line 82); backlink

    +Unknown target name: "bep29".
    @@ -4626,7 +4794,7 @@ public: }; -
    +

    ip_filter()

    @@ -6550,6 +6718,11 @@ potentially better peer
     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:

    @@ -6980,7 +7153,7 @@ int sparse_end(int start) const; region). The purpose of this is to skip parts of files that can be known to contain zeros when checking files.

    -
    +

    move_storage()

    @@ -7057,7 +7230,7 @@ should be moved to slot1This is only used in compact mode.

    Returning true indicates an error occurred.

    -
    +

    rename_file()

    diff --git a/docs/manual.rst b/docs/manual.rst
    index e10c7fd0d..b35afa729 100644
    --- a/docs/manual.rst
    +++ b/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 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:
    diff --git a/docs/our_delay_base.png b/docs/our_delay_base.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..d14ad9ed86ad21a5fed783be6a3beecd1f9c1f96
    GIT binary patch
    literal 34999
    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

    Also make sure to optimize for size when compiling.

    +

    Another way of reducing the executable size is to disable code that isn't used. +There are a number of TORRENT_* 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.

    +

    One, probably, safe macro to define is TORRENT_NO_DEPRECATE 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.

    +

    For all available options, see the building libtorrent secion.

    reduce statistics

    diff --git a/docs/utp.html b/docs/utp.html new file mode 100644 index 000000000..4c6ae138c --- /dev/null +++ b/docs/utp.html @@ -0,0 +1,342 @@ + + + + + + +libtorrent manual + + + + + + + +
    +
    +
    + +
    + +
    +

    libtorrent manual

    + +++ + + + + + +
    Author:Arvid Norberg, arvid@rasterbar.com
    Version:0.16.0
    + +
    +

    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. +
    3. a home router that crashes or slows down by UDP traffic (caused by +the DHT)
    4. +
    5. 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.
    6. +
    +

    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. +
    3. 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.
    4. +
    +

    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)
    +
    +cwnd_thumb.png +

    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.

    +delays_thumb.png +

    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. +
    3. 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.
    4. +
    5. 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).
    6. +
    +

    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. +
    3. 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.
    4. +
    +

    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

    +our_delay_base_thumb.png +

    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.

    +
    +
    +

    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.
    • +
    +
    +
    +
    + +
    + + +
    + + diff --git a/docs/utp.rst b/docs/utp.rst new file mode 100644 index 000000000..b744109b7 --- /dev/null +++ b/docs/utp.rst @@ -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. + diff --git a/examples/Makefile.am b/examples/Makefile.am index 73457a3ba..0c422e523 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -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) diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 6f1de8803..1355ec196 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -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 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 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 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 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 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 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 automatically quit the client after 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 @ 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; diff --git a/examples/enum_if.cpp b/examples/enum_if.cpp index cfd9aa7ec..846596591 100644 --- a/examples/enum_if.cpp +++ b/examples/enum_if.cpp @@ -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::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::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 ":"") diff --git a/examples/utp_test.cpp b/examples/utp_test.cpp new file mode 100644 index 000000000..b26003c4a --- /dev/null +++ b/examples/utp_test.cpp @@ -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 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; +} diff --git a/include/libtorrent/Makefile.am b/include/libtorrent/Makefile.am index 93774e281..9bf0fac2f 100644 --- a/include/libtorrent/Makefile.am +++ b/include/libtorrent/Makefile.am @@ -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 \ diff --git a/include/libtorrent/assert.hpp b/include/libtorrent/assert.hpp index 752fd18e0..f5997bcf4 100644 --- a/include/libtorrent/assert.hpp +++ b/include/libtorrent/assert.hpp @@ -51,11 +51,17 @@ std::string demangle(char const* name); #if (defined __linux__ || defined __MACH__) && defined __GNUC__ +#if TORRENT_USE_IOSTREAM #include +#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 diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index fa0b52ffc..fde526ae5 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -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 @@ -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 diff --git a/include/libtorrent/broadcast_socket.hpp b/include/libtorrent/broadcast_socket.hpp index 040e4d2b6..f560357a9 100644 --- a/include/libtorrent/broadcast_socket.hpp +++ b/include/libtorrent/broadcast_socket.hpp @@ -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 diff --git a/include/libtorrent/bt_peer_connection.hpp b/include/libtorrent/bt_peer_connection.hpp index f5a3f0f85..6373cc089 100644 --- a/include/libtorrent/bt_peer_connection.hpp +++ b/include/libtorrent/bt_peer_connection.hpp @@ -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; diff --git a/include/libtorrent/enum_net.hpp b/include/libtorrent/enum_net.hpp index 68a7dec59..69937d44c 100644 --- a/include/libtorrent/enum_net.hpp +++ b/include/libtorrent/enum_net.hpp @@ -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 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 diff --git a/include/libtorrent/error_code.hpp b/include/libtorrent/error_code.hpp index b323696ee..813c8936f 100644 --- a/include/libtorrent/error_code.hpp +++ b/include/libtorrent/error_code.hpp @@ -165,7 +165,7 @@ namespace libtorrent pex_message_too_large, invalid_pex_message, invalid_lt_tracker_message, - reserved108, + too_frequent_pex, reserved109, reserved110, reserved111, diff --git a/include/libtorrent/extensions.hpp b/include/libtorrent/extensions.hpp index b06a0fc74..10294ece6 100644 --- a/include/libtorrent/extensions.hpp +++ b/include/libtorrent/extensions.hpp @@ -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&) {} diff --git a/include/libtorrent/instantiate_connection.hpp b/include/libtorrent/instantiate_connection.hpp index d0719f38e..a0e95d859 100644 --- a/include/libtorrent/instantiate_connection.hpp +++ b/include/libtorrent/instantiate_connection.hpp @@ -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 diff --git a/include/libtorrent/max.hpp b/include/libtorrent/max.hpp index 64d12cd95..2a7753aa0 100644 --- a/include/libtorrent/max.hpp +++ b/include/libtorrent/max.hpp @@ -93,6 +93,18 @@ namespace libtorrent value = max3::value }; }; + + template + struct max8 + { + enum + { + temp1 = max::value, + temp2 = max3::value, + temp3 = max3::value, + value = max3::value + }; + }; } #endif diff --git a/include/libtorrent/packet_buffer.hpp b/include/libtorrent/packet_buffer.hpp new file mode 100644 index 000000000..1092760dd --- /dev/null +++ b/include/libtorrent/packet_buffer.hpp @@ -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 + +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 + diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index f74da5ef5..8d8d7f27f 100644 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -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 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 - 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; diff --git a/include/libtorrent/peer_info.hpp b/include/libtorrent/peer_info.hpp index 00750d08e..b094e387d 100644 --- a/include/libtorrent/peer_info.hpp +++ b/include/libtorrent/peer_info.hpp @@ -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; diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index b3ce149d7..0f5b0d3a8 100644 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -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 // 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 diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index c9790aced..43a6335e9 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -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. diff --git a/include/libtorrent/session_status.hpp b/include/libtorrent/session_status.hpp index 9cb3849f8..188951503 100644 --- a/include/libtorrent/session_status.hpp +++ b/include/libtorrent/session_status.hpp @@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include "libtorrent/size_type.hpp" +#include 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; }; diff --git a/include/libtorrent/sliding_average.hpp b/include/libtorrent/sliding_average.hpp index 8c0a689d2..3bbdb6c15 100644 --- a/include/libtorrent/sliding_average.hpp +++ b/include/libtorrent/sliding_average.hpp @@ -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 + diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp index b3f7c6fb5..aeaa7e376 100644 --- a/include/libtorrent/socket.hpp +++ b/include/libtorrent/socket.hpp @@ -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 + int level(Protocol const&) const { return IPPROTO_IP; } + template + 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 + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif // TORRENT_HAS_DONT_FRAGMENT } #endif // TORRENT_SOCKET_HPP_INCLUDED diff --git a/include/libtorrent/socket_type.hpp b/include/libtorrent/socket_type.hpp index 26ddcee78..c6244e9be 100644 --- a/include/libtorrent/socket_type.hpp +++ b/include/libtorrent/socket_type.hpp @@ -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()->x; break; \ case socket_type_int_impl::value: \ get()->x; break; \ + case socket_type_int_impl::value: \ + get()->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()->x; \ case socket_type_int_impl::value: \ return get()->x; \ + case socket_type_int_impl::value: \ + return get()->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 { enum { value = 3 }; }; + template <> + struct socket_type_int_impl + { enum { value = 4 }; }; + #if TORRENT_USE_I2P template <> struct socket_type_int_impl - { enum { value = 4 }; }; + { enum { value = 5 }; }; #endif #ifdef TORRENT_USE_OPENSSL template <> struct socket_type_int_impl > - { enum { value = 5 }; }; - - template <> - struct socket_type_int_impl > { enum { value = 6 }; }; template <> - struct socket_type_int_impl > + struct socket_type_int_impl > { enum { value = 7 }; }; + + template <> + struct socket_type_int_impl > + { 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 diff --git a/include/libtorrent/timestamp_history.hpp b/include/libtorrent/timestamp_history.hpp new file mode 100644 index 000000000..0ad977d50 --- /dev/null +++ b/include/libtorrent/timestamp_history.hpp @@ -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 + diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 3ac9e1313..3a9360d39 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -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::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; } diff --git a/include/libtorrent/udp_socket.hpp b/include/libtorrent/udp_socket.hpp index 277996d6c..6dbc8cc36 100644 --- a/include/libtorrent/udp_socket.hpp +++ b/include/libtorrent/udp_socket.hpp @@ -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 + 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 + 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 diff --git a/include/libtorrent/utp_socket_manager.hpp b/include/libtorrent/utp_socket_manager.hpp new file mode 100644 index 000000000..ad8adbcea --- /dev/null +++ b/include/libtorrent/utp_socket_manager.hpp @@ -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 + +#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 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 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 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 + diff --git a/include/libtorrent/utp_stream.hpp b/include/libtorrent/utp_stream.hpp new file mode 100644 index 000000000..e2d3157a0 --- /dev/null +++ b/include/libtorrent/utp_stream.hpp @@ -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 +#include +#include + +#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 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()); + } + private: + char m_storage[sizeof(T)]; + }; + + typedef big_endian_int be_uint64; + typedef big_endian_int be_uint32; + typedef big_endian_int be_uint16; + typedef big_endian_int be_int64; + typedef big_endian_int be_int32; + typedef big_endian_int 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 + void io_control(IO_Control_Command& ioc) {} +#endif + + template + 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 + void set_option(SettableSocketOption const& opt) {} +#endif + + template + 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 + 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(handler, asio::error::operation_not_supported, 0)); + return; + } + + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + m_connect_handler = handler; + do_connect(endpoint, &utp_stream::on_connect); + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + TORRENT_ASSERT(!m_read_handler); + if (m_read_handler) + { + m_io_service.post(boost::bind(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(*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 const& handler); + + template + void open(Protocol const& p, error_code& ec) + { m_open = true; } + + template + void open(Protocol const& p) + { m_open = true; } + + template + 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(*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 + std::size_t write_some(Const_Buffers const& buffers, error_code& ec) + { + // TODO: implement + return 0; + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + if (m_impl == 0) + { + m_io_service.post(boost::bind(handler, asio::error::not_connected, 0)); + return; + } + + TORRENT_ASSERT(!m_write_handler); + if (m_write_handler) + { + m_io_service.post(boost::bind(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(*i), buffer_size(*i)); + } + m_write_handler = handler; + set_write_handler(&utp_stream::on_write); + } + +//private: + + void cancel_handlers(error_code const&); + + boost::function1 m_connect_handler; + boost::function2 m_read_handler; + boost::function2 m_write_handler; + + asio::io_service& m_io_service; + utp_socket_impl* m_impl; + bool m_open; +}; + +} + +#endif diff --git a/parse_sample.py b/parse_sample.py index 3258c9ea3..6310eb38c 100644 --- a/parse_sample.py +++ b/parse_sample.py @@ -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_iteratorget() + ? 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(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(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 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 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 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 } diff --git a/src/enum_net.cpp b/src/enum_net.cpp index 4de007dbc..d786af0b0 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include #include +#include // 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::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 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(); + } + + adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(out_buf_size); + if (!adapter_addresses) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + 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(); + } 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(); + } + + 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(); + } + + 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(); + } + + routes = (MIB_IPFORWARDTABLE*)malloc(out_buf_size); + if (!routes) + { + FreeLibrary(iphlp); + ec = asio::error::no_memory; + return std::vector(); + } + + 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(); } + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + { + ec = error_code(errno, asio::error::system_category); + return std::vector(); + } 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 diff --git a/src/error_code.cpp b/src/error_code.cpp index be7b19784..7d2404a1a 100644 --- a/src/error_code.cpp +++ b/src/error_code.cpp @@ -156,7 +156,7 @@ namespace libtorrent "pex message too large", "invalid pex message", "invalid lt_tracker message", - "", + "pex messages sent too frequent (possible attack)", "", "", "", diff --git a/src/instantiate_connection.cpp b/src/instantiate_connection.cpp index 81a8cbc7a..8842fdbba 100644 --- a/src/instantiate_connection.cpp +++ b/src/instantiate_connection.cpp @@ -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 #include @@ -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(ios); + s.get()->set_impl(sm->new_utp_socket(s.get())); + } + else if (ps.type == proxy_settings::none) { #ifdef TORRENT_USE_OPENSSL if (ssl_context) diff --git a/src/metadata_transfer.cpp b/src/metadata_transfer.cpp index 6429527c2..f13bb5606 100644 --- a/src/metadata_transfer.cpp +++ b/src/metadata_transfer.cpp @@ -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) { diff --git a/src/packet_buffer.cpp b/src/packet_buffer.cpp new file mode 100644 index 000000000..b48a04e41 --- /dev/null +++ b/src/packet_buffer.cpp @@ -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 // 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; + } + +} + diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 34a92268a..fbfd39970 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -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 @@ -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()) (*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()) (*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() + && 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 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() || !m_ses.m_settings.enable_outgoing_tcp) + && m_peer_info + && m_peer_info->supports_holepunch + && !m_holepunch_mode) + { + boost::shared_ptr 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 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()) - { - 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(); + // 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(); + // 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() && 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 diff --git a/src/policy.cpp b/src/policy.cpp index e9972f6ed..ebe188b77 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -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); } diff --git a/src/session.cpp b/src/session.cpp index 71ab3dfa3..2afc612b7 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -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; } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 9d5fb7309..50fdc6a25 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -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()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << " rejected uTP connection\n"; +#endif + if (m_alerts.should_post()) + m_alerts.post_alert(peer_blocked_alert(torrent_handle(), endp.address())); + return; + } + + if (!m_settings.enable_incoming_tcp + && s->get()) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << " rejected TCP connection\n"; +#endif + if (m_alerts.should_post()) + 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()) ++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) diff --git a/src/socket_type.cpp b/src/socket_type.cpp index 86be2fce5..e93b5e29c 100644 --- a/src/socket_type.cpp +++ b/src/socket_type.cpp @@ -53,6 +53,9 @@ namespace libtorrent case socket_type_int_impl::value: get()->~http_stream(); break; + case socket_type_int_impl::value: + get()->~utp_stream(); + break; #if TORRENT_USE_I2P case socket_type_int_impl::value: get()->~i2p_stream(); @@ -69,6 +72,7 @@ namespace libtorrent get >()->~ssl_stream(); break; #endif + default: TORRENT_ASSERT(false); } m_type = 0; } @@ -88,6 +92,9 @@ namespace libtorrent case socket_type_int_impl::value: new ((http_stream*)m_data) http_stream(m_io_service); break; + case socket_type_int_impl::value: + new ((utp_stream*)m_data) utp_stream(m_io_service); + break; #if TORRENT_USE_I2P case socket_type_int_impl::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)) } diff --git a/src/storage.cpp b/src/storage.cpp index 58307e63f..140269dbe 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -347,10 +347,10 @@ namespace libtorrent storage(file_storage const& fs, file_storage const* mapped, std::string const& path , file_pool& fp, std::vector 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)); diff --git a/src/timestamp_history.cpp b/src/timestamp_history.cpp new file mode 100644 index 000000000..c679e918c --- /dev/null +++ b/src/timestamp_history.cpp @@ -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; + } +} + +} diff --git a/src/torrent.cpp b/src/torrent.cpp index 65c5c9c6c..0d883770b 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -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); } diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index ed41f3aae..b58380c40 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -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 diff --git a/src/ut_metadata.cpp b/src/ut_metadata.cpp index dc2a3057f..93ca997f0 100644 --- a/src/ut_metadata.cpp +++ b/src/ut_metadata.cpp @@ -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) { diff --git a/src/ut_pex.cpp b/src/ut_pex.cpp index e1cfb4257..2dff2e94f 100644 --- a/src/ut_pex.cpp +++ b/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() ? 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(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(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(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 > peers4_t; + peers4_t m_peers; +#if TORRENT_USE_IPV6 + typedef std::vector > 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(new ut_pex_peer_plugin(m_torrent , *pc, *this)); } -}} +} } namespace libtorrent { - boost::shared_ptr 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(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 + } } diff --git a/src/utp_socket_manager.cpp b/src/utp_socket_manager.cpp new file mode 100644 index 000000000..2f512f66b --- /dev/null +++ b/src/utp_socket_manager.cpp @@ -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::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 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 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(); + 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; + } +} + diff --git a/src/utp_stream.cpp b/src/utp_stream.cpp new file mode 100644 index 000000000..9fbd63bc7 --- /dev/null +++ b/src/utp_stream.cpp @@ -0,0 +1,2900 @@ +/* + +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/sliding_average.hpp" +#include "libtorrent/utp_socket_manager.hpp" +#include "libtorrent/alloca.hpp" +#include "libtorrent/timestamp_history.hpp" +#include "libtorrent/error.hpp" +#include + +#define TORRENT_UTP_LOG 0 +#define TORRENT_VERBOSE_UTP_LOG 0 +#define TORRENT_UT_SEQ 1 + +#if TORRENT_UTP_LOG +#include +#include "libtorrent/socket_io.hpp" +#endif + +namespace libtorrent { + +#if TORRENT_UTP_LOG + +char const* packet_type_names[] = { "ST_DATA", "ST_FIN", "ST_STATE", "ST_RESET", "ST_SYN" }; +char const* socket_state_names[] = { "NONE", "SYN_SENT", "CONNECTED", "FIN_SENT", "ERROR", "DELETE" }; + +static struct utp_logger +{ + FILE* utp_log_file; + mutex utp_log_mutex; + + utp_logger() : utp_log_file(0) + { + utp_log_file = fopen("utp.log", "w+"); + } + ~utp_logger() + { + if (utp_log_file) fclose(utp_log_file); + } +} log_file_holder; + +void utp_log(char const* fmt, ...) +{ + mutex::scoped_lock lock(log_file_holder.utp_log_mutex); + static ptime start = time_now_hires(); + fprintf(log_file_holder.utp_log_file, "[%012"PRId64"] ", total_microseconds(time_now_hires() - start)); + va_list l; + va_start(l, fmt); + vfprintf(log_file_holder.utp_log_file, fmt, l); + va_end(l); +} + +#define UTP_LOG utp_log +#if TORRENT_VERBOSE_UTP_LOG +#define UTP_LOGV utp_log +#else +#define UTP_LOGV if (false) printf +#endif + +#else + +#define UTP_LOG if (false) printf +#define UTP_LOGV if (false) printf + +#endif + +enum +{ + ACK_MASK = 0xffff, + + // the number of packets that'll fit in the reorder buffer + max_packets_reorder = 512, + + // if a packet receives more than this number of + // duplicate acks, we'll trigger a fast re-send + dup_ack_limit = 3, + + // the max number of packets to fast-resend per + // selective ack message + sack_resend_limit = 3, +}; + +// compare if lhs is less than rhs, taking wrapping +// into account. if lhs is close to UINT_MAX and rhs +// is close to 0, lhs is assumed to have wrapped and +// considered smaller +TORRENT_EXPORT bool compare_less_wrap(boost::uint32_t lhs, boost::uint32_t rhs, boost::uint32_t mask) +{ + // distance walking from lhs to rhs, downwards + boost::uint32_t dist_down = (lhs - rhs) & mask; + // distance walking from lhs to rhs, upwards + boost::uint32_t dist_up = (rhs - lhs) & mask; + + // if the distance walking up is shorter, lhs + // is less than rhs. If the distance walking down + // is shorter, then rhs is less than lhs + return dist_up < dist_down; +} + +// used for out-of-order incoming packets +// as well as sent packets that are waiting to be ACKed +struct packet +{ + // the last time this packet was sent + ptime send_time; + + // the size of the buffer 'buf' pointst to + boost::uint16_t size; + + // this is the offset to the payload inside the buffer + // this is also used as a cursor to describe where the + // next payload that hasn't been consumed yet starts + boost::uint16_t header_size; + + // the number of times this packet has been sent + boost::uint8_t num_transmissions:7; + + // true if we need to send this packet again. All + // outstanding packets are marked as needing to be + // resent on timeouts + bool need_resend:1; + + // this is set to true for packets that were + // sent with the DF bit set (Don't Fragment) + bool mtu_probe:1; + + // the actual packet buffer + char buf[]; +}; + +// since the uTP socket state may be needed after the +// utp_stream is closed, it's kept in a separate struct +// whose lifetime is not tied to the lifetime of utp_stream + +// the utp socket is closely modelled after the asio async +// operations and handler model. For writing to the socket, +// the client provides a list of buffers (for gather/writev +// style of I/O) and whenever the socket can write another +// packet to the stream, it picks up data from these buffers. +// When all of the data has been written, or enough time has +// passed since we first started writing, the write handler +// is called and the write buffer is reset. This means that +// we're not writing anything at all while waiting for the +// client to re-issue a write request. + +// reading is a little bit more complicated, since we must +// be able to receive data even when the user doesn't have +// an outstanding read operation on the socket. When the user +// does however, we want to receive data directly into the +// user's buffer instead of first copying it into our receive +// buffer. This is why the receive case is more complicated. +// There are two receive buffers. One provided by the user, +// which when present is always used. The other one is used +// when the user doesn't have an outstanding read request, +// and hence hasn't provided any buffer space to receive into. + +// the user provided read buffer is called "m_read_buffer" and +// its size is "m_read_buffer_size". The buffer we spill over +// into when the user provided buffer is full or when there +// is none, is "m_receive_buffer" and "m_receive_buffer_size" +// respectively. + +// in order to know when to trigger the read and write handlers +// there are two counters, m_read and m_written, which count +// the number of bytes we've stuffed into the user provided +// read buffer or written to the stream from the write buffer. +// These are used to trigger the handlers if we're written a +// large number of bytes. It's also triggered if we're filled +// the whole read buffer, or written the entire write buffer. +// The last way the handlers can be triggered is if we're read +// or written some, and enough time has elapsed since then. + +// when we receive data into m_receive_buffer (i.e. the buffer +// used when there's no user provided one) is stored as a +// number of heap allocated packets. This is just because it's +// simple to reuse the data structured and it provides all the +// functionality needed for this buffer. + +struct utp_socket_impl +{ + utp_socket_impl(boost::uint16_t recv_id, boost::uint16_t send_id + , void* userdata, utp_socket_manager* sm) + : m_sm(sm) + , m_userdata(userdata) + , m_read_handler(0) + , m_write_handler(0) + , m_connect_handler(0) + , m_remote_address() + , m_read_timeout() + , m_write_timeout() + , m_timeout(time_now_hires() + milliseconds(m_sm->connect_timeout())) + , m_last_cwnd_hit(min_time()) + , m_ack_timer(time_now() + minutes(10)) + , m_last_history_step(time_now_hires()) + , m_cwnd(TORRENT_ETHERNET_MTU << 16) + , m_buffered_incoming_bytes(0) + , m_reply_micro(0) + , m_adv_wnd(TORRENT_ETHERNET_MTU) + , m_bytes_in_flight(0) + , m_read(0) + , m_write_buffer_size(0) + , m_written(0) + , m_receive_buffer_size(0) + , m_read_buffer_size(0) + , m_in_buf_size(100 * 1024 * 1024) + , m_in_packets(0) + , m_out_packets(0) + , m_port(0) + , m_send_id(send_id) + , m_recv_id(recv_id) + , m_ack_nr(0) + , m_seq_nr(0) + , m_acked_seq_nr(0) + , m_fast_resend_seq_nr(0) + , m_eof_seq_nr(0) + , m_mtu(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER - 8 - 24 - 36) + , m_mtu_floor(TORRENT_INET_MIN_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) + , m_mtu_ceiling(TORRENT_ETHERNET_MTU - TORRENT_IPV4_HEADER - TORRENT_UDP_HEADER) + , m_mtu_seq(0) + , m_duplicate_acks(0) + , m_num_timeouts(0) + , m_delay_sample_idx(0) + , m_state(UTP_STATE_NONE) + , m_eof(false) + , m_attached(true) + , m_nagle(true) + { + for (int i = 0; i != num_delay_hist; ++i) + m_delay_sample_hist[i] = UINT_MAX; + } + + ~utp_socket_impl(); + + void init(udp::endpoint const& ep, boost::uint16_t id, void* userdata + , utp_socket_manager* sm) + { + m_remote_address = ep.address(); + m_port = ep.port(); + m_send_id = id + 1; + m_recv_id = id; + m_userdata = userdata; + m_sm = sm; + } + + void tick(ptime const& now); + void init_mtu(int link_mtu, int utp_mtu); + bool incoming_packet(char const* buf, int size + , udp::endpoint const& ep, ptime receive_time); + bool should_delete() const; + tcp::endpoint remote_endpoint(error_code& ec) const + { + if (m_state == UTP_STATE_NONE) + ec = asio::error::not_connected; + else + TORRENT_ASSERT(m_remote_address != address_v4::any()); + return tcp::endpoint(m_remote_address, m_port); + } + std::size_t available() const; + void destroy(); + void detach(); + void send_syn(); + void send_fin(); + + bool send_pkt(bool ack); + bool resend_packet(packet* p, bool fast_resend = false); + void send_reset(utp_header* ph); + void parse_sack(boost::uint16_t packet_ack, char const* ptr, int size, int* acked_bytes + , ptime const now, boost::uint32_t& min_rtt); + void write_payload(char* ptr, int size); + void ack_packet(packet* p, ptime const& receive_time + , boost::uint32_t& min_rtt, boost::uint16_t seq_nr); + void write_sack(char* buf, int size) const; + void incoming(char const* buf, int size, packet* p, ptime now); + void do_ledbat(int acked_bytes, int delay, int in_flight, ptime const now); + int packet_timeout() const; + bool test_socket_state(); + void maybe_trigger_receive_callback(ptime now); + void maybe_trigger_send_callback(ptime now); + bool cancel_handlers(error_code const& ec, bool kill); + bool consume_incoming_data( + utp_header const* ph, char const* ptr, int payload_size, ptime now); + void update_mtu_limits(); + void experienced_loss(int seq_nr); + + void check_receive_buffers() const; + + utp_socket_manager* m_sm; + + // userdata pointer passed along + // with any callback. This is initialized to 0 + // then set to point to the utp_stream when + // hooked up, and then reset to 0 once the utp_stream + // detaches. This is used to know whether or not + // the socket impl is still attached to a utp_stream + // object. When it isn't, we'll never be able to + // signal anything back to the client, and in case + // of errors, we just have to delete ourselves + // i.e. transition to the UTP_STATE_DELETED state + void* m_userdata; + + // This is a platform-independent replacement + // for the regular iovec type in posix. Since + // it's not used in any system call, we might as + // well define our own type instead of wrapping + // the system's type. + struct iovec_t + { + iovec_t(void* b, size_t l): buf(b), len(l) {} + void* buf; + size_t len; + }; + + // if there's currently an async read or write + // operation in progress, these buffers are initialized + // and used, otherwise any bytes received are stuck in + // m_receive_buffer until another read is made + // as we flush from the write buffer, individual iovecs + // are updated to only refer to unflushed portions of the + // buffers. Buffers that empty are erased from the vector. + std::vector m_write_buffer; + + // the user provided read buffer. If this has a size greater + // than 0, we'll always prefer using it over putting received + // data in the m_receive_buffer. As data is stored in the + // read buffer, the iovec_t elements are adjusted to only + // refer to the unwritten portions of the buffers, and the + // ones that fill up are erased from the vector + std::vector m_read_buffer; + + // packets we've received without a read operation + // active. Store them here until the client triggers + // an async_read_some + std::vector m_receive_buffer; + + // this is the error on this socket. If m_state is + // set to UTP_STATE_ERROR_WAIT, this error should be + // forwarded to the client as soon as we have a new + // async operation initiated + error_code m_error; + + // these are the callbacks made into the utp_stream object + // on read/write/connect events + utp_stream::handler_t m_read_handler; + utp_stream::handler_t m_write_handler; + utp_stream::connect_handler_t m_connect_handler; + + // the address of the remote endpoint + address m_remote_address; + + // the send and receive buffers + // maps packet sequence numbers + packet_buffer m_inbuf; + packet_buffer m_outbuf; + + // timers when we should trigger the read and + // write callbacks (unless the buffers fill up + // before) + ptime m_read_timeout; + ptime m_write_timeout; + + // the time when the last packet we sent times out. Including re-sends. + // if we ever end up not having sent anything in one second ( + // or one mean rtt + 2 average deviations, whichever is greater) + // we set our cwnd to 1 MSS. This condition can happen either because + // a packet has timed out and needs to be resent or because our + // cwnd is set to less than one MSS during congestion control. + // it can also happen if the other end sends an advertized window + // size less than one MSS. + ptime m_timeout; + + // the last time we wanted to send more data, but couldn't because + // it would bring the number of outstanding bytes above the cwnd. + // this is used to restrict increasing the cwnd size when we're + // not sending fast enough to need it bigger + ptime m_last_cwnd_hit; + + // the next time we need to send an ACK the latest + // updated every time we send an ACK and every time we + // put off sending an ACK for a received packet + ptime m_ack_timer; + + // the last time we stepped the timestamp history + ptime m_last_history_step; + + // the max number of bytes in-flight. This is a fixed point + // value, to get the true number of bytes, shift right 16 bits + // the value is always >= 0, but the calculations performed on + // it in do_ledbat() is signed. + boost::int64_t m_cwnd; + + timestamp_history m_delay_hist; + timestamp_history m_their_delay_hist; + + // the number of bytes we have buffered in m_inbuf + boost::int32_t m_buffered_incoming_bytes; + + // the timestamp diff in the last packet received + // this is what we'll send back + boost::uint32_t m_reply_micro; + + // this is the advertized receive window the other end sent + // we'll never have more un-acked bytes in flight + // if this ever gets set to zero, we'll try one packet every + // second until the window opens up again + boost::uint32_t m_adv_wnd; + + // the number of un-acked bytes we have sent + boost::int32_t m_bytes_in_flight; + + // the number of bytes read into the user provided + // buffer. If this grows too big, we'll trigger the + // read handler. + boost::int32_t m_read; + + // the sum of the lengths of all iovec in m_write_buffer + boost::int32_t m_write_buffer_size; + + // the number of bytes already written to packets + // from m_write_buffer + boost::int32_t m_written; + + // the sum of all packets stored in m_receive_buffer + boost::int32_t m_receive_buffer_size; + + // the sum of all buffers in m_read_buffer + boost::int32_t m_read_buffer_size; + + // max number of bytes to allocate for receive buffer + boost::int32_t m_in_buf_size; + + // this holds the 3 last delay measurements, + // these are the actual corrected delay measurements. + // the lowest of the 3 last ones is used in the congestion + // controller. This is to not completely close the cwnd + // by a single outlier. + enum { num_delay_hist = 3 }; + boost::uint32_t m_delay_sample_hist[num_delay_hist]; + + // counters + boost::uint32_t m_in_packets; + boost::uint32_t m_out_packets; + + // average RTT + sliding_average<16> m_rtt; + + // port of destination endpoint + boost::uint16_t m_port; + + boost::uint16_t m_send_id; + boost::uint16_t m_recv_id; + + // this is the ack we're sending back. We have + // received all packets up to this sequence number + boost::uint16_t m_ack_nr; + + // the sequence number of the next packet + // we'll send + boost::uint16_t m_seq_nr; + + // this is the sequence number of the packet that + // everything has been ACKed up to. Everything we've + // sent up to this point has been received by the other + // end. + boost::uint16_t m_acked_seq_nr; + + // each packet gets one chance of "fast resend". i.e. + // if we have multiple duplicate acks, we may send a + // packet immediately, if m_fast_resend_seq_nr is set + // to that packet's sequence number + boost::uint16_t m_fast_resend_seq_nr; + + // this is the sequence number of the FIN packet + // we've received. This sequence number is only + // valid if m_eof is true. We should not accept + // any packets beyond this sequence number from the + // other end + boost::uint16_t m_eof_seq_nr; + + // this is the lowest sequence number that, when lost, + // will cause the window size to be cut in half + boost::uint16_t m_loss_seq_nr; + + // the max number of bytes we can send in a packet + // including the header + boost::uint16_t m_mtu; + + // the floor is the largest packet that we have + // been able to get through without fragmentation + boost::uint16_t m_mtu_floor; + + // the ceiling is the largest packet that we might + // be able to get through without fragmentation. + // i.e. ceiling +1 is very likely to not get through + // or we have in fact experienced a drop or ICMP + // message indicating that it is + boost::uint16_t m_mtu_ceiling; + + // the sequence number of the probe in-flight + // this is 0 if there is no probe in flight + boost::uint16_t m_mtu_seq; + + // this is a counter of how many times the current m_acked_seq_nr + // has been ACKed. If it's ACKed more than 3 times, we assume the + // packet with the next sequence number has been lost, and we trigger + // a re-send. Ovbiously an ACK only counts as a duplicate as long as + // we have outstanding packets following it. + boost::uint8_t m_duplicate_acks; + + // the number of packet timeouts we've seen in a row + // this affects the packet timeout time + boost::uint8_t m_num_timeouts; + + enum state_t { + // not yet connected + UTP_STATE_NONE, + // sent a syn packet, not received any acks + UTP_STATE_SYN_SENT, + // syn-ack received and in normal operation + // of sending and receiving data + UTP_STATE_CONNECTED, + // fin sent, but all packets up to the fin packet + // have not yet been acked. We might still be waiting + // for a FIN from the other end + UTP_STATE_FIN_SENT, + + // ====== states beyond this point ===== + // === are considered closing states === + // === and will cause the socket to ==== + // ============ be deleted ============= + + // the socket has been gracefully disconnected + // and is waiting for the client to make a + // socket call so that we can communicate this + // fact and actually delete all the state, or + // there is an error on this socket and we're + // waiting to communicate this to the client in + // a callback. The error in either case is stored + // in m_error. If the socket has gracefully shut + // down, the error is error::eof. + UTP_STATE_ERROR_WAIT, + + // there are no more references to this socket + // and we can delete it + UTP_STATE_DELETE + }; + + // this is the cursor into m_delay_sample_hist + boost::uint8_t m_delay_sample_idx:2; + + // the state the socket is in + boost::uint8_t m_state:3; + + // this is set to true when we receive a fin + bool m_eof:1; + + // is this socket state attached to a user space socket? + bool m_attached:1; + + // this is true if nagle is enabled (which it is by default) + // TODO: support the option to turn it off + bool m_nagle:1; +}; + +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING +int socket_impl_size() { return sizeof(utp_socket_impl); } +#endif + +utp_socket_impl* construct_utp_impl(boost::uint16_t recv_id + , boost::uint16_t send_id, void* userdata + , utp_socket_manager* sm) +{ + return new utp_socket_impl(recv_id, send_id, userdata, sm); +} + +void detach_utp_impl(utp_socket_impl* s) +{ + s->detach(); +} + +void delete_utp_impl(utp_socket_impl* s) +{ + delete s; +} + +bool should_delete(utp_socket_impl* s) +{ + return s->should_delete(); +} + +void tick_utp_impl(utp_socket_impl* s, ptime const& now) +{ + s->tick(now); +} + +void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu) +{ + s->init_mtu(link_mtu, utp_mtu); +} + +bool utp_incoming_packet(utp_socket_impl* s, char const* p + , int size, udp::endpoint const& ep, ptime receive_time) +{ + return s->incoming_packet(p, size, ep, receive_time); +} + +bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, boost::uint16_t id) +{ + return s->m_remote_address == ep.address() + && s->m_port == ep.port() + && s->m_recv_id == id; +} + +udp::endpoint utp_remote_endpoint(utp_socket_impl* s) +{ + return udp::endpoint(s->m_remote_address, s->m_port); +} + +boost::uint16_t utp_receive_id(utp_socket_impl* s) +{ + return s->m_recv_id; +} + +void utp_socket_impl::update_mtu_limits() +{ + TORRENT_ASSERT(m_mtu_floor <= m_mtu_ceiling); + m_mtu = (m_mtu_floor + m_mtu_ceiling) / 2; + + // clear the mtu probe sequence number since + // it was either dropped or acked + m_mtu_seq = 0; + + if (m_mtu_ceiling - m_mtu_floor < 10) + { + // we have narrowed down the mtu within 10 + // bytes. That's good enough, start using + // floor as the packet size from now on. + // set the ceiling to the floor as well to + // disable more probes to be sent + // we'll never re-probe this connection + m_mtu = m_mtu_ceiling = m_mtu_floor; + } +} + +int utp_socket_state(utp_socket_impl const* s) +{ + return s->m_state; +} + +utp_stream::utp_stream(asio::io_service& io_service) + : m_io_service(io_service) + , m_impl(0) + , m_open(false) +{ +} + +utp_socket_impl* utp_stream::get_impl() +{ + return m_impl; +} + +void utp_stream::close() +{ + if (!m_impl) return; + m_impl->destroy(); +} + +std::size_t utp_stream::available() const +{ + return m_impl->available(); +} + +utp_stream::endpoint_type utp_stream::remote_endpoint(error_code& ec) const +{ + if (!m_impl) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + return m_impl->remote_endpoint(ec); +} + +utp_stream::endpoint_type utp_stream::local_endpoint(error_code& ec) const +{ + if (m_impl == 0 || m_impl->m_sm == 0) + { + ec = asio::error::not_connected; + return endpoint_type(); + } + return m_impl->m_sm->local_endpoint(ec); +} + +utp_stream::~utp_stream() +{ + if (m_impl) + { + UTP_LOGV("%8p: utp_stream destructed\n", m_impl); + m_impl->destroy(); + detach_utp_impl(m_impl); + } + + m_impl = 0; +} + +void utp_stream::set_impl(utp_socket_impl* impl) +{ + TORRENT_ASSERT(m_impl == 0); + TORRENT_ASSERT(!m_open); + m_impl = impl; + m_open = true; +} + +int utp_stream::read_buffer_size() const +{ + TORRENT_ASSERT(m_impl); + return m_impl->m_receive_buffer_size; +} + +void utp_stream::on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + + UTP_LOGV("%8p: calling read handler read:%d ec:%s kill:%d\n", s->m_impl + , int(bytes_transferred), ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_read_handler); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + s->m_io_service.post(boost::bind(s->m_read_handler, ec, bytes_transferred)); + s->m_read_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + + UTP_LOGV("%8p: calling write handler written:%d ec:%s kill:%d\n", s->m_impl + , int(bytes_transferred), ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_write_handler); + TORRENT_ASSERT(bytes_transferred > 0 || ec); + s->m_io_service.post(boost::bind(s->m_write_handler, ec, bytes_transferred)); + s->m_write_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::on_connect(void* self, error_code const& ec, bool kill) +{ + utp_stream* s = (utp_stream*)self; + + UTP_LOGV("%8p: calling connect handler ec:%s kill:%d\n" + , s->m_impl, ec.message().c_str(), kill); + + TORRENT_ASSERT(s->m_connect_handler); + s->m_io_service.post(boost::bind(s->m_connect_handler, ec)); + s->m_connect_handler.clear(); + if (kill && s->m_impl) + { + detach_utp_impl(s->m_impl); + s->m_impl = 0; + } +} + +void utp_stream::add_read_buffer(void* buf, size_t len) +{ + TORRENT_ASSERT(m_impl); + TORRENT_ASSERT(len < INT_MAX); + TORRENT_ASSERT(len > 0); + TORRENT_ASSERT(buf); + m_impl->m_read_buffer.push_back(utp_socket_impl::iovec_t(buf, len)); + m_impl->m_read_buffer_size += len; + + UTP_LOGV("%8p: add_read_buffer %d bytes\n", m_impl, int(len)); +} + +// this is the wrapper to add a user provided write buffer to the +// utp_socket_impl. It makes sure the m_write_buffer_size is kept +// up to date +void utp_stream::add_write_buffer(void const* buf, size_t len) +{ + TORRENT_ASSERT(m_impl); + TORRENT_ASSERT(len < INT_MAX); + TORRENT_ASSERT(len > 0); + TORRENT_ASSERT(buf); + +#ifdef TORRENT_DEBUG + int write_buffer_size = 0; + for (std::vector::iterator i = m_impl->m_write_buffer.begin() + , end(m_impl->m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); +#endif + + m_impl->m_write_buffer.push_back(utp_socket_impl::iovec_t((void*)buf, len)); + m_impl->m_write_buffer_size += len; + +#ifdef TORRENT_DEBUG + write_buffer_size = 0; + for (std::vector::iterator i = m_impl->m_write_buffer.begin() + , end(m_impl->m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_impl->m_write_buffer_size == write_buffer_size); +#endif + + UTP_LOGV("%8p: add_write_buffer %d bytes\n", m_impl, int(len)); +} + +// this is called when all user provided read buffers have been added +// and it's time to execute the async operation. The first thing we +// do is to copy any data stored in m_receive_buffer into the user +// provided buffer. This might be enough to in turn trigger the read +// handler immediately. +void utp_stream::set_read_handler(handler_t h) +{ + m_impl->m_read_handler = h; + if (m_impl->test_socket_state()) return; + + UTP_LOGV("%8p: new read handler. %d bytes in buffer\n" + , m_impl, m_impl->m_receive_buffer_size); + + TORRENT_ASSERT(m_impl->m_read_buffer_size > 0); + + // so, the client wants to read. If we already + // have some data in the read buffer, move it into the + // client's buffer right away + + m_impl->m_read += read_some(false); + m_impl->maybe_trigger_receive_callback(time_now_hires()); +} + +size_t utp_stream::read_some(bool clear_buffers) +{ + if (m_impl->m_receive_buffer_size == 0) + { + if (clear_buffers) + { + m_impl->m_read_buffer_size = 0; + m_impl->m_read_buffer.clear(); + } + return 0; + } + + std::vector::iterator target = m_impl->m_read_buffer.begin(); + + size_t ret = 0; + + int pop_packets = 0; + for (std::vector::iterator i = m_impl->m_receive_buffer.begin() + , end(m_impl->m_receive_buffer.end()); i != end;) + { + if (target == m_impl->m_read_buffer.end()) + { + UTP_LOGV(" No more target buffers: %d bytes left in buffer\n" + , m_impl->m_receive_buffer_size); + TORRENT_ASSERT(m_impl->m_read_buffer.empty()); + break; + } + + m_impl->check_receive_buffers(); + + packet* p = *i; + int to_copy = (std::min)(p->size - p->header_size, int(target->len)); + TORRENT_ASSERT(to_copy >= 0); + memcpy(target->buf, p->buf + p->header_size, to_copy); + ret += to_copy; + target->buf = ((char*)target->buf) + to_copy; + TORRENT_ASSERT(target->len >= to_copy); + target->len -= to_copy; + m_impl->m_receive_buffer_size -= to_copy; + TORRENT_ASSERT(m_impl->m_read_buffer_size >= to_copy); + m_impl->m_read_buffer_size -= to_copy; + p->header_size += to_copy; + if (target->len == 0) target = m_impl->m_read_buffer.erase(target); + + m_impl->check_receive_buffers(); + + TORRENT_ASSERT(m_impl->m_receive_buffer_size >= 0); + + // Consumed entire packet + if (p->header_size == p->size) + { + free(p); + ++pop_packets; + *i = 0; + ++i; + } + + if (m_impl->m_receive_buffer_size == 0) + { + UTP_LOGV(" Didn't fill entire target: %d bytes left in buffer\n" + , m_impl->m_receive_buffer_size); + break; + } + } + // remove the packets from the receive_buffer that we already copied over + // and freed + m_impl->m_receive_buffer.erase(m_impl->m_receive_buffer.begin() + , m_impl->m_receive_buffer.begin() + pop_packets); + // we exited either because we ran out of bytes to copy + // or because we ran out of space to copy the bytes to + TORRENT_ASSERT(m_impl->m_receive_buffer_size == 0 + || m_impl->m_read_buffer.empty()); + + UTP_LOGV("%8p: %d packets moved from buffer to user space\n" + , m_impl, pop_packets); + + if (clear_buffers) + { + m_impl->m_read_buffer_size = 0; + m_impl->m_read_buffer.clear(); + } + TORRENT_ASSERT(ret > 0); + return ret; +} + +// this is called when all user provided write buffers have been +// added. Start trying to send packets with the payload immediately. +void utp_stream::set_write_handler(handler_t h) +{ + UTP_LOGV("%8p: new write handler. %d bytes to write\n" + , m_impl, m_impl->m_write_buffer_size); + + TORRENT_ASSERT(m_impl->m_write_buffer_size > 0); + + m_impl->m_write_handler = h; + m_impl->m_written = 0; + if (m_impl->test_socket_state()) return; + + // try to write. send_pkt returns false if there's + // no more payload to send or if the congestion window + // is full and we can't send more packets right now + while (m_impl->send_pkt(false)); + + // if there was an error in send_pkt(), m_impl may be + // 0 at this point + if (m_impl) m_impl->maybe_trigger_send_callback(time_now_hires()); +} + +void utp_stream::do_connect(tcp::endpoint const& ep, utp_stream::connect_handler_t handler) +{ + int link_mtu, utp_mtu; + m_impl->m_sm->mtu_for_dest(ep.address(), link_mtu, utp_mtu); + m_impl->init_mtu(link_mtu, utp_mtu); + TORRENT_ASSERT(m_impl->m_connect_handler == 0); + m_impl->m_remote_address = ep.address(); + m_impl->m_port = ep.port(); + m_impl->m_connect_handler = handler; + + if (m_impl->test_socket_state()) return; + m_impl->send_syn(); +} + +// =========== utp_socket_impl ============ + +utp_socket_impl::~utp_socket_impl() +{ + TORRENT_ASSERT(!m_attached); + + UTP_LOGV("%8p: destroying utp socket state\n", this); + + // free any buffers we're holding + for (boost::uint16_t i = m_inbuf.cursor(), end((m_inbuf.cursor() + + m_inbuf.capacity()) & ACK_MASK); + i != end; i = (i + 1) & ACK_MASK) + { + void* p = m_inbuf.remove(i); + free(p); + } + for (boost::uint16_t i = m_outbuf.cursor(), end((m_outbuf.cursor() + + m_outbuf.capacity()) & ACK_MASK); + i != end; i = (i + 1) & ACK_MASK) + { + void* p = m_outbuf.remove(i); + free(p); + } + + for (std::vector::iterator i = m_receive_buffer.begin() + , end = m_receive_buffer.end(); i != end; ++i) + { + free(*i); + } +} + +bool utp_socket_impl::should_delete() const +{ + // if the socket state is not attached anymore we're free + // to delete it from the client's point of view. The other + // endpoint however might still need to be told that we're + // closing the socket. Only delete the state if we're not + // attached and we're in a state where the other end doesn't + // expect the socket to still be alive + bool ret = (m_state >= UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_NONE) + && !m_attached; + + if (ret) + { + UTP_LOGV("%8p: should_delete() = true\n", this); + } + + return ret; +} + +void utp_socket_impl::maybe_trigger_receive_callback(ptime now) +{ + // nothing has been read or there's no outstanding read operation + if (m_read == 0 || m_read_handler == 0) return; + + if (m_read > m_read_buffer_size / 2 || now >= m_read_timeout) + { + UTP_LOGV("%8p: calling read handler read:%d\n", this, m_read); + m_read_handler(m_userdata, m_read, m_error, false); + m_read_handler = 0; + m_read = 0; + m_read_buffer_size = 0; + m_read_buffer.clear(); + } +} + +void utp_socket_impl::maybe_trigger_send_callback(ptime now) +{ + // nothing has been written or there's no outstanding write operation + if (m_written == 0 || m_write_handler == 0) return; + + if (m_written > m_write_buffer_size / 2 || now >= m_write_timeout) + { + UTP_LOGV("%8p: calling write handler written:%d\n", this, m_written); + + m_write_handler(m_userdata, m_written, m_error, false); + m_write_handler = 0; + m_written = 0; + m_write_buffer_size = 0; + m_write_buffer.clear(); + } +} + +void utp_socket_impl::destroy() +{ +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: destroy state:%s\n", this, socket_state_names[m_state]); +#endif + + if (m_userdata == 0) return; + + if (m_state == UTP_STATE_CONNECTED) + { + send_fin(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + + bool cancelled = cancel_handlers(asio::error::operation_aborted, true); + + m_userdata = 0; + m_read_buffer.clear(); + m_read_buffer_size = 0; + + m_write_buffer.clear(); + m_write_buffer_size = 0; + + if ((m_state == UTP_STATE_ERROR_WAIT + || m_state == UTP_STATE_NONE + || m_state == UTP_STATE_SYN_SENT) && cancelled) + { + m_state = UTP_STATE_DELETE; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + return; + } + + // #error our end is closing. Wait for everything to be acked +} + +void utp_socket_impl::detach() +{ + UTP_LOGV("%8p: detach()\n", this); + m_attached = false; +} + +void utp_socket_impl::send_syn() +{ + m_seq_nr = rand(); + m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; + m_loss_seq_nr = m_acked_seq_nr; + m_ack_nr = 0; + m_fast_resend_seq_nr = m_seq_nr; + + packet* p = (packet*)malloc(sizeof(packet) + sizeof(utp_header)); + p->size = sizeof(utp_header); + p->header_size = sizeof(utp_header); + p->num_transmissions = 1; + p->need_resend = false; + utp_header* h = (utp_header*)p->buf; + h->type_ver = (ST_SYN << 4) | 1; + h->extension = 0; + // using recv_id here is intentional! This is an odd + // thing in uTP. The syn packet is sent with the connection + // ID that it expects to receive the syn ack on. All + // subsequent connection IDs will be this plus one. + h->connection_id = m_recv_id; + h->timestamp_difference_microseconds = m_reply_micro; + h->wnd_size = 0; + h->seq_nr = m_seq_nr; + h->ack_nr = 0; + + ptime now = time_now_hires(); + p->send_time = now; + h->timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time())); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: send_syn seq_nr:%d id:%d target:%s\n" + , this, int(m_seq_nr), int(m_recv_id) + , print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str()); +#endif + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port), (char const*)h + , sizeof(utp_header), ec); + + if (ec) + { + free(p); + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); + m_outbuf.insert(m_seq_nr, p); + + m_seq_nr = (m_seq_nr + 1) & ACK_MASK; + + TORRENT_ASSERT(!m_error); + m_state = UTP_STATE_SYN_SENT; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif +} + +void utp_socket_impl::send_fin() +{ + TORRENT_ASSERT(m_state != UTP_STATE_FIN_SENT); + + // we need a heap allocated packet in order to stick it + // in the send buffer, so that we can resend it + packet* p = (packet*)malloc(sizeof(packet) + sizeof(utp_header)); + + p->size = sizeof(utp_header); + p->header_size = sizeof(utp_header); + p->num_transmissions = 1; + p->need_resend = false; + utp_header* h = (utp_header*)p->buf; + + h->type_ver = (ST_FIN << 4) | 1; + h->extension = 0; + h->connection_id = m_send_id; + h->timestamp_difference_microseconds = m_reply_micro; + h->wnd_size = m_in_buf_size - m_buffered_incoming_bytes - m_receive_buffer_size; + h->seq_nr = m_seq_nr; + h->ack_nr = m_ack_nr; + + ptime now = time_now_hires(); + p->send_time = now; + h->timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time())); + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)h, sizeof(utp_header), ec); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: sending FIN seq_nr:%d ack_nr:%d type:%s " + "id:%d target:%s size:%d error:%s send_buffer_size:%d\n" + , this, int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , int(sizeof(utp_header)), ec.message().c_str(), m_write_buffer_size); +#endif + + if (ec) + { + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + free(p); + return; + } + +#if !TORRENT_UT_SEQ + // if the other end closed the connection immediately + // our FIN packet will end up having the same sequence + // number as the SYN, so this assert is invalid + TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); +#endif + + packet* old = (packet*)m_outbuf.insert(m_seq_nr, p); + if (old) + { + if (!old->need_resend) m_bytes_in_flight -= old->size - old->header_size; + free(old); + } + m_seq_nr = (m_seq_nr + 1) & ACK_MASK; + m_fast_resend_seq_nr = m_seq_nr; + + TORRENT_ASSERT(!m_error); + m_state = UTP_STATE_FIN_SENT; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif +} + +void utp_socket_impl::send_reset(utp_header* ph) +{ + utp_header h; + h.type_ver = (ST_RESET << 4) | 1; + h.extension = 0; + h.connection_id = m_send_id; + h.timestamp_difference_microseconds = m_reply_micro; + h.wnd_size = 0; + h.seq_nr = rand(); + h.ack_nr = ph->seq_nr; + ptime now = time_now_hires(); + h.timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time())); + + UTP_LOGV("%8p: send_reset seq_nr:%d id:%d ack_nr:%d\n" + , this, int(h.seq_nr), int(m_send_id), int(ph->seq_nr)); + + // ignore errors here + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port), (char const*)&h, sizeof(h), ec); +} + +std::size_t utp_socket_impl::available() const +{ + return m_receive_buffer_size; +} + +void utp_socket_impl::parse_sack(boost::uint16_t packet_ack, char const* ptr + , int size, int* acked_bytes, ptime const now, boost::uint32_t& min_rtt) +{ + if (size == 0) return; + + // this is the sequence number the current bit represents + int ack_nr = (packet_ack + 2) & ACK_MASK; + +#if TORRENT_UTP_LOG + std::string bitmask; + for (char const* b = ptr, *end = ptr + size; b != end; ++b) + { + unsigned char bitfield = unsigned(*b); + unsigned char mask = 1; + // for each bit + for (int i = 0; i < 8; ++i) + { + bitmask += (mask & bitfield) ? "1" : "0"; + mask <<= 1; + } + } + UTP_LOGV("%8p: got SACK first:%d %s our_seq_nr:%u\n" + , this, ack_nr, bitmask.c_str(), m_seq_nr); +#endif + + // the number of acked packets past the fast re-send sequence number + // this is used to determine if we should trigger more fast re-sends + int dups = 0; + + // the sequence number of the last ACKed packet + int last_ack = packet_ack; + + // for each byte + for (char const* end = ptr + size; ptr != end; ++ptr) + { + unsigned char bitfield = unsigned(*ptr); + unsigned char mask = 1; + // for each bit + for (int i = 0; i < 8; ++i) + { + if (mask & bitfield) + { + last_ack = ack_nr; + if (m_fast_resend_seq_nr == ack_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + if (compare_less_wrap(m_fast_resend_seq_nr, ack_nr, 0xffff)) ++dups; + // this bit was set, ack_nr was received + packet* p = (packet*)m_outbuf.remove(ack_nr); + if (p) + { + acked_bytes += p->size - p->header_size; + // each ACKed packet counts as a duplicate ack + UTP_LOGV("%8p: duplicate_acks:%u fast_resend_seq_nr:%u\n" + , this, m_duplicate_acks, m_fast_resend_seq_nr); + ack_packet(p, now, min_rtt, ack_nr); + } + else if ((m_acked_seq_nr + 1) == ack_nr) + { + // this packet must have been acked by a previous + // selective ack + m_acked_seq_nr = ack_nr; + } + } + + mask <<= 1; + ack_nr = (ack_nr + 1) & ACK_MASK; + + // we haven't sent packets past this point. + // if there are any more bits set, we have to + // ignore them anyway + if (ack_nr == m_seq_nr) break; + } + if (ack_nr == m_seq_nr) break; + } + + // we received more than dup_ack_limit ACKs in this SACK message. + // trigger fast re-send + if (dups >= dup_ack_limit && compare_less_wrap(m_fast_resend_seq_nr, last_ack, 0xffff)) + { + experienced_loss(m_fast_resend_seq_nr); + int num_resent = 0; + for (; m_fast_resend_seq_nr != last_ack; m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); + if (!p) continue; + ++num_resent; + if (!resend_packet(p, true)) break; + m_duplicate_acks = 0; + if (num_resent >= sack_resend_limit) break; + } + } +} + +// copies data from the write buffer into the packet +// pointed to by ptr +void utp_socket_impl::write_payload(char* ptr, int size) +{ +#ifdef TORRENT_DEBUG + int write_buffer_size = 0; + for (std::vector::iterator i = m_write_buffer.begin() + , end(m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); +#endif + TORRENT_ASSERT(!m_write_buffer.empty() || size == 0); + TORRENT_ASSERT(m_write_buffer_size >= size); + std::vector::iterator i = m_write_buffer.begin(); + + if (size == 0) return; + + ptime now = time_now_hires(); + + int buffers_to_clear = 0; + while (size > 0) + { + // i points to the iovec we'll start copying from + int to_copy = (std::min)(size, int(i->len)); + memcpy(ptr, static_cast(i->buf), to_copy); + size -= to_copy; + if (m_written == 0) + { + m_write_timeout = now + milliseconds(100); + UTP_LOGV("%8p: setting write timeout to 100 ms from now\n", this); + } + TORRENT_ASSERT(to_copy >= 0); + TORRENT_ASSERT(to_copy < INT_MAX / 2 && m_written < INT_MAX / 2); + m_written += to_copy; + ptr += to_copy; + i->len -= to_copy; + TORRENT_ASSERT(m_write_buffer_size >= to_copy); + m_write_buffer_size -= to_copy; + ((char const*&)i->buf) += to_copy; + if (i->len == 0) ++buffers_to_clear; + ++i; + } + + if (buffers_to_clear) + m_write_buffer.erase(m_write_buffer.begin() + , m_write_buffer.begin() + buffers_to_clear); + +#ifdef TORRENT_DEBUG + write_buffer_size = 0; + for (std::vector::iterator i = m_write_buffer.begin() + , end(m_write_buffer.end()); i != end; ++i) + { + write_buffer_size += i->len; + } + TORRENT_ASSERT(m_write_buffer_size == write_buffer_size); +#endif +} + +// sends a packet, pulls data from the write buffer (if there's any) +// if ack is true, we need to send a packet regardless of if there's +// any data. Returns true if we could send more data (i.e. call +// send_pkt() again) +bool utp_socket_impl::send_pkt(bool ack) +{ + // This assert is bad because we call this function to ack + // received FIN when we're in UTP_STATE_FIN_SENT. + // + // TORRENT_ASSERT(m_state != UTP_STATE_FIN_SENT); + + // first see if we need to resend any packets + + for (int i = (m_acked_seq_nr + 1) & ACK_MASK; i != m_seq_nr; i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (!p) continue; + if (!p->need_resend) continue; + if (!resend_packet(p)) + { + // we couldn't resend the packet. It probably doesn't + // fit in our cwnd. If ack is set, we need to continue + // to send our ack anyway, if we don't have to send an + // ack, we might as well return + if (!ack) return false; + // resend_packet might have failed + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return false; + break; + } + + // don't fast-resend this packet + if (m_fast_resend_seq_nr == i) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + } + + bool ret = false; + + int sack = 0; + if (m_inbuf.size()) + { + // the SACK bitfield should ideally fit all + // the pieces we have successfully received + sack = (m_inbuf.span() + 7) / 8; + if (sack > 32) sack = 32; + } + + int header_size = sizeof(utp_header) + (sack ? sack + 2 : 0); + int payload_size = m_write_buffer_size; + if (m_mtu - header_size < payload_size) + { + payload_size = m_mtu - header_size; + ret = true; // there's more data to send + } + + // if we have one MSS worth of data, make sure it fits in our + // congestion window and the advertized receive window from + // the other end. + if (m_bytes_in_flight + payload_size > (std::min)(int(m_cwnd >> 16), int(m_adv_wnd - m_bytes_in_flight))) + { + // this means there's not enough room in the send window for + // another packet. We have to hold off sending this data. + // we still need to send an ACK though + payload_size = 0; + + // we're restrained by the window size + m_last_cwnd_hit = time_now_hires(); + + // there's no more space in the cwnd, no need to + // try to send more right now + ret = false; + + UTP_LOGV("%8p: no space in window send_buffer_size:%d cwnd:%d " + "ret:%d adv_wnd:%d in-flight:%d mtu:%d\n" + , this, m_write_buffer_size, int(m_cwnd >> 16) + , ret, m_adv_wnd, m_bytes_in_flight, m_mtu); + } + + // if we don't have any data to send, or can't send any data + // and we don't have any data to ack, don't send a packet + if (payload_size == 0 && !ack) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: skipping send seq_nr:%d ack_nr:%d " + "id:%d target:%s header_size:%d error:%s send_buffer_size:%d cwnd:%d " + "ret:%d adv_wnd:%d in-flight:%d mtu:%d\n" + , this, int(m_seq_nr), int(m_ack_nr) + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , header_size, ec.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , int(ret), m_adv_wnd, m_bytes_in_flight, m_mtu); +#endif + return false; + } + + if (((m_seq_nr - m_acked_seq_nr) & ACK_MASK) > 1 + && payload_size < m_mtu - header_size + && !ack + && m_nagle) + { + // this is nagle. If we don't have a full packet + // worth of payload to send AND we have at least + // one outstanding packet, hold off. Once the + // outstanding packet is acked, we'll send this + // payload + UTP_LOGV("%8p: NAGLE not enough payload send_buffer_size:%d cwnd:%d " + "ret:%d adv_wnd:%d in-flight:%d mtu:%d\n" + , this, m_write_buffer_size, int(m_cwnd >> 16) + , ret, m_adv_wnd, m_bytes_in_flight, m_mtu); + return false; + } + + int packet_size = header_size + payload_size; + + // MTU DISCOVERY + bool use_as_probe = false; + if (m_mtu_seq == 0 + && packet_size > m_mtu_floor + && m_seq_nr != 0) + { + use_as_probe = true; + m_mtu_seq = m_seq_nr; + } + + packet* p; + // we only need a heap allocation if we have payload and + // need to keep the packet around (in the outbuf) + if (payload_size) p = (packet*)malloc(sizeof(packet) + packet_size); + else p = (packet*)TORRENT_ALLOCA(char, sizeof(packet) + packet_size); + + p->size = packet_size; + p->header_size = packet_size - payload_size; + p->num_transmissions = 1; + p->need_resend = false; + p->mtu_probe = use_as_probe; + char* ptr = p->buf; + utp_header* h = (utp_header*)ptr; + ptr += sizeof(utp_header); + + h->type_ver = ((payload_size ? ST_DATA : ST_STATE) << 4) | 1; + h->extension = sack ? 1 : 0; + h->connection_id = m_send_id; + h->timestamp_difference_microseconds = m_reply_micro; + h->wnd_size = m_in_buf_size - m_buffered_incoming_bytes - m_receive_buffer_size; + // seq_nr is ignored for ST_STATE packets, so it doesn't + // matter that we say this is a sequence number we haven't + // actually sent yet + h->seq_nr = m_seq_nr; + h->ack_nr = m_ack_nr; + + if (sack) + { + *ptr++ = 0; // end of extension chain + *ptr++ = sack; // bytes for SACK bitfield + write_sack(ptr, sack); + ptr += sack; + } + + write_payload(ptr, payload_size); + + // fill in the timestamp as late as possible + ptime now = time_now_hires(); + p->send_time = now; + h->timestamp_microseconds = boost::uint32_t(total_microseconds(now - min_time())); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: sending packet seq_nr:%d ack_nr:%d type:%s " + "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " + "ret:%d adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u " + "mtu_probe:%d\n" + , this, int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , packet_size, m_error.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , ret, m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) + , boost::uint32_t(h->timestamp_difference_microseconds), int(p->mtu_probe)); +#endif + + TORRENT_ASSERT(!m_error); + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)h, packet_size, ec + , use_as_probe ? utp_socket_manager::dont_fragment : 0); + + ++m_out_packets; + + if (ec == error::message_size && use_as_probe) + { + m_mtu_ceiling = m_mtu - 1; + update_mtu_limits(); + // TODO: we might want to do something else here + // as well, to resend the packet immediately without + // it being an MTU probe + } + else if (ec) + { + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + if (payload_size) free(p); + return false; + } + + // we just sent a packet. this means we just ACKed the last received + // packet as well. So, we can now reset the delayed ack timer to + // not trigger for a long time + m_ack_timer = now + minutes(10); + + // if we have payload, we need to save the packet until it's acked + // and progress m_seq_nr + if (payload_size) + { +#if !TORRENT_UT_SEQ + // if the other end closed the connection immediately + // our FIN packet will end up having the same sequence + // number as the SYN, so this assert is invalid + TORRENT_ASSERT(!m_outbuf.at(m_seq_nr)); +#endif + packet* old = (packet*)m_outbuf.insert(m_seq_nr, p); + if (old) + { + if (!old->need_resend) m_bytes_in_flight -= old->size - old->header_size; + free(old); + } + m_seq_nr = (m_seq_nr + 1) & ACK_MASK; + TORRENT_ASSERT(payload_size >= 0); + m_bytes_in_flight += payload_size; + } + + return ret; +} + +// size is in bytes +void utp_socket_impl::write_sack(char* buf, int size) const +{ + TORRENT_ASSERT(m_inbuf.size()); + int ack_nr = (m_ack_nr + 2) & ACK_MASK; + char* end = buf + size; + + for (; buf != end; ++buf) + { + *buf = 0; + int mask = 1; + for (int i = 0; i < 8; ++i) + { + if (m_inbuf.at(ack_nr)) *buf |= mask; + mask <<= 1; + ack_nr = (ack_nr + 1) & ACK_MASK; + } + } +} + +bool utp_socket_impl::resend_packet(packet* p, bool fast_resend) +{ + // for fast re-sends the packet hasn't been marked as needing resending + TORRENT_ASSERT(p->need_resend || fast_resend); + + TORRENT_ASSERT(!m_error); + + if (fast_resend + && ((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq + && m_mtu_seq != 0) + { + // we got multiple acks for the packet before our probe, assume + // it was dropped because it was too big + m_mtu_ceiling = m_mtu - 1; + update_mtu_limits(); + } + + // we can only resend the packet if there's + // enough space in our congestion window + int window_size_left = (std::min)(int(m_cwnd >> 16), int(m_adv_wnd)) - m_bytes_in_flight; + if (!fast_resend&& p->size - p->header_size > window_size_left) + { + m_last_cwnd_hit = time_now_hires(); + return false; + } + + // plus one since we have fast-resend as well, which doesn't + // necessarily trigger by a timeout + TORRENT_ASSERT(p->num_transmissions < m_sm->num_resends() + 1); + + TORRENT_ASSERT(p->size - p->header_size >= 0); + if (p->need_resend) m_bytes_in_flight += p->size - p->header_size; + + ++p->num_transmissions; + p->need_resend = false; + utp_header* h = (utp_header*)p->buf; + // update packet header + h->timestamp_difference_microseconds = m_reply_micro; + p->send_time = time_now_hires(); + h->timestamp_microseconds = boost::uint32_t(total_microseconds(p->send_time - min_time())); + + error_code ec; + m_sm->send_packet(udp::endpoint(m_remote_address, m_port) + , (char const*)p->buf, p->size, ec); + ++m_out_packets; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: re-sending packet seq_nr:%d ack_nr:%d type:%s " + "id:%d target:%s size:%d error:%s send_buffer_size:%d cwnd:%d " + "adv_wnd:%d in-flight:%d mtu:%d timestamp:%u time_diff:%u\n" + , this, int(h->seq_nr), int(h->ack_nr), packet_type_names[h->get_type()] + , m_send_id, print_endpoint(udp::endpoint(m_remote_address, m_port)).c_str() + , p->size, ec.message().c_str(), m_write_buffer_size, int(m_cwnd >> 16) + , m_adv_wnd, m_bytes_in_flight, m_mtu, boost::uint32_t(h->timestamp_microseconds) + , boost::uint32_t(h->timestamp_difference_microseconds)); +#endif + + if (ec) + { + m_error = ec; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return false; + } + + return true; +} + +void utp_socket_impl::experienced_loss(int seq_nr) +{ + // since loss often comes in bursts, we only cut the + // window in half once per RTT. This is implemented + // by limiting which packets can cause us to cut the + // window size. The first packet that's lost will + // update the limit to the last sequence number we sent. + // i.e. only packet sent after this loss can cause another + // window size cut + if (compare_less_wrap(seq_nr, m_loss_seq_nr, ACK_MASK)) return; + + // cut window size in 2 + m_cwnd = (std::max)(m_cwnd / 2, boost::int64_t(m_mtu << 16)); + m_loss_seq_nr = m_seq_nr; + UTP_LOGV("%8p: Lost packet %d caused cwnd cut\n", this, seq_nr); + + // the window size could go below one MMS here, if it does, + // we'll get a timeout in about one second +} + +void utp_socket_impl::ack_packet(packet* p, ptime const& receive_time + , boost::uint32_t& min_rtt, boost::uint16_t seq_nr) +{ + TORRENT_ASSERT(p); + if (!p->need_resend) + { + TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); + m_bytes_in_flight -= p->size - p->header_size; + } + + if (seq_nr == m_mtu_seq && m_mtu_seq != 0) + { + TORRENT_ASSERT(p->mtu_probe); + // our mtu probe was acked! + m_mtu_floor = m_mtu; + update_mtu_limits(); + } + + // increment the acked sequence number counter + if (((m_acked_seq_nr + 1) & ACK_MASK) == seq_nr) + { + m_acked_seq_nr = seq_nr; + // update loss seq number if it's less than the packet + // that was just acked. If loss seq nr is greater, it suggests + // that we're still in a window that has experienced loss + if (compare_less_wrap(m_loss_seq_nr, m_acked_seq_nr, ACK_MASK)) + m_loss_seq_nr = m_acked_seq_nr; + m_duplicate_acks = 0; + } + // increment the fast resend sequence number + if (m_fast_resend_seq_nr == seq_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + boost::uint32_t rtt = boost::uint32_t(total_microseconds(receive_time - p->send_time)); + if (receive_time < p->send_time) + { + // this means our clock is not monotonic. Just assume the RTT was 100 ms + rtt = 100000; + + // the clock for this plaform is not monotonic! + TORRENT_ASSERT(false); + } + + UTP_LOGV("%8p: acked packet %d (%d bytes) (rtt:%u)\n" + , this, seq_nr, p->size - p->header_size, rtt / 1000); + + m_rtt.add_sample(rtt / 1000); + if (rtt < min_rtt) min_rtt = rtt; + free(p); +} + +void utp_socket_impl::incoming(char const* buf, int size, packet* p, ptime now) +{ + while (!m_read_buffer.empty()) + { + if (p) + { + buf = p->buf + p->header_size; + TORRENT_ASSERT(p->size - p->header_size >= size); + } + iovec_t* target = &m_read_buffer.front(); + + int to_copy = (std::min)(size, int(target->len)); + memcpy(target->buf, buf, to_copy); + if (m_read == 0) + { + m_read_timeout = now + milliseconds(100); + UTP_LOGV("%8p: setting read timeout to 100 ms from now\n", this); + } + m_read += to_copy; + target->buf = ((char*)target->buf) + to_copy; + target->len -= to_copy; + buf += to_copy; + TORRENT_ASSERT(m_read_buffer_size >= to_copy); + m_read_buffer_size -= to_copy; + size -= to_copy; + if (target->len == 0) m_read_buffer.erase(m_read_buffer.begin()); + if (p) + { + p->header_size += to_copy; + TORRENT_ASSERT(p->header_size <= p->size); + } + + if (size == 0) + { + TORRENT_ASSERT(p == 0 || p->header_size == p->size); + free(p); + maybe_trigger_receive_callback(now); + return; + } + } + + TORRENT_ASSERT(m_read_buffer_size == 0); + + if (!p) + { + TORRENT_ASSERT(buf); + p = (packet*)malloc(sizeof(packet) + size); + p->size = size; + p->header_size = 0; + memcpy(p->buf, buf, size); + } + if (m_receive_buffer_size == 0) m_read_timeout = now + milliseconds(100); + // save this packet until the client issues another read + m_receive_buffer.push_back(p); + m_receive_buffer_size += p->size - p->header_size; + + check_receive_buffers(); +} + +bool utp_socket_impl::cancel_handlers(error_code const& ec, bool kill) +{ + TORRENT_ASSERT(ec); + bool ret = m_read_handler || m_write_handler || m_connect_handler; + if (m_read_handler) m_read_handler(m_userdata, 0, ec, kill); + m_read_handler = 0; + if (m_write_handler) m_write_handler(m_userdata, 0, ec, kill); + m_write_handler = 0; + if (m_connect_handler) m_connect_handler(m_userdata, ec, kill); + m_connect_handler = 0; + return ret; +} + +bool utp_socket_impl::consume_incoming_data( + utp_header const* ph, char const* ptr, int payload_size + , ptime now) +{ + if (ph->get_type() != ST_DATA) return false; + + if (m_eof && m_ack_nr == m_eof_seq_nr) + { + // What?! We've already received a FIN and everything up + // to it has been acked. Ignore this packet + return true; + } + + if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK)) + { + TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); + + // we received a packet in order + incoming(ptr, payload_size, 0, now); + m_ack_nr = (m_ack_nr + 1) & ACK_MASK; + + // If this packet was previously in the reorder buffer + // it would have been acked when m_ack_nr-1 was acked. + TORRENT_ASSERT(m_inbuf.at(m_ack_nr) == 0); + + UTP_LOGV("%8p: remove inbuf: %d (%d)\n" + , this, m_ack_nr, int(m_inbuf.size())); + + for (;;) + { + int const next_ack_nr = (m_ack_nr + 1) & ACK_MASK; + + packet* p = (packet*)m_inbuf.remove(next_ack_nr); + + if (!p) + break; + + m_buffered_incoming_bytes -= p->size - p->header_size; + incoming(0, p->size - p->header_size, p, now); + + m_ack_nr = next_ack_nr; + + UTP_LOGV("%8p: reordered remove inbuf: %d (%d)\n" + , this, m_ack_nr, int(m_inbuf.size())); + } + + // should we trigger the read handler? + maybe_trigger_receive_callback(now); + } + else + { + // this packet was received out of order. Stick it in the + // reorder buffer until it can be delivered in order + + // have we already received this packet and passed it on + // to the client? + if (!compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) + { + UTP_LOGV("%8p: already received seq_nr: %d\n" + , this, int(ph->seq_nr)); + return true; + } + + // do we already have this packet? If so, just ignore it + if (m_inbuf.at(ph->seq_nr)) + { + UTP_LOGV("%8p: already received seq_nr: %d\n" + , this, int(ph->seq_nr)); + return true; + } + + // we don't need to save the packet header, just the payload + packet* p = (packet*)malloc(sizeof(packet) + payload_size); + p->size = payload_size; + p->header_size = 0; + p->num_transmissions = 0; + p->need_resend = false; + memcpy(p->buf, ptr, payload_size); + m_inbuf.insert(ph->seq_nr, p); + m_buffered_incoming_bytes += p->size; + + UTP_LOGV("%8p: out of order. insert inbuf: %d (%d) m_ack_nr: %d\n" + , this, int(ph->seq_nr), int(m_inbuf.size()), m_ack_nr); + } + + return false; +} + +// returns true of the socket was closed +bool utp_socket_impl::test_socket_state() +{ + // if the socket is in a state where it's dead, just waiting to + // tell the client that it's closed. Do that and transition into + // the deleted state, where it will be deleted + // it might be possible to get here twice, in which we need to + // cancel any new handlers as well, even though we're already + // in the delete state + if (!m_error) return false; + TORRENT_ASSERT(m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s error:%s\n" + , this, socket_state_names[m_state], m_error.message().c_str()); +#endif + + if (cancel_handlers(m_error, true)) + { + m_state = UTP_STATE_DELETE; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + return true; + } + return false; +} + +void utp_socket_impl::init_mtu(int link_mtu, int utp_mtu) +{ + // if we're in a RAM constrained environment, don't increase + // the buffer size for interfaces with large MTUs. Just stick + // to ethernet frame sizes + if (m_sm->allow_dynamic_sock_buf()) + { + // Make sure that we have enough socket buffer space + // for sending and receiving packets of this size + // add 10% for smaller ACKs and other overhead + m_sm->set_sock_buf(link_mtu * 11 / 10); + } + else if (link_mtu > TORRENT_ETHERNET_MTU) + { + // we can't use larger packets than this since we're + // not allocating any more memory for socket buffers + int decrease = link_mtu - TORRENT_ETHERNET_MTU; + utp_mtu -= decrease; + link_mtu -= decrease; + } + + m_mtu = utp_mtu; + m_mtu_ceiling = utp_mtu; + if (m_mtu_floor > utp_mtu) m_mtu_floor = utp_mtu; + + // if the window size is smaller than one packet size + // set it to one + if ((m_cwnd >> 16) < m_mtu) m_cwnd = m_mtu << 16; + + UTP_LOGV("%8p: intializing MTU to: %d [%d, %d]\n" + , this, m_mtu, m_mtu_floor, m_mtu_ceiling); +} + +// return false if this is an invalid packet +bool utp_socket_impl::incoming_packet(char const* buf, int size + , udp::endpoint const& ep, ptime receive_time) +{ + utp_header* ph = (utp_header*)buf; + + if (ph->get_version() != 1) + { + UTP_LOGV("%8p: incoming packet version:%d (ignored)\n" + , this, int(ph->get_version())); + return false; + } + + // SYN packets have special (reverse) connection ids + if (ph->get_type() != ST_SYN && ph->connection_id != m_recv_id) + { + UTP_LOGV("%8p: incoming packet id:%d expected:%d (ignored)\n" + , this, int(ph->connection_id), int(m_recv_id)); + return false; + } + + if (ph->get_type() >= NUM_TYPES) + { + UTP_LOGV("%8p: incoming packet type:%d (ignored)\n" + , this, int(ph->get_type())); + return false; + } + + if (m_state == UTP_STATE_NONE && ph->get_type() == ST_SYN) + { + m_remote_address = ep.address(); + m_port = ep.port(); + } + + if (m_state != UTP_STATE_NONE && ph->get_type() == ST_SYN) + { + UTP_LOGV("%8p: incoming packet type:ST_SYN (ignored)\n", this); + return true; + } + + bool step = false; + if (receive_time - m_last_history_step > minutes(1)) + { + step = true; + m_last_history_step = receive_time; + } + + // this is the difference between their send time and our receive time + // 0 means no sample yet + boost::uint32_t their_delay = 0; + if (ph->timestamp_microseconds != 0) + { + m_reply_micro = boost::uint32_t(total_microseconds(receive_time - min_time())) + - ph->timestamp_microseconds; + boost::uint32_t prev_base = m_their_delay_hist.initialized() ? m_their_delay_hist.base() : 0; + their_delay = m_their_delay_hist.add_sample(m_reply_micro, step); + int base_change = m_their_delay_hist.base() - prev_base; + UTP_LOGV("%8p: their_delay::add_sample:%u prev_base:%u new_base:%u\n" + , this, m_reply_micro, prev_base, m_their_delay_hist.base()); + + if (prev_base && base_change < 0 && base_change > -10000) + { + // their base delay went down. This is caused by clock drift. To compensate, + // adjust our base delay upwards + // don't adjust more than 10 ms. If the change is that big, something is probably wrong + m_delay_hist.adjust_base(-base_change); + } + + UTP_LOGV("%8p: incoming packet reply_micro:%u base_change:%d\n" + , this, m_reply_micro, prev_base ? base_change : 0); + } + + if (ph->get_type() == ST_RESET) + { + UTP_LOGV("%8p: incoming packet type:RESET\n", this); + m_error = asio::error::connection_reset; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return true; + } + + // is this ACK valid? If the other end is ACKing + // a packet that hasn't been sent yet + // just ignore it. A 3rd party could easily inject a packet + // like this in a stream, don't sever it because of it. + // since m_seq_nr is the sequence number of the next packet + // we'll send (and m_seq_nr-1 was the last packet we sent), + // if the ACK we got is greater than the last packet we sent + // something is wrong. + // If our state is state_none, this packet must be a syn packet + // and the ack_nr should be ignored + boost::uint16_t cmp_seq_nr = (m_seq_nr - 1) & ACK_MASK; +#if TORRENT_UT_SEQ + if (m_state == UTP_STATE_SYN_SENT && ph->get_type() == ST_STATE) + cmp_seq_nr = m_seq_nr; +#endif + if (m_state != UTP_STATE_NONE + && compare_less_wrap(cmp_seq_nr, ph->ack_nr, ACK_MASK)) + { + UTP_LOGV("%8p: incoming packet ack_nr:%d our seq_nr:%d (ignored)\n" + , this, int(ph->ack_nr), m_seq_nr); + return true; + } + + // check to make sure the sequence number of this packet + // is reasonable. If it's a data packet and we've already + // received it, ignore it. This is either a stray old packet + // that finally made it here (after having been re-sent) or + // an attempt to interfere with the connection from a 3rd party + // in both cases, we can safely ignore the timestamp and ACK + // information in this packet +/* + // even if we've already received this packet, we need to + // send another ack to it, since it may be a resend caused by + // our ack getting dropped + if (m_state != UTP_STATE_SYN_SENT + && ph->get_type() == ST_DATA + && !compare_less_wrap(m_ack_nr, ph->seq_nr, ACK_MASK)) + { + // we've already received this packet + UTP_LOGV("%8p: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" + , this, int(ph->seq_nr), m_ack_nr); + return true; + } +*/ + + // if the socket is closing, always ignore any packet + // with a higher sequence number than the FIN sequence number + if (m_eof && compare_less_wrap(m_eof_seq_nr, ph->seq_nr, ACK_MASK)) + { + UTP_LOGV("%8p: incoming packet seq_nr:%d eof_seq_nr:%d (ignored)\n" + , this, int(ph->seq_nr), m_eof_seq_nr); + + } + + if (m_state != UTP_STATE_NONE + && m_state != UTP_STATE_SYN_SENT + && compare_less_wrap((m_ack_nr + max_packets_reorder) & ACK_MASK, ph->seq_nr, ACK_MASK)) + { + // this is too far out to fit in our reorder buffer. Drop it + // This is either an attack to try to break the connection + // or a seariously damaged connection that lost a lot of + // packets. Neither is very likely, and it should be OK + // to drop the timestamp information. + UTP_LOGV("%8p: incoming packet seq_nr:%d our ack_nr:%d (ignored)\n" + , this, int(ph->seq_nr), m_ack_nr); + return true; + } + + ++m_in_packets; + + // this is a valid incoming packet, update the timeout timer + m_num_timeouts = 0; + m_timeout = receive_time + milliseconds(packet_timeout()); + UTP_LOGV("%8p: updating timeout to: now + %d\n" + , this, packet_timeout()); + + // the test for INT_MAX here is a work-around for a bug in uTorrent where + // it's sometimes sent as INT_MAX when it is in fact uninitialized + const boost::uint32_t sample = ph->timestamp_difference_microseconds == INT_MAX + ? 0 : ph->timestamp_difference_microseconds; + + boost::uint32_t delay = 0; + if (sample != 0) + { + delay = m_delay_hist.add_sample(sample, step); + m_delay_sample_hist[m_delay_sample_idx++] = delay; + if (m_delay_sample_idx >= num_delay_hist) m_delay_sample_idx = 0; + } + + int acked_bytes = 0; + + TORRENT_ASSERT(m_bytes_in_flight >= 0); + int prev_bytes_in_flight = m_bytes_in_flight; + + m_adv_wnd = ph->wnd_size; + + // if we get an ack for the same sequence number as + // was last ACKed, and we have outstanding packets, + // it counts as a duplicate ack + if (ph->ack_nr == m_acked_seq_nr && m_outbuf.size()) + { + ++m_duplicate_acks; + } + + boost::uint32_t min_rtt = UINT_MAX; + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // has this packet already been ACKed? + // if the ACK we just got is less than the max ACKed + // sequence number, it doesn't tell us anything. + // So, only act on it if the ACK is greater than the last acked + // sequence number + if (m_state != UTP_STATE_NONE && compare_less_wrap(m_acked_seq_nr, ph->ack_nr, ACK_MASK)) + { + int const next_ack_nr = ph->ack_nr; + + for (int ack_nr = (m_acked_seq_nr + 1) & ACK_MASK; + ack_nr != ((next_ack_nr + 1) & ACK_MASK); + ack_nr = (ack_nr + 1) & ACK_MASK) + { + if (m_fast_resend_seq_nr == ack_nr) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + packet* p = (packet*)m_outbuf.remove(ack_nr); + if (!p) + { + if (((m_acked_seq_nr + 1) & ACK_MASK) == ack_nr) + m_acked_seq_nr = ack_nr; + continue; + } + acked_bytes += p->size - p->header_size; + ack_packet(p, receive_time, min_rtt, ack_nr); + } + + // update loss seq number if it's less than the packet + // that was just acked. If loss seq nr is greater, it suggests + // that we're still in a window that has experienced loss + if (compare_less_wrap(m_loss_seq_nr, m_acked_seq_nr, ACK_MASK)) + m_loss_seq_nr = m_acked_seq_nr; + + m_duplicate_acks = 0; + if (compare_less_wrap(m_fast_resend_seq_nr, (m_acked_seq_nr + 1) & ACK_MASK, ACK_MASK)) + m_fast_resend_seq_nr = (m_acked_seq_nr + 1) & ACK_MASK; + } + + // look for extended headers + char const* ptr = buf; + ptr += sizeof(utp_header); + + unsigned int extension = ph->extension; + while (extension) + { + // invalid packet. It says it has an extension header + // but the packet is too short + if (ptr - buf + 2 > size) + { + UTP_LOGV("%8p: invalid extension header\n", this); + return true; + } + int next_extension = unsigned(*ptr++); + unsigned int len = unsigned(*ptr++); + if (ptr - buf + len > size) + { + UTP_LOGV("%8p: invalid extension header size:%d packet:%d\n" + , this, len, int(ptr - buf)); + return true; + } + switch(extension) + { + case 1: // selective ACKs + parse_sack(ph->ack_nr, ptr, len, &acked_bytes, receive_time, min_rtt); + break; + } + ptr += len; + extension = next_extension; + } + + // the send operation in parse_sack() may have set the socket to an error + // state, in which case we shouldn't continue + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + + if (m_duplicate_acks >= dup_ack_limit + && ((m_acked_seq_nr + 1) & ACK_MASK) == m_fast_resend_seq_nr) + { + // LOSS + + UTP_LOGV("%8p: Packet %d lost.\n", this, m_fast_resend_seq_nr); + + // resend the lost packet + packet* p = (packet*)m_outbuf.at(m_fast_resend_seq_nr); + TORRENT_ASSERT(p); + if (p) + { + experienced_loss(m_fast_resend_seq_nr); + resend_packet(p, true); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + // don't fast-resend this again + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + } + + // ptr points to the payload of the packet + // size is the packet size, payload is the + // number of payload bytes are in this packet + const int header_size = ptr - buf; + const int payload_size = size - header_size; + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: incoming packet seq_nr:%d ack_nr:%d type:%s id:%d size:%d timestampdiff:%u timestamp:%u " + "our ack_nr:%d our seq_nr:%d our acked_seq_nr:%d our state:%s\n" + , this, int(ph->seq_nr), int(ph->ack_nr), packet_type_names[ph->get_type()] + , int(ph->connection_id), payload_size, boost::uint32_t(ph->timestamp_difference_microseconds) + , boost::uint32_t(ph->timestamp_microseconds), m_ack_nr, m_seq_nr, m_acked_seq_nr, socket_state_names[m_state]); +#endif + + if (ph->get_type() == ST_FIN) + { + // We ignore duplicate FIN packets, but we still need to ACK them. + if (ph->seq_nr == ((m_ack_nr + 1) & ACK_MASK) + || ph->seq_nr == m_ack_nr) + { + UTP_LOGV("%8p: FIN received in order\n", this); + + // The FIN arrived in order, nothing else is in the + // reorder buffer. + +// TORRENT_ASSERT(m_inbuf.size() == 0); + m_ack_nr = ph->seq_nr; + + // Transition to UTP_STATE_FIN_SENT. The sent FIN is also an ack + // to the FIN we received. Once we're in UTP_STATE_FIN_SENT we + // just need to wait for our FIN to be acked. + + if (m_state == UTP_STATE_FIN_SENT) + { + send_pkt(true); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + else + { + send_fin(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + } + + if (m_eof) + { + UTP_LOGV("%8p: duplicate FIN packet (ignoring)\n", this); + return true; + } + m_eof = true; + m_eof_seq_nr = ph->seq_nr; + + // we will respond with a fin once we have received everything up to m_eof_seq_nr + } + + switch (m_state) + { + case UTP_STATE_NONE: + { + if (ph->get_type() == ST_SYN) + { + // if we're in state_none, the only thing + // we accept are SYN packets. + m_state = UTP_STATE_CONNECTED; + + m_remote_address = ep.address(); + m_port = ep.port(); + +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n" + , this, socket_state_names[m_state]); +#endif + m_ack_nr = ph->seq_nr; + m_seq_nr = rand(); + m_acked_seq_nr = (m_seq_nr - 1) & ACK_MASK; + m_loss_seq_nr = m_acked_seq_nr; + + TORRENT_ASSERT(m_send_id == ph->connection_id); + TORRENT_ASSERT(m_recv_id == ((m_send_id + 1) & 0xffff)); + + send_pkt(true); + + return true; + } + else + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: type:%s state:%s (ignored)\n" + , this, packet_type_names[ph->get_type()], socket_state_names[m_state]); +#endif + return true; + } + break; + } + case UTP_STATE_SYN_SENT: + { + // just wait for an ack to our SYN, ignore everything else + if (ph->ack_nr != ((m_seq_nr - 1) & ACK_MASK)) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: incorrect ack_nr (%d) waiting for %d\n" + , this, int(ph->ack_nr), (m_seq_nr - 1) & ACK_MASK); +#endif + return true; + } + + TORRENT_ASSERT(!m_error); + m_state = UTP_STATE_CONNECTED; +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: state:%s\n", this, socket_state_names[m_state]); +#endif + + // only progress our ack_nr on ST_DATA messages + // since our m_ack_nr is uninitialized at this point + // we still need to set it to something regardless + if (ph->get_type() == ST_DATA) + m_ack_nr = ph->seq_nr; + else + m_ack_nr = (ph->seq_nr - 1) & ACK_MASK; + + // notify the client that the socket connected + if (m_connect_handler) + { + UTP_LOGV("%8p: calling connect handler\n", this); + m_connect_handler(m_userdata, m_error, false); + } + m_connect_handler = 0; + // fall through + } + case UTP_STATE_CONNECTED: + { + // the lowest seen RTT can be used to clamp the delay + // within reasonable bounds. The one-way delay is never + // higher than the round-trip time. + + // it's impossible for delay to be more than the RTT, so make + // sure to clamp it as a sanity check + if (delay > min_rtt) delay = min_rtt; + + // only use the minimum from the last 3 delay measurements + delay = *std::min_element(m_delay_sample_hist, m_delay_sample_hist + num_delay_hist); + + if (sample && acked_bytes && prev_bytes_in_flight) + do_ledbat(acked_bytes, delay, prev_bytes_in_flight, receive_time); + + consume_incoming_data(ph, ptr, payload_size, receive_time); + + // the parameter to send_pkt tells it if we're acking data + // If we are, we'll send an ACK regardless of if we have any + // space left in our send window or not. If we just got an ACK + // (i.e. ST_STATE) we're not ACKing anything. If we just + // received a FIN packet, we need to ack that as well + bool has_ack = ph->get_type() == ST_DATA || ph->get_type() == ST_FIN || ph->get_type() == ST_SYN; + int delayed_ack = m_sm->delayed_ack(); + if (has_ack && delayed_ack && m_ack_timer > receive_time) + { + // we have data to ACK, and delayed ACKs are enabled. + // update the ACK timer and clear the flag, to pretend + // like we don't have anything to ACK + m_ack_timer = (std::min)(m_ack_timer, receive_time + milliseconds(delayed_ack)); + has_ack = false; + UTP_LOGV("%8p: delaying ack. timer triggers in %d milliseconds\n" + , this, int(total_milliseconds(m_ack_timer - time_now_hires()))); + } + + if (send_pkt(has_ack)) + { + // try to send more data as long as we can + while (send_pkt(false)); + } + maybe_trigger_send_callback(receive_time); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + + // Everything up to the FIN has been receieved, respond with a FIN + // from our side. + if (m_eof && m_ack_nr == ((m_eof_seq_nr - 1) & ACK_MASK)) + { + UTP_LOGV("%8p: incoming stream consumed\n", this); + + // This transitions to the UTP_STATE_FIN_SENT state. + send_fin(); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return true; + } + +#if TORRENT_UTP_LOG + if (sample && acked_bytes && prev_bytes_in_flight) + { + char their_delay_base[20]; + if (m_their_delay_hist.initialized()) + snprintf(their_delay_base, sizeof(their_delay_base), "%u", m_their_delay_hist.base()); + else + strcpy(their_delay_base, "-"); + + char our_delay_base[20]; + if (m_delay_hist.initialized()) + snprintf(our_delay_base, sizeof(our_delay_base), "%u", m_delay_hist.base()); + else + strcpy(our_delay_base, "-"); + + UTP_LOG("%8p: " + "actual_delay:%u " + "our_delay:%f " + "their_delay:%f " + "off_target:%f " + "max_window:%u " + "upload_rate:%d " + "delay_base:%s " + "delay_sum:%f " + "target_delay:%d " + "acked_bytes:%d " + "cur_window:%d " + "scaled_gain:%f " + "rtt:%u " + "rate:%d " + "quota:%d " + "wnduser:%u " + "rto:%d " + "timeout:%d " + "get_microseconds:%u " + "cur_window_packets:%u " + "packet_size:%d " + "their_delay_base:%s " + "their_actual_delay:%u " + "seq_nr:%u " + "acked_seq_nr:%u " + "reply_micro:%u " + "min_rtt:%u " + "send_buffer:%d " + "recv_buffer:%d " + "\n" + , this + , sample + , float(delay / 1000.f) + , float(their_delay / 1000.f) + , float(int(m_sm->target_delay() - delay)) / 1000.f + , boost::uint32_t(m_cwnd >> 16) + , 0 + , our_delay_base + , float(delay + their_delay) / 1000.f + , m_sm->target_delay() / 1000 + , acked_bytes + , m_bytes_in_flight + , 0.f // float(scaled_gain) + , m_rtt.mean() + , int(m_cwnd * 1000 / (m_rtt.mean()?m_rtt.mean():50)) >> 16 + , 0 + , m_adv_wnd + , packet_timeout() + , int(total_milliseconds(m_timeout - receive_time)) + , int(total_microseconds(receive_time - min_time())) + , (m_seq_nr - m_acked_seq_nr) & ACK_MASK + , m_mtu + , their_delay_base + , boost::uint32_t(m_reply_micro) + , m_seq_nr + , m_acked_seq_nr + , m_reply_micro + , min_rtt / 1000 + , m_write_buffer_size + , m_read_buffer_size); + } +#endif + + return true; + } + case UTP_STATE_FIN_SENT: + { + // There are two ways we can end up in this state: + // + // 1. If the socket has been explicitly closed on our + // side, in which case m_eof is false. + // + // 2. If we received a FIN from the remote side, in which + // case m_eof is true. If this is the case, we don't + // come here until everything up to the FIN has been + // received. + // + // + // + + // At this point m_seq_nr - 1 is the FIN sequence number. + + // We can receive both ST_DATA and ST_STATE here, because after + // we have closed our end of the socket, the remote end might + // have data in the pipeline. We don't really care about the + // data, but we do have to ack it. Or rather, we have to ack + // the FIN that will come after the data. + + // Case 1: + // --------------------------------------------------------------- + // + // If we are here because the local endpoint was closed, we need + // to first wait for all of our messages to be acked: + // + // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + // + // `m_seq_nr - 1` is the ST_FIN message that we sent. + // + // ---------------------- + // + // After that has happened we need to wait for the remote side + // to send its ST_FIN message. When we receive that we send an + // ST_STATE back to ack, and wait for a sufficient period. + // During this wait we keep acking incoming ST_FIN's. This is + // all handled at the top of this function. + // + // Note that the user handlers are all cancelled when the initial + // close() call happens, so nothing will happen on the user side + // after that. + + // Case 2: + // --------------------------------------------------------------- + // + // If we are here because we received a ST_FIN message, and then + // sent our own ST_FIN to ack that, we need to wait for our ST_FIN + // to be acked: + // + // if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + // + // `m_seq_nr - 1` is the ST_FIN message that we sent. + // + // After that has happened we know the remote side has all our + // data, and we can gracefully shut down. + + if (consume_incoming_data(ph, ptr, payload_size, receive_time)) + return true; + + if (m_acked_seq_nr == ((m_seq_nr - 1) & ACK_MASK)) + { + // When this happens we know that the remote side has + // received all of our packets. + + UTP_LOGV("%8p: FIN acked\n", this); + + if (!m_attached) + { + UTP_LOGV("%8p: close initiated here, delete socket\n", this); + m_error = asio::error::eof; + m_state = UTP_STATE_DELETE; + test_socket_state(); + } + else + { + UTP_LOGV("%8p: closing socket\n", this); + m_error = asio::error::eof; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + } + } + + return true; + } + case UTP_STATE_DELETE: + default: + { + // respond with a reset + send_reset(ph); + return true; + } + } + + return false; +} + +void utp_socket_impl::do_ledbat(int acked_bytes, int delay, int in_flight, ptime const now) +{ + // the portion of the in-flight bytes that were acked. This is used to make + // the gain factor be scaled by the rtt. The formula is applied once per + // rtt, or on every ACK skaled by the number of ACKs per rtt + TORRENT_ASSERT(in_flight > 0); + TORRENT_ASSERT(acked_bytes > 0); + + int target_delay = m_sm->target_delay(); + + // all of these are fixed points with 16 bits fraction portion + boost::int64_t window_factor = (boost::int64_t(acked_bytes) << 16) / in_flight; + boost::int64_t delay_factor = (boost::int64_t(target_delay - delay) << 16) / target_delay; + boost::int64_t scaled_gain = (window_factor * delay_factor) >> 16; + scaled_gain *= boost::int64_t(m_sm->gain_factor()); + + if (scaled_gain > 0 && m_last_cwnd_hit + milliseconds((std::max)(m_rtt.mean(), 10)) < now) + { + // we haven't bumped into the cwnd limit size in the last second + // this probably means we have a send rate limit, so we shouldn't make + // the cwnd size any larger + scaled_gain = 0; + } + + UTP_LOGV("%8p: do_ledbat delay:%d off_target: %d window_factor:%f target_factor:%f " + "scaled_gain:%f cwnd:%d\n" + , this, delay, target_delay - delay, window_factor / float(1 << 16) + , delay_factor / float(1 << 16) + , scaled_gain / float(1 << 16), int(m_cwnd >> 16)); + + // if scaled_gain + m_cwnd <= 0, set m_cwnd to 0 + if (-scaled_gain >= m_cwnd) + { + m_cwnd = 0; + } + else + { + m_cwnd += scaled_gain; + TORRENT_ASSERT(m_cwnd > 0); + } +} + +void utp_stream::bind(endpoint_type const& ep, error_code& ec) { } + +// returns the number of milliseconds a packet would have before +// it would time-out if it was sent right now. Takes the RTT estimate +// into account +int utp_socket_impl::packet_timeout() const +{ + // SYN packets have a bit longer timeout, since we don't + // have an RTT estimate yet, make a conservative guess + if (m_state == UTP_STATE_NONE) return 3000; + + int timeout = (std::max)(m_sm->min_timeout(), m_rtt.mean() + m_rtt.avg_deviation() * 2); + if (m_num_timeouts > 0) timeout += (1 << (int(m_num_timeouts) - 1)) * 1000; + return timeout; +} + +void utp_socket_impl::tick(ptime const& now) +{ +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: tick:%s r: %d (%s) w: %d (%s)\n" + , this, socket_state_names[m_state], m_read, m_read_handler ? "handler" : "no handler" + , m_written, m_write_handler ? "handler" : "no handler"); +#endif + bool window_opened = false; + + TORRENT_ASSERT(m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK) || ((m_seq_nr - m_acked_seq_nr) & ACK_MASK) <= 1); + + // don't hang on to received data for too long, and don't + // wait too long telling the client we've sent some data. + // these functions will trigger time callback if we have + // a reason to and it's been long enough since we sent or + // received the data + maybe_trigger_receive_callback(now); + maybe_trigger_send_callback(now); + + // if we're already in an error state, we're just waiting for the + // client to perform an operation so that we can communicate the + // error. No need to do anything else with this socket + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + + if (now > m_timeout) + { + // TIMEOUT! + // set cwnd to 1 MSS + + // the window went from less than one MSS to one MSS + // we can now sent messages again, the send window was opened + if ((m_cwnd >> 16) < m_mtu) window_opened = true; + + m_cwnd = m_mtu << 16; + if (m_outbuf.size()) ++m_num_timeouts; + m_timeout = now + milliseconds(packet_timeout()); + + UTP_LOGV("%8p: timeout resetting cwnd:%d\n" + , this, int(m_cwnd >> 16)); + + if (((m_acked_seq_nr + 1) & ACK_MASK) == m_mtu_seq + && ((m_seq_nr - 1) & ACK_MASK) == m_mtu_seq + && m_mtu_seq != 0) + { + // we timed out, and the only outstanding packet + // we had was the probe. Assume it was dropped + // because it was too big + m_mtu_ceiling = m_mtu - 1; + update_mtu_limits(); + } + + // we dropped all packets, that includes the mtu probe + m_mtu_seq = 0; + + // since we've already timed out now, don't count + // loss that we might detect for packets that just + // timed out + m_loss_seq_nr = m_seq_nr; + + // we need to go one past m_seq_nr to cover the case + // where we just sent a SYN packet and then adjusted for + // the uTorrent sequence number reuse + for (int i = m_acked_seq_nr & ACK_MASK; + i != ((m_seq_nr + 1) & ACK_MASK); + i = (i + 1) & ACK_MASK) + { + packet* p = (packet*)m_outbuf.at(i); + if (!p) continue; + if (p->need_resend) continue; + p->need_resend = true; + TORRENT_ASSERT(m_bytes_in_flight >= p->size - p->header_size); + m_bytes_in_flight -= p->size - p->header_size; + UTP_LOGV("%8p: Packet %d lost.\n", this, i); + } + + TORRENT_ASSERT(m_bytes_in_flight == 0); + + // if we have a packet that needs re-sending, resend it + packet* p = (packet*)m_outbuf.at((m_acked_seq_nr + 1) & ACK_MASK); + if (p) + { + if (p->num_transmissions >= m_sm->num_resends() + || (m_state == UTP_STATE_SYN_SENT && p->num_transmissions >= m_sm->syn_resends()) + || (m_state == UTP_STATE_FIN_SENT && p->num_transmissions >= m_sm->fin_resends())) + { +#if TORRENT_UTP_LOG + UTP_LOGV("%8p: %d failed sends in a row. Socket timed out. state:%s\n" + , this, p->num_transmissions, socket_state_names[m_state]); +#endif + + // the connection is dead + m_error = asio::error::timed_out; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + + // don't fast-resend this packet + if (m_fast_resend_seq_nr == ((m_acked_seq_nr + 1) & ACK_MASK)) + m_fast_resend_seq_nr = (m_fast_resend_seq_nr + 1) & ACK_MASK; + + // the packet timed out, resend it + resend_packet(p); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + else if (m_state < UTP_STATE_FIN_SENT) + { + send_pkt(false); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + else if (m_state == UTP_STATE_FIN_SENT) + { + // the connection is dead + m_error = asio::error::eof; + m_state = UTP_STATE_ERROR_WAIT; + test_socket_state(); + return; + } + } + + if (now > m_ack_timer) + { + UTP_LOGV("%8p: ack timer expired, sending ACK\n", this); + // we need to send an ACK now! + send_pkt(true); + if (m_state == UTP_STATE_ERROR_WAIT || m_state == UTP_STATE_DELETE) return; + } + + switch (m_state) + { + case UTP_STATE_NONE: + case UTP_STATE_DELETE: + return; +// case UTP_STATE_SYN_SENT: +// +// break; + } +} + +void utp_socket_impl::check_receive_buffers() const +{ + std::size_t size = 0; + + for (std::vector::const_iterator i = m_receive_buffer.begin() + , end(m_receive_buffer.end()); i != end; ++i) + { + if (packet const* p = *i) + size += p->size - p->header_size; + } + + TORRENT_ASSERT(size == m_receive_buffer_size); +} + +} + diff --git a/test/Jamfile b/test/Jamfile index 8a2c1efc9..813fced4e 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -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 ] diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp index f11b02630..9a16dce12 100644 --- a/test/setup_transfer.cpp +++ b/test/setup_transfer.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)); diff --git a/test/test_primitives.cpp b/test/test_primitives.cpp index 1d11f7626..f45fc98f5 100644 --- a/test/test_primitives.cpp +++ b/test/test_primitives.cpp @@ -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 diff --git a/test/test_utp.cpp b/test/test_utp.cpp new file mode 100644 index 000000000..22ec980a4 --- /dev/null +++ b/test/test_utp.cpp @@ -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 +#include + +#include "test.hpp" +#include "setup_transfer.hpp" +#include +#include + +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 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; +} +