From 273f8bd92169a84828593d7a597a2c2c8eacd8a8 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Wed, 6 Jun 2007 00:41:20 +0000 Subject: [PATCH] merged back encryption branch to trunk --- AUTHORS | 1 + ChangeLog | 1 + Jamfile | 15 +- docs/building.html | 21 +- docs/building.rst | 20 +- docs/dht_extensions.html | 12 +- docs/features.html | 30 +- docs/manual.html | 836 ++++++++-------- docs/manual.rst | 84 +- examples/client_test.cpp | 6 +- include/Makefile.am | 1 + include/libtorrent/aux_/session_impl.hpp | 10 + include/libtorrent/bt_peer_connection.hpp | 109 ++- include/libtorrent/buffer.hpp | 6 + include/libtorrent/pe_crypto.hpp | 125 +++ include/libtorrent/peer_connection.hpp | 9 +- include/libtorrent/peer_id.hpp | 7 + include/libtorrent/peer_info.hpp | 4 + include/libtorrent/policy.hpp | 11 + include/libtorrent/session.hpp | 5 + include/libtorrent/session_settings.hpp | 36 + src/Makefile.am | 3 +- src/bt_peer_connection.cpp | 1085 +++++++++++++++++++-- src/http_tracker_connection.cpp | 4 + src/pe_crypto.cpp | 122 +++ src/peer_connection.cpp | 37 +- src/policy.cpp | 8 +- src/session.cpp | 12 + src/session_impl.cpp | 8 + src/ut_pex.cpp | 22 +- test/Jamfile | 1 + test/Makefile.am | 5 +- test/setup_transfer.cpp | 8 +- test/setup_transfer.hpp | 2 +- test/test_pe_crypto.cpp | 194 ++++ 35 files changed, 2323 insertions(+), 537 deletions(-) create mode 100644 include/libtorrent/pe_crypto.hpp create mode 100644 src/pe_crypto.cpp create mode 100644 test/test_pe_crypto.cpp diff --git a/AUTHORS b/AUTHORS index bb244d62c..dd56b1e89 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,6 +6,7 @@ Massaroddel Tianhao Qiu. Contributions by: +Shyam Magnus Jonsson Daniel Wallin Cory Nelson diff --git a/ChangeLog b/ChangeLog index c38433775..b94bd89f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * added encryption support * added parole mode for peers whose data fails the hash check. * optimized heap usage in piece-picker and web seed downloader. * fixed bug in DHT where older write tokens weren't accepted. diff --git a/Jamfile b/Jamfile index 44f3ea892..33755e186 100755 --- a/Jamfile +++ b/Jamfile @@ -28,6 +28,10 @@ feature dht-support : on off logging : composite propagated symmetric link-incom feature.compose off : TORRENT_DISABLE_DHT ; feature.compose logging : TORRENT_DHT_VERBOSE_LOGGING ; +feature pe-support : on off : composite propagated symmetric ; +feature.compose on : ; +feature.compose off : TORRENT_DISABLE_ENCRYPTION ; + feature openssl : off on : composite propagated symmetric link-incompatible ; feature.compose on : TORRENT_USE_OPENSSL ; @@ -37,6 +41,8 @@ feature.compose off : TORRENT_DISABLE_RESOLVE_COUNTRI feature character-set : ansi unicode : composite propagated link-incompatible ; feature.compose unicode : _UNICODE UNICODE ; +feature zlib : shipped system : composite ; + feature statistics : off on : composite propagated symmetric link-incompatible ; feature.compose on : TORRENT_STATS ; @@ -136,7 +142,7 @@ project torrent ./include ./include/libtorrent - ./zlib + shipped:./zlib $(BOOST_ROOT) release:NDEBUG BOOST_ALL_NO_LIB @@ -179,13 +185,16 @@ project torrent lib torrent : src/$(SOURCES).cpp - zlib/$(ZLIB_SOURCES).c $(LIBS) # pch : on:src/$(KADEMLIA_SOURCES).cpp logging:src/$(KADEMLIA_SOURCES).cpp - off:src/sha1.cpp + shipped:zlib/$(ZLIB_SOURCES).c + system:z + on:src/pe_crypto.cpp + on:crypt on:crypt + off,off:src/sha1.cpp ; diff --git a/docs/building.html b/docs/building.html index 3a47a7b30..79fd597ba 100644 --- a/docs/building.html +++ b/docs/building.html @@ -206,6 +206,24 @@ logging of the DHT protocol traffic. +zlib +
    +
  • system - links against the zlib supplied +with your operating system.
  • +
  • shipped - links against the zlib bundled +with the libtorrent package.
  • +
+ + +pe-support +
    +
  • on - turns on support for encrypted +connections. requires openssl (libcrypto)
  • +
  • off - turns off support for encrypted +connections. openssl is not linked in.
  • +
+ + link
  • static - builds libtorrent as a static @@ -273,7 +291,8 @@ boost.program-options symbols.

    sure all build variants will actually compile), you can invoke this command:

     bjam debug release link=shared link=static logging=verbose logging=default \
    -logging=none dht-support=on dht-support=logging dht-support=off
    +logging=none dht-support=on dht-support=logging dht-support=off pe-support=on \
    +pe-support=off zlib=shipped zlib=system
     
    diff --git a/docs/building.rst b/docs/building.rst index c456fd55d..61e788c88 100644 --- a/docs/building.rst +++ b/docs/building.rst @@ -208,6 +208,16 @@ Build features: | | logging of the DHT protocol traffic. | | | * ``off`` - build without DHT support. | +------------------------+----------------------------------------------------+ +| ``zlib`` | * ``system`` - links against the zlib supplied | +| | with your operating system. | +| | * ``shipped`` - links against the zlib bundled | +| | with the libtorrent package. | ++------------------------+----------------------------------------------------+ +| ``pe-support`` | * ``on`` - turns on support for encrypted | +| | connections. requires openssl (libcrypto) | +| | * ``off`` - turns off support for encrypted | +| | connections. openssl is not linked in. | ++------------------------+----------------------------------------------------+ | ``link`` | * ``static`` - builds libtorrent as a static | | | library (.a / .lib) | | | * ``shared`` - builds libtorrent as a shared | @@ -261,7 +271,9 @@ To build all possible variants of libtorrent (good for testing when making sure all build variants will actually compile), you can invoke this command:: bjam debug release link=shared link=static logging=verbose logging=default \ - logging=none dht-support=on dht-support=logging dht-support=off + logging=none dht-support=on dht-support=logging dht-support=off pe-support=on \ + pe-support=off zlib=shipped zlib=system openssl=on openssl=off \ + character-set=ansi character-set=unicode building with autotools ----------------------- @@ -472,6 +484,12 @@ defines you can use to control the build. | ``TORRENT_DHT_VERBOSE_LOGGING`` | This will enable verbose logging of the DHT | | | protocol traffic. | +---------------------------------+-------------------------------------------------+ +| ``TORRENT_DISABLE_ENCRYPTION`` | This will disable any encryption support and | +| | the openssl dependency that comes with it. | +| | Encryption support is the peer connection | +| | encrypted supported by clients such as | +| | uTorrent, Azureus and KTorrent. | ++---------------------------------+-------------------------------------------------+ If you experience that libtorrent uses unreasonable amounts of cpu, it will diff --git a/docs/dht_extensions.html b/docs/dht_extensions.html index 96c6b39ed..4fda3f42f 100644 --- a/docs/dht_extensions.html +++ b/docs/dht_extensions.html @@ -19,11 +19,11 @@ Arvid Norberg, arvid@rasterbar.com -
    -

    Mainline DHT extensions

    +
    +

    Mainline DHT extensions

    libtorrent implements a few extensions to the Mainline DHT protocol.

    -
    -

    client identification

    +
    +

    client identification

    In each DHT packet, an extra key is inserted named "v". This is a string describing the client and version used. This can help alot when debugging and finding errors in client implementations. The string is encoded as four @@ -51,8 +51,8 @@ as a binary number describing the client version.

    -
    -

    IPv6 support

    +
    +

    IPv6 support

    The DHT messages that don't support IPv6 are the nodes replies. They encode all the contacts as 6 bytes packed together in sequence in a string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but diff --git a/docs/features.html b/docs/features.html index 646959a62..1e1dd486c 100644 --- a/docs/features.html +++ b/docs/features.html @@ -20,16 +20,16 @@

    -
    -

    introduction

    +
    +

    introduction

    libtorrent is a C++ library that aims to be a good alternative to all the other bittorrent implementations around. It is a library and not a full featured client, although it comes with a working @@ -41,8 +41,8 @@ example client.

  • to be very easy to use
-
-

features

+
+

features

libtorrent is still being developed, however it is stable. It is an ongoing project (including this documentation). The current state includes the following features:

@@ -67,7 +67,7 @@ as well as all local peers in a separate fast-resume file. This means it can download parts of the same piece from different peers. It will also prefer to download whole pieces from single peers if the download speed is high enough from that particular peer. -
  • supports the udp-tracker protocol by Olaf van der Spek.
  • +
  • supports the udp-tracker protocol by Olaf van der Spek.
  • queues torrents for file check, instead of checking all of them in parallel.
  • supports http proxies and basic proxy authentication
  • gzipped tracker-responses
  • @@ -76,7 +76,7 @@ unchoked peers
  • implements fair trade. User settable trade-ratio, must at least be 1:1, but one can choose to trade 1 for 2 or any other ratio that isn't unfair to the other party.
  • -
  • supports an extension protocol. See extensions.
  • +
  • supports an extension protocol. See extensions.
  • supports the no_peer_id=1 extension that will ease the load off trackers.
  • possibility to limit the number of connections.
  • delays have messages if there's no other outgoing traffic to the peer, and @@ -91,8 +91,8 @@ want to download.
  • being connected
    -
    -

    portability

    +
    +

    portability

    libtorrent is portable at least among Windows, MacOS X and other UNIX-systems. It uses Boost.Thread, Boost.Filesystem, Boost.Date_time and various other boost libraries as well as zlib (shipped) and asio (shipped). At least version @@ -114,8 +114,8 @@ epoll on linux and kqueue on MacOS X and BSD.

  • msvc6
  • -
    -

    license

    +
    +

    license

    libtorrent is released under the BSD-license.

    This means that you can use the library in your project without having to release its source code. The only requirement is that you give credit diff --git a/docs/manual.html b/docs/manual.html index f23744f2b..a1e5962b4 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -22,158 +22,160 @@

    -

    Table of contents

    +

    Table of contents

    -
    -

    overview

    +
    +

    overview

    The interface of libtorrent consists of a few classes. The main class is the session, it contains the main loop that serves all torrents.

    The basic usage is as follows:

    @@ -199,8 +201,8 @@ the session, it conta

    Each class and function is described in this manual.

    -
    -

    primitive network types

    +
    +

    primitive network types

    There are a few typedefs in the libtorrent namespace which pulls in network types from the asio namespace. These are:

    @@ -219,8 +221,8 @@ udp::endpoint
     

    Which are the endpoint types used in libtorrent. An endpoint is an address with an associated port.

    -
    -

    session

    +
    +

    session

    The session class has the following synopsis:

     class session: public boost::noncopyable
    @@ -257,8 +259,8 @@ class session: public boost::noncopyable
             torrent_handle find_torrent(sha_hash const& ih);
             std::vector<torrent_handle> get_torrents() const;
     
    -        void set_settings(
    -                session_settings const& settings);
    +        void set_settings(session_settings const& settings);
    +        void set_pe_settings(pe_settings const& settings);
     
             void set_upload_rate_limit(int bytes_per_second);
             int upload_rate_limit() const;
    @@ -308,8 +310,8 @@ class session: public boost::noncopyable
     

    Once it's created, the session object will spawn the main thread that will do all the work. The main thread will be idle as long it doesn't have any torrents to participate in.

    -
    -

    session()

    +
    +

    session()

     session(fingerprint const& print
    @@ -328,16 +330,16 @@ The other constructor, that takes a port range and an interface as well as the f
     will automatically try to listen on a port on the given interface. For more information about
     the parameters, see listen_on() function.

    -
    -

    ~session()

    +
    +

    ~session()

    The destructor of session will notify all trackers that our torrents have been shut down. If some trackers are down, they will time out. All this before the destructor of session returns. So, it's advised that any kind of interface (such as windows) are closed before destructing the session object. Because it can take a few second for it to finish. The timeout can be set with set_settings().

    -
    -

    abort()

    +
    +

    abort()

     session_proxy abort();
     
    @@ -359,8 +361,8 @@ public: };
    -
    -

    add_torrent()

    +
    +

    add_torrent()

     torrent_handle add_torrent(
    @@ -405,13 +407,13 @@ about the torrent's progress, its peers etc. It is also used to abort a torrent.
     

    The second overload that takes a tracker url and an info-hash instead of metadata (torrent_info) can be used with torrents where (at least some) peers support the metadata extension. For the overload to be available, libtorrent must be built -with extensions enabled (TORRENT_ENABLE_EXTENSIONS defined). It also takes an -optional name argument. This may be 0 in case no name should be assigned to the -torrent. In case it's not 0, the name is used for the torrent as long as it doesn't +with extensions enabled (TORRENT_DISABLE_EXTENSIONS must not be defined). It also +takes an optional name argument. This may be 0 in case no name should be assigned +to the torrent. In case it's not 0, the name is used for the torrent as long as it doesn't have metadata. See torrent_handle::name.

    -
    -

    remove_torrent() find_torrent() get_torrents()

    +
    +

    remove_torrent() find_torrent() get_torrents()

     void remove_torrent(torrent_handle const& h);
    @@ -428,8 +430,8 @@ In case the torrent cannot be found, an invalid torrent_handle is returned.

    get_torrents() returns a vector of torrent_handles to all the torrents currently in the session.

    -
    -

    set_upload_rate_limit() set_download_rate_limit() upload_rate_limit() download_rate_limit()

    +
    +

    set_upload_rate_limit() set_download_rate_limit() upload_rate_limit() download_rate_limit()

     void set_upload_rate_limit(int bytes_per_second);
    @@ -446,8 +448,8 @@ of upload rate.
     download_rate_limit() and upload_rate_limit() returns the previously
     set limits.

    -
    -

    set_max_uploads() set_max_connections()

    +
    +

    set_max_uploads() set_max_connections()

     void set_max_uploads(int limit);
    @@ -460,8 +462,8 @@ minimum of at least two connections per torrent, so if you set a too low
     connections limit, and open too many torrents, the limit will not be met. The
     number of uploads is at least one per torrent.

    -
    -

    num_uploads() num_connections()

    +
    +

    num_uploads() num_connections()

     int num_uploads() const;
    @@ -471,8 +473,8 @@ int num_connections() const;
     

    Returns the number of currently unchoked peers and the number of connections (including half-open ones) respectively.

    -
    -

    set_max_half_open_connections()

    +
    +

    set_max_half_open_connections()

     void set_max_half_open_connections(int limit);
    @@ -488,8 +490,8 @@ and passing -1 as the limit, means to have no limit. When limiting the number
     of simultaneous connection attempts, peers will be put in a queue waiting for
     their turn to get connected.

    -
    -

    set_ip_filter()

    +
    +

    set_ip_filter()

     void set_ip_filter(ip_filter const& filter);
    @@ -502,8 +504,8 @@ accepted and not, see ip_filter.

    Each time a peer is blocked because of the IP filter, a peer_blocked_alert is generated.

    -
    -

    status()

    +
    +

    status()

     session_status status() const;
    @@ -533,7 +535,6 @@ struct session_status
             int dht_nodes;
             int dht_cache_nodes;
             int dht_torrents;
    -        int dht_global_nodes;
     };
     

    has_incoming_connections is false as long as no incoming connections have been @@ -554,11 +555,9 @@ table. This number only includes active nodes, not cache nodes. The are used to replace the regular nodes in the routing table in case any of them becomes unresponsive.

    dht_torrents are the number of torrents tracked by the DHT at the moment.

    -

    dht_global_nodes is an estimation of the total number of nodes in the DHT -network.

    -
    -

    is_listening() listen_port() listen_on()

    +
    +

    is_listening() listen_port() listen_on()

     bool is_listening() const;
    @@ -601,8 +600,8 @@ with a DHT ping packet, and connect to those that responds first. On windows one
     can only connect to a few peers at a time because of a built in limitation (in XP
     Service pack 2).

    -
    -

    pop_alert() set_severity_level()

    +
    +

    pop_alert() set_severity_level()

     std::auto_ptr<alert> pop_alert();
    @@ -613,8 +612,8 @@ void set_severity_level(alert::severity_t s);
     set_severity_level() you can filter how serious the event has to be for you to
     receive it through pop_alert(). For information, see alerts.

    -
    -

    add_extension()

    +
    +

    add_extension()

     void add_extension(boost::function<
    @@ -643,8 +642,20 @@ ses.add_extension(&libtorrent::create_metadata_plugin);
     ses.add_extension(&libtorrent::create_ut_pex_plugin);
     
    -
    -

    set_peer_proxy() set_web_seed_proxy() set_tracker_proxy() set_dht_proxy()

    +
    +

    set_settings() set_pe_settings()

    +
    +
    +void set_settings(session_settings const& settings);
    +void set_pe_settings(pe_settings const& settings);
    +
    +
    +

    Sets the session settings and the packet encryption settings respectively. +See session_settings and pe_settings for more information on available +options.

    +
    +
    +

    set_peer_proxy() set_web_seed_proxy() set_tracker_proxy() set_dht_proxy()

     void set_peer_proxy(proxy_settings const& s);
    @@ -665,8 +676,8 @@ it only has any effect if the proxy supports UDP.

    For more information on what settings are available for proxies, see proxy_settings.

    -
    -

    peer_proxy() web_seed_proxy() tracker_proxy() dht_proxy()

    +
    +

    peer_proxy() web_seed_proxy() tracker_proxy() dht_proxy()

     proxy_settings const& peer_proxy() const;
    @@ -678,8 +689,8 @@ proxy_settings const& dht_proxy() const;
     

    These functions returns references to their respective current settings.

    The dht_proxy is not available when DHT is disabled.

    -
    -

    start_dht() stop_dht() set_dht_settings() dht_state()

    +
    +

    start_dht() stop_dht() set_dht_settings() dht_state()

     void start_dht(entry const& startup_state);
    @@ -739,8 +750,8 @@ 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.

    -
    -

    add_dht_node() add_dht_router()

    +
    +

    add_dht_node() add_dht_router()

     void add_dht_node(std::pair<std::string, int> const& node);
    @@ -759,8 +770,8 @@ for bootstrapping, to keep the load off them.

    router.bittorrent.com.

    -
    -

    entry

    +
    +

    entry

    The entry class represents one node in a bencoded hierarchy. It works as a variant type, it can be either a list, a dictionary (std::map), an integer or a string. This is its synopsis:

    @@ -823,8 +834,8 @@ public: };

    TODO: finish documentation of entry.

    -
    -

    integer() string() list() dict() type()

    +
    +

    integer() string() list() dict() type()

     integer_type& integer();
    @@ -876,8 +887,8 @@ if (entry* i = torrent_file.find_key("announce"))
     

    To make it easier to extract information from a torrent file, the class torrent_info exists.

    -
    -

    operator[]

    +
    +

    operator[]

     entry& operator[](char const* key);
    @@ -895,8 +906,8 @@ given key, a reference to a newly inserted element at that key.

    existing element at the given key. If the key is not found, it will throw libtorrent::type_error.

    -
    -

    find_key()

    +
    +

    find_key()

     entry* find_key(char const* key);
    @@ -910,8 +921,8 @@ element cannot be found, they will return 0. If an element with the given
     key is found, the return a pointer to it.

    -
    -

    torrent_info

    +
    +

    torrent_info

    The torrent_info has the following synopsis:

     class torrent_info
    @@ -975,8 +986,8 @@ public:
             sha1_hash const& hash_for_piece(unsigned int index) const;
     };
     
    -
    -

    torrent_info()

    +
    +

    torrent_info()

     torrent_info();
    @@ -999,8 +1010,8 @@ object from the information found in the given torrent_file. The entry, use bdecode(),
     see bdecode() bencode().

    -
    -

    set_comment() set_piece_size() set_creator() set_hash() add_tracker() add_file()

    +
    +

    set_comment() set_piece_size() set_creator() set_hash() add_tracker() add_file()

     void set_comment(char const* str);
    @@ -1032,8 +1043,8 @@ of the torrent. The sizecreate_torrent().

    For a complete example of how to create a torrent from a file structure, see make_torrent.

    -
    -

    create_torrent()

    +
    +

    create_torrent()

     entry create_torrent();
    @@ -1047,8 +1058,8 @@ object.

    Note that a torrent file must include at least one file, and it must have at least one tracker url or at least one DHT node.

    -
    -

    begin_files() end_files() rbegin_files() rend_files()

    +
    +

    begin_files() end_files() rbegin_files() rend_files()

     file_iterator begin_files() const;
    @@ -1083,8 +1094,8 @@ struct file_entry
     };
     
    -
    -

    num_files() file_at()

    +
    +

    num_files() file_at()

     int num_files() const;
    @@ -1094,8 +1105,8 @@ file_entry const& file_at(int index) const;
     

    If you need index-access to files you can use the num_files() and file_at() to access files using indices.

    -
    -

    map_block()

    +
    +

    map_block()

     std::vector<file_slice> map_block(int piece, size_type offset
    @@ -1120,8 +1131,8 @@ as argument. The offsetsize is the number of bytes this range is. The size + offset
     will never be greater than the file size.

    -
    -

    map_file()

    +
    +

    map_file()

     peer_request map_file(int file_index, size_type file_offset
    @@ -1148,8 +1159,8 @@ struct peer_request
     + size is not allowed to be greater than the file size. file_index
     must refer to a valid file, i.e. it cannot be >= num_files().

    -
    -

    url_seeds()

    +
    +

    url_seeds()

     std::vector<std::string> const& url_seeds() const;
    @@ -1162,8 +1173,8 @@ adds one url to the list of url-seeds. Currently, the only transport protocol
     supported for the url is http.

    See HTTP seeding for more information.

    -
    -

    print()

    +
    +

    print()

     void print(std::ostream& os) const;
    @@ -1173,8 +1184,8 @@ void print(std::ostream& os) const;
     the torrent file to the given outstream. This function has been deprecated and will
     be removed from future releases.

    -
    -

    trackers()

    +
    +

    trackers()

     std::vector<announce_entry> const& trackers() const;
    @@ -1193,8 +1204,8 @@ struct announce_entry
     };
     
    -
    -

    total_size() piece_length() piece_size() num_pieces()

    +
    +

    total_size() piece_length() piece_size() num_pieces()

     size_type total_size() const;
    @@ -1211,8 +1222,8 @@ the piece index as argument and gives you the exact size of that piece. It will
     be the same as piece_length() except in the case of the last piece, which may
     be smaller.

    -
    -

    hash_for_piece() info_hash()

    +
    +

    hash_for_piece() info_hash()

     size_type piece_size(unsigned int index) const;
    @@ -1225,8 +1236,8 @@ torrent file. For more information on the info_hash() will only return a valid hash if the torrent_info was read from a
     .torrent file or if an entry was created from it (through create_torrent).

    -
    -

    name() comment() creation_date() creator()

    +
    +

    name() comment() creation_date() creator()

     std::string const& name() const;
    @@ -1243,8 +1254,8 @@ in the torrent file, this will return a date of January 1:st 1970.

    creator() returns the creator string in the torrent. If there is no creator string it will return an empty string.

    -
    -

    priv() set_priv()

    +
    +

    priv() set_priv()

     bool priv() const;
    @@ -1255,8 +1266,8 @@ void set_priv(bool v);
     distributed on the trackerless network (the kademlia DHT).

    set_priv() sets or clears the private flag on this torrent.

    -
    -

    nodes()

    +
    +

    nodes()

     std::vector<std::pair<std::string, int> > const& nodes() const;
    @@ -1265,8 +1276,8 @@ std::vector<std::pair<std::string, int> > const& nodes() const;
     

    If this torrent contains any DHT nodes, they are put in this vector in their original form (host name and port number).

    -
    -

    add_node()

    +
    +

    add_node()

     void add_node(std::pair<std::string, int> const& node);
    @@ -1276,8 +1287,8 @@ void add_node(std::pair<std::string, int> const& node);
     be used, by the client, to bootstrap into the DHT network.

    -
    -

    torrent_handle

    +
    +

    torrent_handle

    You will usually have to store your torrent handles somewhere, since it's the object through which you retrieve information about the torrent and aborts the torrent. Its declaration looks like this:

    @@ -1368,8 +1379,8 @@ two exceptions, info_hash()
    -
    -

    piece_priority() prioritize_pieces() piece_priorities() prioritize_files()

    +
    +

    piece_priority() prioritize_pieces() piece_priorities() prioritize_files()

     void piece_priority(int index, int priority) const;
    @@ -1413,8 +1424,8 @@ torrent. Each element is the current priority of that piece.

    files in the torrent. Each entry is the priority of that file. The function sets the priorities of all the pieces in the torrent based on the vector.

    -
    -

    file_progress()

    +
    +

    file_progress()

     void file_progress(std::vector<float>& fp);
    @@ -1425,8 +1436,8 @@ range [0, 1]) describing the download progress of each file in this torrent.
     The progress values are ordered the same as the files in the torrent_info.
     This operation is not very cheap.

    -
    -

    save_path()

    +
    +

    save_path()

     boost::filesystem::path save_path() const;
    @@ -1435,8 +1446,8 @@ boost::filesystem::path save_path() const;
     

    save_path() returns the path that was given to add_torrent() when this torrent was started.

    -
    -

    move_storage()

    +
    +

    move_storage()

     bool move_storage(boost::filesystem::path const& save_path) const;
    @@ -1448,8 +1459,8 @@ the same drive as the original save path. If the move operation fails, this func
     returns false, otherwise true. Post condition for successful operation is:
     save_path() == save_path.

    -
    -

    force_reannounce()

    +
    +

    force_reannounce()

     void force_reannounce() const;
    @@ -1459,8 +1470,8 @@ void force_reannounce() const;
     peers. If the torrent is invalid, queued or in checking mode, this functions will throw
     invalid_handle.

    -
    -

    connect_peer()

    +
    +

    connect_peer()

     void connect_peer(asio::ip::tcp::endpoint const& adr, int source = 0) const;
    @@ -1474,8 +1485,8 @@ will throw invalid_handle. The s
     the source mask of this peer. Typically this is one of the source flags in peer_info.
     i.e. tracker, pex, dht etc.

    -
    -

    name()

    +
    +

    name()

     std::string name() const;
    @@ -1485,8 +1496,8 @@ std::string name() const;
     case the torrent was started without metadata, and hasn't completely received it yet,
     it returns the name given to it when added to the session. See session::add_torrent.

    -
    -

    set_ratio()

    +
    +

    set_ratio()

     void set_ratio(float ratio) const;
    @@ -1500,8 +1511,8 @@ attempt to upload in return for each download. e.g. if set to 2, the client will
     2 bytes for every byte received. The default setting for this is 0, which will make it work
     as a standard client.

    -
    -

    set_upload_limit() set_download_limit() upload_limit() download_limit()

    +
    +

    set_upload_limit() set_download_limit() upload_limit() download_limit()

     void set_upload_limit(int limit) const;
    @@ -1519,8 +1530,8 @@ limit.

    upload_limit and download_limit will return the current limit setting, for upload and download, respectively.

    -
    -

    set_sequenced_download_threshold()

    +
    +

    set_sequenced_download_threshold()

     void set_sequenced_download_threshold(int threshold);
    @@ -1537,8 +1548,8 @@ in practice.

    negatively in the swarm. It should basically only be used in situations where the random seeks on the disk is the download bottleneck.

    -
    -

    set_peer_upload_limit() set_peer_download_limit()

    +
    +

    set_peer_upload_limit() set_peer_download_limit()

     void set_peer_upload_limit(asio::ip::tcp::endpoint ip, int limit) const;
    @@ -1548,8 +1559,8 @@ void set_peer_download_limit(asio::ip::tcp::endpoint ip, int limit) const;
     

    Works like set_upload_limit and set_download_limit respectively, but controls individual peer instead of the whole torrent.

    -
    -

    pause() resume() is_paused()

    +
    +

    pause() resume() is_paused()

     void pause() const;
    @@ -1563,8 +1574,8 @@ all potential (not connected) peers. You can use file_error_alert.

    -
    -

    resolve_countries()

    +
    +

    resolve_countries()

     void resolve_countries(bool r);
    @@ -1576,8 +1587,8 @@ torrent. It defaults to false. If it is set to true, the country member set. See peer_info for more information
     on how to interpret this field.

    -
    -

    is_seed()

    +
    +

    is_seed()

     bool is_seed() const;
    @@ -1585,8 +1596,8 @@ bool is_seed() const;
     

    Returns true if the torrent is in seed mode (i.e. if it has finished downloading).

    -
    -

    has_metadata()

    +
    +

    has_metadata()

     bool has_metadata() const;
    @@ -1597,8 +1608,8 @@ metadata has been downloaded). The only scenario where this can return false is
     was started torrent-less (i.e. with just an info-hash and tracker ip). Note that if the torrent
     doesn't have metadata, the member get_torrent_info() will throw.

    -
    -

    set_tracker_login()

    +
    +

    set_tracker_login()

     void set_tracker_login(std::string const& username
    @@ -1608,8 +1619,8 @@ void set_tracker_login(std::string const& username
     

    set_tracker_login() sets a username and password that will be sent along in the HTTP-request of the tracker announce. Set this if the tracker requires authorization.

    -
    -

    trackers() replace_trackers()

    +
    +

    trackers() replace_trackers()

     std::vector<announce_entry> const& trackers() const;
    @@ -1625,8 +1636,8 @@ a list of the same form as the one returned from force_reannounce().

    -
    -

    add_url_seed()

    +
    +

    add_url_seed()

     void add_url_seed(std::string const& url);
    @@ -1638,8 +1649,8 @@ will connect to the server and try to download pieces from it, unless it's
     paused, queued, checking or seeding.

    See HTTP seeding for more information.

    -
    -

    use_interface()

    +
    +

    use_interface()

     void use_interface(char const* net_interface) const;
    @@ -1650,8 +1661,8 @@ connections. By default, it uses the same interface as the 
     
    -
    -

    info_hash()

    +
    +

    info_hash()

     sha1_hash info_hash() const;
    @@ -1659,8 +1670,8 @@ sha1_hash info_hash() const;
     

    info_hash() returns the info-hash for the torrent.

    -
    -

    set_max_uploads() set_max_connections()

    +
    +

    set_max_uploads() set_max_connections()

     void set_max_uploads(int max_uploads) const;
    @@ -1674,8 +1685,8 @@ connections are used up, incoming connections may be refused or poor connections
     This must be at least 2. The default is unlimited number of connections. If -1 is given to the
     function, it means unlimited.

    -
    -

    write_resume_data()

    +
    +

    write_resume_data()

     entry write_resume_data() const;
    @@ -1700,8 +1711,8 @@ fast resume data, and then close it down.

    closing down. In full allocation mode the reume data is never invalidated by subsequent writes to the files, since pieces won't move around.

    -
    -

    status()

    +
    +

    status()

     torrent_status status() const;
    @@ -1711,8 +1722,8 @@ torrent_status status() const;
     torrent. If the torrent_handle is invalid, it will throw invalid_handle exception.
     See torrent_status.

    -
    -

    get_download_queue()

    +
    +

    get_download_queue()

     void get_download_queue(std::vector<partial_piece_info>& queue) const;
    @@ -1756,8 +1767,8 @@ the same category as themselves. The reason for this is to keep the number of pa
     downloaded pieces down. Pieces set to none can be converted into any of fast,
     medium or slow as soon as a peer want to download from it.

    -
    -

    get_peer_info()

    +
    +

    get_peer_info()

     void get_peer_info(std::vector<peer_info>&) const;
    @@ -1768,8 +1779,8 @@ with one entry for each peer connected to this torrent, given the handle is vali
     torrent_handle is invalid, it will throw invalid_handle exception. Each entry in
     the vector contains information about that particular peer. See peer_info.

    -
    -

    get_torrent_info()

    +
    +

    get_torrent_info()

     torrent_info const& get_torrent_info() const;
    @@ -1782,8 +1793,8 @@ exception will be thrown. The torrent may be in a state without metadata only if
     it was started without a .torrent file, i.e. by using the libtorrent extension of
     just supplying a tracker and info-hash.

    -
    -

    is_valid()

    +
    +

    is_valid()

     bool is_valid() const;
    @@ -1798,8 +1809,8 @@ that refers to that torrent will become invalid.

    TODO: document storage

    -
    -

    torrent_status

    +
    +

    torrent_status

    It contains the following fields:

     struct torrent_status
    @@ -1972,8 +1983,8 @@ bytes that each bit in the partia
     (see get_download_queue()). This is typically 16 kB, but it may be
     larger if the pieces are larger.

    -
    -

    peer_info

    +
    +

    peer_info

    It contains the following fields:

     struct peer_info
    @@ -2164,8 +2175,8 @@ string.

    connection_type can currently be one of standard_bittorrent or web_seed. These are currently the only implemented protocols.

    -
    -

    session_settings

    +
    +

    session_settings

    You have some control over tracker requests through the session_settings object. You create it and fill it with your settings and then use session::set_settings() to apply them. You have control over proxy and authorization settings and also the user-agent @@ -2191,7 +2202,6 @@ struct session_settings bool allow_multiple_connections_per_ip; int max_failcount; int min_reconnect_time; - int peer_connect_timeout; bool use_dht_as_fallback; }; @@ -2266,18 +2276,70 @@ a peer is retrieved from a peer source (other than DHT) the failcount is decremented by one, allowing another try.

    min_reconnect_time is the time to wait between connection attempts. If the peer fails, the time is multiplied by fail counter.

    -

    peer_connect_timeout the number of seconds to wait after a connection -attempt is initiated to a peer until it is considered as having timed out. -The default is 10 seconds. This setting is especially important in case -the number of half-open connections are limited, since stale half-open -connection may delay the connection of other peers considerably.

    use_dht_as_fallback determines how the DHT is used. If this is true (which it is by default), the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error message or a time out.

    -
    -

    proxy_settings

    +
    +

    pe_settings

    +

    The pe_settings structure is used to control the settings related +to peer protocol encryption:

    +
    +struct pe_settings
    +{
    +        pe_settings();
    +
    +        enum enc_policy
    +        {
    +                forced,
    +                enabled,
    +                disabled
    +        };
    +
    +        enum enc_level
    +        {
    +                plaintext,
    +                rc4,
    +                both
    +        };
    +
    +        enc_policy out_enc_policy;
    +        enc_policy in_enc_policy;
    +        enc_level allowed_enc_level;
    +        bool prefer_rc4;
    +};
    +
    +

    in_enc_policy and out_enc_policy control the settings for incoming +and outgoing connections respectively. The settings for these are:

    +
    +
      +
    • forced - Only encrypted connections are allowed. Incoming connections +that are not encrypted are closed and if the encrypted outgoing connection +fails, a non-encrypted retry will not be made.
    • +
    • enabled - encrypted connections are enabled, but non-encrypted +connections are allowed. An incoming non-encrypted connection will +be accepted, and if an outgoing encrypted connection fails, a non- +encrypted connection will be tried.
    • +
    • disabled - only non-encrypted connections are allowed.
    • +
    +
    +

    allowed_enc_level determines the encryption level of the +connections. This setting will adjust which encryption scheme is +offered to the other peer, as well as which encryption scheme is +selected by the client. The settings are:

    +
    +
      +
    • plaintext - only the handshake is encrypted, the bulk of the traffic +remains unchanged.
    • +
    • rc4 - the entire stream is encrypted with RC4
    • +
    • both - both RC4 and plaintext connections are allowed.
    • +
    +
    +

    prefer_rc4 can be set to true if you want to prefer the RC4 encrypted stream.

    +
    +
    +

    proxy_settings

    The proxy_settings structs contains the information needed to direct certain traffic to a proxy.

    @@ -2329,8 +2391,8 @@ user authorization. The username and password will be sent to the proxy.
    -
    -

    ip_filter

    +
    +

    ip_filter

    The ip_filter class is a set of rules that uniquely categorizes all ip addresses as allowed or disallowed. The default constructor creates a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for @@ -2362,8 +2424,8 @@ public: }; -

    -

    ip_filter()

    +
    +

    ip_filter()

     ip_filter()
    @@ -2373,8 +2435,8 @@ ip_filter()
     

    postcondition: access(x) == 0 for every x

    -
    -

    add_rule()

    +
    +

    add_rule()

     void add_rule(address first, address last, int flags);
    @@ -2391,8 +2453,8 @@ means disallowed.

    This means that in a case of overlapping ranges, the last one applied takes precedence.

    -
    -

    access()

    +
    +

    access()

     int access(address const& addr) const;
    @@ -2403,8 +2465,8 @@ can currently be 0 or ip_filter::
     is O(log n), where n is the minimum number of non-overlapping ranges to describe
     the current filter.

    -
    -

    export_filter()

    +
    +

    export_filter()

     boost::tuple<std::vector<ip_range<address_v4> >
    @@ -2419,8 +2481,8 @@ entry in the returned vector is a range with the access control specified in its
     and one for IPv6 addresses.

    -
    -

    big_number

    +
    +

    big_number

    Both the peer_id and sha1_hash types are typedefs of the class big_number. It represents 20 bytes of data. Its synopsis follows:

    @@ -2440,8 +2502,8 @@ public:
     

    The iterators gives you access to individual bytes.

    -
    -

    hasher

    +
    +

    hasher

    This class creates sha1-hashes. Its declaration looks like this:

     class hasher
    @@ -2467,8 +2529,8 @@ call reset() to reini
     

    The sha1-algorithm used was implemented by Steve Reid and released as public domain. For more info, see src/sha1.cpp.

    -
    -

    fingerprint

    +
    +

    fingerprint

    The fingerprint class represents information about a client and its version. It is used to encode this information into the client's peer id.

    This is the class declaration:

    @@ -2530,10 +2592,10 @@ sure not to clash with anybody else. Here are some taken id's:

    version of your client. All these numbers must be within the range [0, 9].

    to_string() will generate the actual string put in the peer-id, and return it.

    -
    -

    free functions

    -
    -

    identify_client()

    +
    +

    free functions

    +
    +

    identify_client()

     std::string identify_client(peer_id const& id);
    @@ -2543,8 +2605,8 @@ std::string identify_client(peer_id const& id);
     to extract a string describing a client version from its peer-id. It will recognize most clients
     that have this kind of identification in the peer-id.

    -
    -

    client_fingerprint()

    +
    +

    client_fingerprint()

     boost::optional<fingerprint> client_fingerprint(peer_id const& p);
    @@ -2555,8 +2617,8 @@ to automate the identification of clients. It will not be able to identify peers
     standard encodings. Only Azureus style, Shadow's style and Mainline style. This function is
     declared in the header <libtorrent/identify_client.hpp>.

    -
    -

    bdecode() bencode()

    +
    +

    bdecode() bencode()

     template<class InIt> entry bdecode(InIt start, InIt end);
    @@ -2595,8 +2657,8 @@ entry e = bdecode(buf, buf + data_size);
     

    If bdecode() encounters invalid encoded data in the range given to it it will throw invalid_encoding.

    -
    -

    supports_sparse_files()

    +
    +

    supports_sparse_files()

     bool supports_sparse_files(boost::filesystem::path const&);
    @@ -2610,8 +2672,8 @@ of sparse files, but rather the support for seeking passed end of file and
     write data there, with expected behavior.

    -
    -

    alerts

    +
    +

    alerts

    The pop_alert() function on session is the interface for retrieving alerts, warnings, messages and errors from libtorrent. If there hasn't occurred any errors (matching your severity level) pop_alert() will @@ -2702,8 +2764,8 @@ struct torrent_alert: alert };

    The specific alerts, that all derives from alert, are:

    -
    -

    listen_failed_alert

    +
    +

    listen_failed_alert

    This alert is generated when none of the ports, given in the port range, to session can be opened for listening. This alert is generated as severity level fatal.

    @@ -2715,8 +2777,8 @@ struct listen_failed_alert: alert };
    -
    -

    portmap_error_alert

    +
    +

    portmap_error_alert

    This alert is generated when a NAT router was successfully found but some part of the port mapping request failed. It contains a text message that may help the user figure out what is wrong. This alert is not generated in @@ -2733,8 +2795,8 @@ struct portmap_error_alert: alert };

    -
    -

    portmap_alert

    +
    +

    portmap_alert

    This alert is generated when a NAT router was successfully found and a port was successfully mapped on it. On a NAT:ed network with a NAT-PMP capable router, this is typically generated once when mapping the TCP @@ -2748,8 +2810,8 @@ struct portmap_alert: alert };

    -
    -

    file_error_alert

    +
    +

    file_error_alert

    If the storage fails to read or write files that it needs access to, this alert is generated and the torrent is paused. It is generated as severity level fatal.

    @@ -2763,8 +2825,8 @@ struct file_error_alert: torrent_alert
     };
     
    -
    -

    tracker_announce_alert

    +
    +

    tracker_announce_alert

    This alert is generated each time a tracker announce is sent (or attempted to be sent). It is generated at severity level info.

    @@ -2778,8 +2840,8 @@ struct tracker_announce_alert: torrent_alert
     };
     
    -
    -

    tracker_alert

    +
    +

    tracker_alert

    This alert is generated on tracker time outs, premature disconnects, invalid response or a HTTP response other than "200 OK". From the alert you can get the handle to the torrent the tracker belongs to. This alert is generated as severity level warning.

    @@ -2799,8 +2861,8 @@ struct tracker_alert: torrent_alert };
    -
    -

    tracker_reply_alert

    +
    +

    tracker_reply_alert

    This alert is only for informational purpose. It is generated when a tracker announce succeeds. It is generated regardless what kind of tracker was used, be it UDP, HTTP or the DHT. It is generated with severity level info.

    @@ -2819,8 +2881,8 @@ struct tracker_reply_alert: torrent_alert

    The num_peers tells how many peers were returned from the tracker. This is not necessarily all new peers, some of them may already be connected.

    -
    -

    tracker_warning_alert

    +
    +

    tracker_warning_alert

    This alert is triggered if the tracker reply contains a warning field. Usually this means that the tracker announce was successful, but the tracker has a message to the client. The message string in the alert will contain the warning message from @@ -2835,8 +2897,8 @@ struct tracker_warning_alert: torrent_alert };

    -
    -

    url_seed_alert

    +
    +

    url_seed_alert

    This alert is generated when a HTTP seed name lookup fails. This alert is generated as severity level warning.

    It contains url to the HTTP seed that failed along with an error message.

    @@ -2851,8 +2913,8 @@ struct url_seed_alert: torrent_alert };
    -
    -

    hash_failed_alert

    +
    +

    hash_failed_alert

    This alert is generated when a finished piece fails its hash check. You can get the handle to the torrent which got the failed piece and the index of the piece itself from the alert. This alert is generated as severity level info.

    @@ -2870,8 +2932,8 @@ struct hash_failed_alert: torrent_alert };
    -
    -

    peer_ban_alert

    +
    +

    peer_ban_alert

    This alert is generated when a peer is banned because it has sent too many corrupt pieces to us. It is generated at severity level info. The handle member is a torrent_handle to the torrent that this peer was a member of.

    @@ -2889,8 +2951,8 @@ struct peer_ban_alert: torrent_alert };
    -
    -

    peer_error_alert

    +
    +

    peer_error_alert

    This alert is generated when a peer sends invalid data over the peer-peer protocol. The peer will be disconnected, but you get its ip address from the alert, to identify it. This alert is generated as severity level debug.

    @@ -2908,8 +2970,8 @@ struct peer_error_alert: alert };
    -
    -

    invalid_request_alert

    +
    +

    invalid_request_alert

    This is a debug alert that is generated by an incoming invalid piece request. The handle is a handle to the torrent the peer is a member of. Ïp is the address of the peer and the request is the actual incoming request from the peer. The alert is generated as severity level @@ -2944,8 +3006,8 @@ struct peer_request the index of the piece it want data from, start is the offset within the piece where the data should be read, and length is the amount of data it wants.

    -
    -

    torrent_finished_alert

    +
    +

    torrent_finished_alert

    This alert is generated when a torrent switches from being a downloader to a seed. It will only be generated once per torrent. It contains a torrent_handle to the torrent in question. This alert is generated as severity level info.

    @@ -2960,8 +3022,8 @@ struct torrent_finished_alert: torrent_alert };
    -
    -

    metadata_failed_alert

    +
    +

    metadata_failed_alert

    This alert is generated when the metadata has been completely received and the info-hash failed to match it. i.e. the metadata that was received was corrupt. libtorrent will automatically retry to fetch it in this case. This is only relevant when running a @@ -2978,8 +3040,8 @@ struct metadata_failed_alert: torrent_alert };

    -
    -

    metadata_received_alert

    +
    +

    metadata_received_alert

    This alert is generated when the metadata has been completely received and the torrent can start downloading. It is not generated on torrents that are started with metadata, but only those that needs to download it from peers (when utilizing the libtorrent extension). @@ -2995,8 +3057,8 @@ struct metadata_received_alert: torrent_alert };

    -
    -

    fastresume_rejected_alert

    +
    +

    fastresume_rejected_alert

    This alert is generated when a fastresume file has been passed to add_torrent but the files on disk did not match the fastresume file. The string explains the reason why the resume file was rejected. It is generated at severity level warning.

    @@ -3010,8 +3072,8 @@ struct fastresume_rejected_alert: torrent_alert };
    -
    -

    peer_blocked_alert

    +
    +

    peer_blocked_alert

    This alert is generated when a peer is blocked by the IP filter. It has the severity leve info. The ip member is the address that was blocked.

    @@ -3026,8 +3088,8 @@ struct peer_blocked_alert: alert
     };
     
    -
    -

    dispatcher

    +
    +

    dispatcher

    The handle_alert class is defined in <libtorrent/alert.hpp>.

    Examples usage:

    @@ -3072,12 +3134,12 @@ parameters to select between more types. If the number of types are more than
     including <libtorrent/alert.hpp>.

    -
    -

    exceptions

    +
    +

    exceptions

    There are a number of exceptions that can be thrown from different places in libtorrent, here's a complete list with description.

    -
    -

    invalid_handle

    +
    +

    invalid_handle

    This exception is thrown when querying information from a torrent_handle that hasn't been initialized or that has become invalid.

    @@ -3087,8 +3149,8 @@ struct invalid_handle: std::exception
     };
     
    -
    -

    duplicate_torrent

    +
    +

    duplicate_torrent

    This is thrown by add_torrent() if the torrent already has been added to the session.

    @@ -3098,8 +3160,8 @@ struct duplicate_torrent: std::exception
     };
     
    -
    -

    invalid_encoding

    +
    +

    invalid_encoding

    This is thrown by bdecode() if the input data is not a valid bencoding.

     struct invalid_encoding: std::exception
    @@ -3108,8 +3170,8 @@ struct invalid_encoding: std::exception
     };
     
    -
    -

    type_error

    +
    +

    type_error

    This is thrown from the accessors of entry if the data type of the entry doesn't match the type you want to extract from it.

    @@ -3119,8 +3181,8 @@ struct type_error: std::runtime_error
     };
     
    -
    -

    invalid_torrent_file

    +
    +

    invalid_torrent_file

    This exception is thrown from the constructor of torrent_info if the given bencoded information doesn't meet the requirements on what information has to be present in a torrent file.

    @@ -3131,8 +3193,8 @@ struct invalid_torrent_file: std::exception
     
    -
    -

    fast resume

    +
    +

    fast resume

    The fast resume mechanism is a way to remember which pieces are downloaded and where they are put between sessions. You can generate fast resume data by calling torrent_handle::write_resume_data() on torrent_handle. You can @@ -3145,8 +3207,8 @@ start from scratch on the partially downloaded pieces.

    will skip the time consuming checks. It may have to do the checking anyway, if the fast-resume data is corrupt or doesn't fit the storage for that torrent, then it will not trust the fast-resume data and just do the checking.

    -
    -

    file format

    +
    +

    file format

    The file format is a bencoded dictionary containing the following fields:

    @@ -3251,8 +3313,8 @@ last resume data checkpoint.
    -
    -

    threads

    +
    +

    threads

    libtorrent starts 2 or 3 threads.

      @@ -3270,8 +3332,8 @@ non-blocking host name resolution to simulate non-blocking behavior.
    -
    -

    storage allocation

    +
    +

    storage allocation

    There are two modes in which storage (files on disk) are allocated in libtorrent.

      @@ -3291,8 +3353,8 @@ different drawbacks and benefits.

      any files are filtered and if the filesystem supports sparse files.

      To know if the filesystem supports sparse files (and to know if libtorrent believes the filesystem supports sparse files), see supports_sparse_files().

      -
      -

      full allocation

      +
      +

      full allocation

      When a torrent is started in full allocation mode, the checker thread (see threads) will make sure that the entire storage is allocated, and fill any gaps with zeros. This will be skipped if the filesystem supports sparse files or automatic zero filling. @@ -3323,8 +3385,8 @@ out of date data, since pieces won't move around.

    -
    -

    compact allocation

    +
    +

    compact allocation

    The compact allocation will only allocate as much storage as it needs to keep the pieces downloaded so far. This means that pieces will be moved around to be placed at their final position in the files while downloading (to make sure the completed @@ -3372,8 +3434,8 @@ contain any piece), return that slot index.

    -
    -

    extensions

    +
    +

    extensions

    These extensions all operates within the extension protocol. The name of the extension is the name used in the extension-list packets, and the payload is the data in the extended message (not counting the @@ -3382,8 +3444,8 @@ length-prefix, message-id nor extension-id).

    handshake, it may be incompatible with future versions of the mainline bittorrent client.

    These are the extensions that are currently implemented.

    -
    -

    metadata from peers

    +
    +

    metadata from peers

    Extension name: "LT_metadata"

    The point with this extension is that you don't have to distribute the metadata (.torrent-file) separately. The metadata can be distributed @@ -3501,8 +3563,8 @@ doesn't have any metadata.

    -
    -

    HTTP seeding

    +
    +

    HTTP seeding

    The HTTP seed extension implements this specification.

    The libtorrent implementation assumes that, if the URL ends with a slash ('/'), the filename should be appended to it in order to request pieces from @@ -3512,8 +3574,8 @@ torrent's name '/' the file name is appended. This is the same directory structure that libtorrent will download torrents into.

    -
    -

    filename checks

    +
    +

    filename checks

    Boost.Filesystem will by default check all its paths to make sure they conform to filename requirements on many platforms. If you don't want this check, you can set it to either only check for native filesystem requirements or turn it off @@ -3523,8 +3585,8 @@ boost::filesystem::path::default_name_check(boost::filesystem::native);

    for example. For more information, see the Boost.Filesystem docs.

    -
    -

    acknowledgments

    +
    +

    acknowledgments

    Written by Arvid Norberg. Copyright © 2003-2006

    Contributions by Magnus Jonsson, Daniel Wallin and Cory Nelson

    Lots of testing, suggestions and contributions by Massaroddel and Tianhao Qiu.

    diff --git a/docs/manual.rst b/docs/manual.rst index 2bde8fbc7..db4a7218b 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -92,8 +92,8 @@ The ``session`` class has the following synopsis:: torrent_handle find_torrent(sha_hash const& ih); std::vector get_torrents() const; - void set_settings( - session_settings const& settings); + void set_settings(session_settings const& settings); + void set_pe_settings(pe_settings const& settings); void set_upload_rate_limit(int bytes_per_second); int upload_rate_limit() const; @@ -250,9 +250,9 @@ about the torrent's progress, its peers etc. It is also used to abort a torrent. The second overload that takes a tracker url and an info-hash instead of metadata (``torrent_info``) can be used with torrents where (at least some) peers support the metadata extension. For the overload to be available, libtorrent must be built -with extensions enabled (``TORRENT_ENABLE_EXTENSIONS`` defined). It also takes an -optional ``name`` argument. This may be 0 in case no name should be assigned to the -torrent. In case it's not 0, the name is used for the torrent as long as it doesn't +with extensions enabled (``TORRENT_DISABLE_EXTENSIONS`` must not be defined). It also +takes an optional ``name`` argument. This may be 0 in case no name should be assigned +to the torrent. In case it's not 0, the name is used for the torrent as long as it doesn't have metadata. See ``torrent_handle::name``. remove_torrent() find_torrent() get_torrents() @@ -515,6 +515,19 @@ e.g. .. _`libtorrent plugins`: libtorrent_plugins.html +set_settings() set_pe_settings() +-------------------------------- + + :: + + void set_settings(session_settings const& settings); + void set_pe_settings(pe_settings const& settings); + +Sets the session settings and the packet encryption settings respectively. +See session_settings_ and pe_settings_ for more information on available +options. + + set_peer_proxy() set_web_seed_proxy() set_tracker_proxy() set_dht_proxy() ------------------------------------------------------------------------- @@ -556,6 +569,7 @@ These functions returns references to their respective current settings. The ``dht_proxy`` is not available when DHT is disabled. + start_dht() stop_dht() set_dht_settings() dht_state() ----------------------------------------------------- @@ -2310,6 +2324,66 @@ Default is 10 minutes all trackers in its tracker list has failed. Either by an explicit error message or a time out. +pe_settings +=========== + +The ``pe_settings`` structure is used to control the settings related +to peer protocol encryption:: + + struct pe_settings + { + pe_settings(); + + enum enc_policy + { + forced, + enabled, + disabled + }; + + enum enc_level + { + plaintext, + rc4, + both + }; + + enc_policy out_enc_policy; + enc_policy in_enc_policy; + enc_level allowed_enc_level; + bool prefer_rc4; + }; + + +``in_enc_policy`` and ``out_enc_policy`` control the settings for incoming +and outgoing connections respectively. The settings for these are: + + * ``forced`` - Only encrypted connections are allowed. Incoming connections + that are not encrypted are closed and if the encrypted outgoing connection + fails, a non-encrypted retry will not be made. + + * ``enabled`` - encrypted connections are enabled, but non-encrypted + connections are allowed. An incoming non-encrypted connection will + be accepted, and if an outgoing encrypted connection fails, a non- + encrypted connection will be tried. + + * ``disabled`` - only non-encrypted connections are allowed. + +``allowed_enc_level`` determines the encryption level of the +connections. This setting will adjust which encryption scheme is +offered to the other peer, as well as which encryption scheme is +selected by the client. The settings are: + + * ``plaintext`` - only the handshake is encrypted, the bulk of the traffic + remains unchanged. + + * ``rc4`` - the entire stream is encrypted with RC4 + + * ``both`` - both RC4 and plaintext connections are allowed. + +``prefer_rc4`` can be set to true if you want to prefer the RC4 encrypted stream. + + proxy_settings ============== diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 9072eb3d2..9c5c06d97 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -297,7 +297,11 @@ void print_peer_info(std::ostream& out, std::vector const << ((i->flags & peer_info::local_connection)?'l':'r') << ((i->flags & peer_info::seed)?'s':'.') << ((i->flags & peer_info::on_parole)?'p':'.') - << " " +#ifndef TORRENT_DISABLE_ENCRYPTION + << ((i->flags & peer_info::rc4_encrypted)?'E': + (i->flags & peer_info::plaintext_encrypted)?'e':'.') +#endif + << " " << ((i->source & peer_info::tracker)?"T":"_") << ((i->source & peer_info::pex)?"P":"_") << ((i->source & peer_info::dht)?"D":"_") diff --git a/include/Makefile.am b/include/Makefile.am index 34095f020..c6ff1e919 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -27,6 +27,7 @@ libtorrent/peer.hpp \ libtorrent/peer_connection.hpp \ libtorrent/bt_peer_connection.hpp \ libtorrent/web_peer_connection.hpp \ +libtorrent/pe_crypto.hpp \ libtorrent/natpmp.hpp \ libtorrent/pch.hpp \ libtorrent/peer_id.hpp \ diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 589fcc1d6..4759d4a18 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -217,6 +217,11 @@ namespace libtorrent entry dht_state() const; #endif +#ifndef TORRENT_DISABLE_ENCRYPTION + void set_pe_settings(pe_settings const& settings); + pe_settings const& get_pe_settings() const { return m_pe_settings; } +#endif + // called when a port mapping is successful, or a router returns // a failure to map a port void on_port_mapping(int tcp_port, int udp_port, std::string const& errmsg); @@ -429,6 +434,11 @@ namespace libtorrent // but for the udp port used by the DHT. int m_external_udp_port; #endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + pe_settings m_pe_settings; +#endif + boost::shared_ptr m_natpmp; boost::shared_ptr m_upnp; boost::shared_ptr m_lsd; diff --git a/include/libtorrent/bt_peer_connection.hpp b/include/libtorrent/bt_peer_connection.hpp index bd2e9e88b..2a5000011 100755 --- a/include/libtorrent/bt_peer_connection.hpp +++ b/include/libtorrent/bt_peer_connection.hpp @@ -1,6 +1,7 @@ /* -Copyright (c) 2003, Arvid Norberg +Copyright (c) 2003 - 2006, Arvid Norberg +Copyright (c) 2007, Arvid Norberg, Un Shyam All rights reserved. Redistribution and use in source and binary forms, with or without @@ -68,6 +69,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_request.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" +#include "libtorrent/pe_crypto.hpp" namespace libtorrent { @@ -102,6 +104,11 @@ namespace libtorrent , policy::peer* peerinfo); ~bt_peer_connection(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + bool supports_encryption() const + { return m_encrypted; } +#endif enum message_type { @@ -213,19 +220,76 @@ namespace libtorrent // will be invalid. boost::optional downloading_piece_progress() const; +#ifndef TORRENT_DISABLE_ENCRYPTION + + // if (is_local()), we are 'a' otherwise 'b' + // + // 1. a -> b dhkey, pad + // 2. b -> a dhkey, pad + // 3. a -> b sync, payload + // 4. b -> a sync, payload + // 5. a -> b payload + + void write_pe1_2_dhkey(); + void write_pe3_sync(); + void write_pe4_sync(int crypto_select); + + void write_pe_vc_cryptofield(buffer::interval& write_buf, + int crypto_field, int pad_size); + + // stream key (info hash of attached torrent) + // secret is the DH shared secret + // initializes m_RC4_handler + void init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key); + + // these functions encrypt the send buffer if m_rc4_encrypted + // is true, otherwise it passes the call to the + // peer_connection functions of the same names + void send_buffer(char* begin, char* end); + buffer::interval allocate_send_buffer(int size); + void setup_send(); + + // Returns offset at which bytestream (src, src + src_size) + // matches bytestream(target, target + target_size). + // If no sync found, return -1 + int get_syncoffset(char const* src, int src_size, + char const* target, int target_size) const; +#endif + enum state { - read_protocol_length = 0, - read_protocol_string, +#ifndef TORRENT_DISABLE_ENCRYPTION + read_pe_dhkey = 0, + read_pe_syncvc, + read_pe_synchash, + read_pe_skey_vc, + read_pe_cryptofield, + read_pe_pad, + read_pe_ia, + init_bt_handshake, + read_protocol_identifier, +#else + read_protocol_identifier = 0, +#endif read_info_hash, read_peer_id, + // handshake complete read_packet_size, read_packet }; +#ifndef TORRENT_DISABLE_ENCRYPTION + enum + { + handshake_len = 68, + dh_key_len = 96 + }; +#endif + std::string m_client_version; + // state of on_receive state m_state; // the timeout in seconds @@ -262,6 +326,44 @@ namespace libtorrent #endif bool m_supports_dht_port; +#ifndef TORRENT_DISABLE_ENCRYPTION + // this is set to true after the encryption method has been + // succesfully negotiated (either plaintext or rc4), to signal + // automatic encryption/decryption. + bool m_encrypted; + + // true if rc4, false if plaintext + bool m_rc4_encrypted; + + // used to disconnect peer if sync points are not found within + // the maximum number of bytes + int m_sync_bytes_read; + + // hold information about latest allocated send buffer + // need to check for non zero (begin, end) for operations with this + buffer::interval m_enc_send_buffer; + + // initialized during write_pe1_2_dhkey, and destroyed on + // creation of m_RC4_handler. Cannot reinitialize once + // initialized. + boost::scoped_ptr m_DH_key_exchange; + + // if RC4 is negotiated, this is used for + // encryption/decryption during the entire session. Destroyed + // if plaintext is selected + boost::scoped_ptr m_RC4_handler; + + // (outgoing only) synchronize verification constant with + // remote peer, this will hold RC4_decrypt(vc). Destroyed + // after the sync step. + boost::scoped_array m_sync_vc; + + // (incoming only) synchronize hash with remote peer, holds + // the sync hash (hash("req1",secret)). Destroyed after the + // sync step. + boost::scoped_ptr m_sync_hash; +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + #ifndef NDEBUG // this is set to true when the client's // bitfield is sent to this peer @@ -269,6 +371,7 @@ namespace libtorrent bool m_in_constructor; #endif + }; } diff --git a/include/libtorrent/buffer.hpp b/include/libtorrent/buffer.hpp index 91ef58874..0cb44225a 100644 --- a/include/libtorrent/buffer.hpp +++ b/include/libtorrent/buffer.hpp @@ -74,6 +74,12 @@ public: return begin[index]; } + bool operator==(const const_interval& p_interval) + { + return (begin == p_interval.begin + && end == p_interval.end); + } + int left() const { assert(end >= begin); return end - begin; } char const* begin; diff --git a/include/libtorrent/pe_crypto.hpp b/include/libtorrent/pe_crypto.hpp new file mode 100644 index 000000000..91616c42d --- /dev/null +++ b/include/libtorrent/pe_crypto.hpp @@ -0,0 +1,125 @@ +/* + +Copyright (c) 2007, Un Shyam +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#ifndef TORRENT_PE_CRYPTO_HPP_INCLUDED +#define TORRENT_PE_CRYPTO_HPP_INCLUDED + +#include + +#include +#include +#include + +#include "peer_id.hpp" // For sha1_hash + +namespace libtorrent +{ + class DH_key_exchange + { + public: + DH_key_exchange (); + ~DH_key_exchange (); + + // Get local public key, always 96 bytes + char const* get_local_key (void) const; + + // read remote_pubkey, generate and store shared secret in + // m_dh_secret + void compute_secret (const char* remote_pubkey); + + const char* get_secret (void) const; + + private: + int get_local_key_size () const + { + assert (m_DH); + return BN_num_bytes (m_DH->pub_key); + } + + DH* m_DH; + static const unsigned char m_dh_prime[96]; + static const unsigned char m_dh_generator[1]; + + char m_dh_local_key[96]; + char m_dh_secret[96]; + }; + + class RC4_handler // Non copyable + { + public: + // Input longkeys must be 20 bytes + RC4_handler (const sha1_hash& rc4_local_longkey, + const sha1_hash& rc4_remote_longkey) + + { + RC4_set_key (&m_local_key, 20, + reinterpret_cast(rc4_local_longkey.begin())); + RC4_set_key (&m_remote_key, 20, + reinterpret_cast(rc4_remote_longkey.begin())); + + // Discard first 1024 bytes + char buf[1024]; + encrypt (buf, 1024); + decrypt (buf, 1024); + }; + + ~RC4_handler () {}; + + void encrypt (char* pos, int len) + { + assert (len >= 0); + assert (pos); + + RC4 (&m_local_key, len, reinterpret_cast(pos), + reinterpret_cast(pos)); + } + + void decrypt (char* pos, int len) + { + assert (len >= 0); + assert (pos); + + RC4 (&m_remote_key, len, reinterpret_cast(pos), + reinterpret_cast(pos)); + } + + private: + RC4_KEY m_local_key; // Key to encrypt outgoing data + RC4_KEY m_remote_key; // Key to decrypt incoming data + }; + +} // namespace libtorrent + +#endif // TORRENT_PE_CRYPTO_HPP_INCLUDED +#endif // TORRENT_DISABLE_ENCRYPTION diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index d1ae9c93a..9514874fb 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -395,6 +395,14 @@ namespace libtorrent - m_write_pos; } +#ifndef TORRENT_DISABLE_ENCRYPTION + buffer::interval wr_recv_buffer() + { + return buffer::interval(&m_recv_buffer[0] + , &m_recv_buffer[0] + m_recv_pos); + } +#endif + buffer::const_interval receive_buffer() const { return buffer::const_interval(&m_recv_buffer[0] @@ -408,7 +416,6 @@ namespace libtorrent bool packet_finished() const { - assert(m_recv_pos <= m_packet_size); return m_packet_size <= m_recv_pos; } diff --git a/include/libtorrent/peer_id.hpp b/include/libtorrent/peer_id.hpp index e5a224bc4..23a5eb463 100755 --- a/include/libtorrent/peer_id.hpp +++ b/include/libtorrent/peer_id.hpp @@ -117,6 +117,13 @@ namespace libtorrent return *this; } + big_number& operator ^= (big_number const& n) + { + for (int i = 0; i< number_size; ++i) + m_number[i] ^= n.m_number[i]; + return *this; + } + unsigned char& operator[](int i) { assert(i >= 0 && i < number_size); return m_number[i]; } diff --git a/include/libtorrent/peer_info.hpp b/include/libtorrent/peer_info.hpp index 467055c94..e6c92e580 100755 --- a/include/libtorrent/peer_info.hpp +++ b/include/libtorrent/peer_info.hpp @@ -57,6 +57,10 @@ namespace libtorrent queued = 0x100, on_parole = 0x200, seed = 0x400 +#ifndef TORRENT_DISABLE_ENCRYPTION + , rc4_encrypted = 0x800, + plaintext_encrypted = 0x1000 +#endif }; unsigned int flags; diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index 7aae27299..9404aa095 100755 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -135,6 +135,17 @@ namespace libtorrent tcp::endpoint ip; connection_type type; +#ifndef TORRENT_DISABLE_ENCRYPTION + // Hints encryption support of peer. Only effective for + // and when the outgoing encryption policy allows both + // encrypted and non encrypted connections + // (pe_settings::out_enc_policy == enabled). The initial + // state of this flag determines the initial connection + // attempt type (true = encrypted, false = standard). + // This will be toggled everytime either an encrypted or + // non-encrypted handshake fails. + bool pe_support; +#endif // the number of failed connection attempts this peer has int failcount; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index b209257a8..c80ada5c9 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -178,6 +178,11 @@ namespace libtorrent void add_dht_router(std::pair const& node); #endif +#ifndef TORRENT_DISABLE_ENCRYPTION + void set_pe_settings(pe_settings const& settings); + pe_settings const& get_pe_settings() const; +#endif + #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::function(torrent*)> ext); #endif diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index 1d61782ba..5d39264a6 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -269,6 +269,42 @@ namespace libtorrent }; #endif +#ifndef TORRENT_DISABLE_ENCRYPTION + + struct pe_settings + { + pe_settings() + : out_enc_policy(enabled) + , in_enc_policy(enabled) + , allowed_enc_level(both) + , prefer_rc4(false) + {} + + enum enc_policy + { + forced, // disallow non encrypted connections + enabled, // allow encrypted and non encrypted connections + disabled // disallow encrypted connections + }; + + enum enc_level + { + plaintext, // use only plaintext encryption + rc4, // use only rc4 encryption + both // allow both + }; + + enc_policy out_enc_policy; + enc_policy in_enc_policy; + + enc_level allowed_enc_level; + // if the allowed encryption level is both, setting this to + // true will prefer rc4 if both methods are offered, plaintext + // otherwise + bool prefer_rc4; + }; +#endif + } #endif diff --git a/src/Makefile.am b/src/Makefile.am index 4be4e7862..5745d0d93 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ libtorrent_la_SOURCES = allocate_resources.cpp \ bandwidth_manager.cpp entry.cpp escape_string.cpp \ peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \ -stat.cpp storage.cpp torrent.cpp torrent_handle.cpp \ +stat.cpp storage.cpp torrent.cpp torrent_handle.cpp pe_crypto.cpp \ torrent_info.cpp tracker_manager.cpp http_connection.cpp \ http_tracker_connection.cpp udp_tracker_connection.cpp \ alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \ @@ -59,6 +59,7 @@ $(top_srcdir)/include/libtorrent/peer.hpp \ $(top_srcdir)/include/libtorrent/peer_connection.hpp \ $(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ $(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/pe_crypto.hpp \ $(top_srcdir)/include/libtorrent/natpmp.hpp \ $(top_srcdir)/include/libtorrent/pch.hpp \ $(top_srcdir)/include/libtorrent/peer_id.hpp \ diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index f6a78459b..27631d255 100755 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -1,6 +1,7 @@ /* -Copyright (c) 2003, Arvid Norberg +Copyright (c) 2003 - 2006, Arvid Norberg +Copyright (c) 2007, Arvid Norberg, Un Shyam All rights reserved. Redistribution and use in source and binary forms, with or without @@ -50,13 +51,17 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/extensions.hpp" #include "libtorrent/aux_/session_impl.hpp" +#ifndef TORRENT_DISABLE_ENCRYPTION +#include "libtorrent/pe_crypto.hpp" +#include "libtorrent/hasher.hpp" +#endif + using boost::bind; using boost::shared_ptr; using libtorrent::aux::session_impl; namespace libtorrent { - const bt_peer_connection::message_handler bt_peer_connection::m_message_handler[] = { @@ -83,11 +88,17 @@ namespace libtorrent , policy::peer* peerinfo) : peer_connection(ses, tor, s, remote , peerinfo) - , m_state(read_protocol_length) + , m_state(read_protocol_identifier) #ifndef TORRENT_DISABLE_EXTENSIONS , m_supports_extensions(false) #endif , m_supports_dht_port(false) +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_encrypted(false) + , m_rc4_encrypted(false) + , m_sync_bytes_read(0) + , m_enc_send_buffer(0, 0) +#endif #ifndef NDEBUG , m_sent_bitfield(false) , m_in_constructor(true) @@ -97,21 +108,57 @@ namespace libtorrent (*m_logger) << "*** bt_peer_connection\n"; #endif - write_handshake(); - - // start in the state where we are trying to read the - // handshake from the other side - reset_recv_buffer(1); - - // assume the other end has no pieces - boost::shared_ptr t = associated_torrent().lock(); - assert(t); +#ifndef TORRENT_DISABLE_ENCRYPTION - if (t->ready_for_connections()) - write_bitfield(t->pieces()); + pe_settings::enc_policy const& out_enc_policy = m_ses.get_pe_settings().out_enc_policy; - setup_send(); - setup_receive(); + if (out_enc_policy == pe_settings::forced) + { + write_pe1_2_dhkey(); + + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else if (out_enc_policy == pe_settings::enabled) + { + assert(peer_info_struct()); + + policy::peer* pi = peer_info_struct(); + if (pi->pe_support == true) + { + // toggle encryption support flag, toggled back to + // true if encrypted portion of the handshake + // completes correctly + pi->pe_support = !(pi->pe_support); + + write_pe1_2_dhkey(); + m_state = read_pe_dhkey; + reset_recv_buffer(dh_key_len); + setup_receive(); + } + else // pi->pe_support == false + { + // toggled back to false if standard handshake + // completes correctly (without encryption) + pi->pe_support = !(pi->pe_support); + + write_handshake(); + reset_recv_buffer(20); + setup_receive(); + } + } + else if (out_enc_policy == pe_settings::disabled) +#endif + { + write_handshake(); + + // start in the state where we are trying to read the + // handshake from the other side + reset_recv_buffer(20); + setup_receive(); + } + #ifndef NDEBUG m_in_constructor = false; #endif @@ -122,16 +169,23 @@ namespace libtorrent , boost::shared_ptr s , policy::peer* peerinfo) : peer_connection(ses, s, peerinfo) - , m_state(read_protocol_length) + , m_state(read_protocol_identifier) #ifndef TORRENT_DISABLE_EXTENSIONS , m_supports_extensions(false) #endif , m_supports_dht_port(false) +#ifndef TORRENT_DISABLE_ENCRYPTION + , m_encrypted(false) + , m_rc4_encrypted(false) + , m_sync_bytes_read(0) + , m_enc_send_buffer(0, 0) +#endif #ifndef NDEBUG , m_sent_bitfield(false) , m_in_constructor(true) #endif { + // we are not attached to any torrent yet. // we have to wait for the handshake to see // which torrent the connector want's to connect to @@ -141,12 +195,17 @@ namespace libtorrent // that are part of a torrent. Since this is an incoming // connection, we have to give it some initial bandwidth // to send the handshake. +#ifndef TORRENT_DISABLE_ENCRYPTION + m_bandwidth_limit[download_channel].assign(2048); + m_bandwidth_limit[upload_channel].assign(2048); +#else m_bandwidth_limit[download_channel].assign(80); m_bandwidth_limit[upload_channel].assign(80); +#endif // start in the state where we are trying to read the // handshake from the other side - reset_recv_buffer(1); + reset_recv_buffer(20); setup_receive(); #ifndef NDEBUG m_in_constructor = false; @@ -189,11 +248,21 @@ namespace libtorrent if (has_peer_choked()) p.flags |= peer_info::remote_choked; if (support_extensions()) p.flags |= peer_info::supports_extensions; if (is_local()) p.flags |= peer_info::local_connection; - if (!is_connecting() && m_state < read_packet_size) + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_encrypted) + { + m_rc4_encrypted ? + p.flags |= peer_info::rc4_encrypted : + p.flags |= peer_info::plaintext_encrypted; + } +#endif + + if (!is_connecting() && in_handshake()) p.flags |= peer_info::handshake; if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; if (is_queued()) p.flags |= peer_info::queued; - + p.client = m_client_version; p.connection_type = peer_info::standard_bittorrent; @@ -204,6 +273,296 @@ namespace libtorrent return m_state < read_packet_size; } +#ifndef TORRENT_DISABLE_ENCRYPTION + + void bt_peer_connection::write_pe1_2_dhkey() + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(!m_DH_key_exchange.get()); + +#ifdef TORRENT_VERBOSE_LOGGING + if (is_local()) + (*m_logger) << " initiating encrypted handshake\n"; +#endif + + m_DH_key_exchange.reset(new DH_key_exchange); + + int pad_size = std::rand() % 512; + buffer::interval send_buf = allocate_send_buffer(dh_key_len + pad_size); + + std::copy (m_DH_key_exchange->get_local_key(), + m_DH_key_exchange->get_local_key() + dh_key_len, + send_buf.begin); + + std::generate(send_buf.begin + dh_key_len, send_buf.end, std::rand); + setup_send(); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " sent DH key\n"; +#endif + } + + void bt_peer_connection::write_pe3_sync() + { + assert (!m_encrypted); + assert (!m_rc4_encrypted); + assert (is_local()); + + boost::shared_ptr t = associated_torrent().lock(); + assert(t); + + hasher h; + sha1_hash const& info_hash = t->torrent_file().info_hash(); + char const* const secret = m_DH_key_exchange->get_secret(); + + int pad_size = 0; // rand() % 512; // Keep 0 for now + + // synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia) + buffer::interval send_buf = + allocate_send_buffer (20 + 20 + 8 + 4 + 2 + pad_size + 2); + + // sync hash (hash('req1',S)) + h.reset(); + h.update("req1",4); + h.update(secret, dh_key_len); + sha1_hash sync_hash = h.final(); + + std::copy (sync_hash.begin(), sync_hash.end(), send_buf.begin); + send_buf.begin += 20; + + // stream key obfuscated hash [ hash('req2',SKEY) xor hash('req3',S) ] + h.reset(); + h.update("req2",4); + h.update((const char*)info_hash.begin(), 20); + sha1_hash streamkey_hash = h.final(); + + h.reset(); + h.update("req3",4); + h.update(secret, dh_key_len); + sha1_hash obfsc_hash = h.final(); + obfsc_hash ^= streamkey_hash; + + std::copy (obfsc_hash.begin(), obfsc_hash.end(), send_buf.begin); + send_buf.begin += 20; + + // Discard DH key exchange data, setup RC4 keys + init_pe_RC4_handler(secret, info_hash); + m_DH_key_exchange.reset(); // secret should be invalid at this point + + // write the verification constant and crypto field + assert(send_buf.left() == 8 + 4 + 2 + pad_size + 2); + int encrypt_size = send_buf.left(); + + int crypto_provide = 0; + pe_settings::enc_level const& allowed_enc_level = m_ses.get_pe_settings().allowed_enc_level; + + if (allowed_enc_level == pe_settings::both) + crypto_provide = 0x03; + else if (allowed_enc_level == pe_settings::rc4) + crypto_provide = 0x02; + else if (allowed_enc_level == pe_settings::plaintext) + crypto_provide = 0x01; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " crypto provide : [ "; + if (allowed_enc_level == pe_settings::both) + (*m_logger) << "plaintext rc4 ]\n"; + else if (allowed_enc_level == pe_settings::rc4) + (*m_logger) << "rc4 ]\n"; + else if (allowed_enc_level == pe_settings::plaintext) + (*m_logger) << "plaintext ]\n"; +#endif + + write_pe_vc_cryptofield(send_buf, crypto_provide, pad_size); + m_RC4_handler->encrypt(send_buf.end - encrypt_size, encrypt_size); + + assert(send_buf.begin == send_buf.end); + setup_send(); + } + + void bt_peer_connection::write_pe4_sync(int crypto_select) + { + assert(!is_local()); + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(crypto_select == 0x02 || crypto_select == 0x01); + + int pad_size = 0; // rand() % 512; // Keep 0 for now + + const int buf_size = 8+4+2+pad_size; + buffer::interval send_buf = allocate_send_buffer(buf_size); + write_pe_vc_cryptofield(send_buf, crypto_select, pad_size); + + m_RC4_handler->encrypt(send_buf.end - buf_size, buf_size); + setup_send(); + + // encryption method has been negotiated + if (crypto_select == 0x02) + m_rc4_encrypted = true; + else // 0x01 + m_rc4_encrypted = false; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " crypto select : [ "; + if (crypto_select == 0x01) + (*m_logger) << "plaintext ]\n"; + else + (*m_logger) << "rc4 ]\n"; +#endif + } + + void bt_peer_connection::write_pe_vc_cryptofield(buffer::interval& write_buf, + int crypto_field, int pad_size) + { + assert(crypto_field <= 0x03 && crypto_field > 0); + assert(pad_size == 0); // pad not used yet + // vc,crypto_field,len(pad),pad, (len(ia)) + assert( (write_buf.left() == 8+4+2+pad_size+2 && is_local()) || + (write_buf.left() == 8+4+2+pad_size && !is_local()) ); + + // encrypt(vc, crypto_provide/select, len(Pad), len(IA)) + // len(pad) is zero for now, len(IA) only for outgoing connections + + // vc + std::fill(write_buf.begin, write_buf.begin + 8, 0); + write_buf.begin += 8; + + detail::write_uint32(crypto_field, write_buf.begin); + detail::write_uint16(pad_size, write_buf.begin); // len (pad) + + // fill pad with zeroes + // std::fill(write_buf.begin, write_buf.begin+pad_size, 0); + // write_buf.begin += pad_size; + + // append len(ia) if we are initiating + if (is_local()) + detail::write_uint16(handshake_len, write_buf.begin); // len(IA) + + assert (write_buf.begin == write_buf.end); + } + + void bt_peer_connection::init_pe_RC4_handler(char const* secret, sha1_hash const& stream_key) + { + assert(secret); + + hasher h; + const char keyA[] = "keyA"; + const char keyB[] = "keyB"; + + // encryption rc4 longkeys + // outgoing connection : hash ('keyA',S,SKEY) + // incoming connection : hash ('keyB',S,SKEY) + + is_local() ? h.update(keyA, 4) : h.update(keyB, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 20); + const sha1_hash local_key = h.final(); + + h.reset(); + + // decryption rc4 longkeys + // outgoing connection : hash ('keyB',S,SKEY) + // incoming connection : hash ('keyA',S,SKEY) + + is_local() ? h.update(keyB, 4) : h.update(keyA, 4); + h.update(secret, dh_key_len); + h.update((char const*)stream_key.begin(), 20); + const sha1_hash remote_key = h.final(); + + assert(!m_RC4_handler.get()); + m_RC4_handler.reset (new RC4_handler (local_key, remote_key)); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " computed RC4 keys\n"; +#endif + } + + void bt_peer_connection::send_buffer(char* begin, char* end) + { + assert (begin); + assert (end); + assert (end > begin); + + if (m_rc4_encrypted && m_encrypted) + m_RC4_handler->encrypt(begin, end - begin); + + peer_connection::send_buffer(begin, end); + } + + buffer::interval bt_peer_connection::allocate_send_buffer(int size) + { + if (m_rc4_encrypted && m_encrypted) + { + m_enc_send_buffer = peer_connection::allocate_send_buffer(size); + return m_enc_send_buffer; + } + else + { + buffer::interval i = peer_connection::allocate_send_buffer(size); + return i; + } + } + + void bt_peer_connection::setup_send() + { + if (m_rc4_encrypted && m_encrypted) + { + assert (m_enc_send_buffer.begin); + assert (m_enc_send_buffer.end); + assert (m_enc_send_buffer.left() > 0); + + m_RC4_handler->encrypt (m_enc_send_buffer.begin, m_enc_send_buffer.left()); + } + peer_connection::setup_send(); + } + + int bt_peer_connection::get_syncoffset(char const* src, int src_size, + char const* target, int target_size) const + { + assert (target_size >= src_size); + assert (src_size > 0); + assert (src); + assert (target); + + int traverse_limit = target_size - src_size; + + // TODO: this could be optimized using knuth morris pratt + for (int i = 0; i < traverse_limit; ++i) + { + char const* target_ptr = target + i; + if (std::equal(src, src+src_size, target_ptr)) + return i; + } + +// // Partial sync +// for (int i = 0; i < target_size; ++i) +// { +// // first is iterator in src[] at which mismatch occurs +// // second is iterator in target[] at which mismatch occurs +// std::pair ret; +// int src_sync_size; +// if (i > traverse_limit) // partial sync test +// { +// ret = std::mismatch(src, src + src_size - (i - traverse_limit), &target[i]); +// src_sync_size = ret.first - src; +// if (src_sync_size == (src_size - (i - traverse_limit))) +// return i; +// } +// else // complete sync test +// { +// ret = std::mismatch(src, src + src_size, &target[i]); +// src_sync_size = ret.first - src; +// if (src_sync_size == src_size) +// return i; +// } +// } + + // no complete sync + return -1; + } +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + void bt_peer_connection::write_handshake() { INVARIANT_CHECK; @@ -770,6 +1129,7 @@ namespace libtorrent { INVARIANT_CHECK; + assert(!in_handshake()); boost::shared_ptr t = associated_torrent().lock(); assert(t); assert(m_sent_bitfield == false); @@ -1017,69 +1377,565 @@ namespace libtorrent INVARIANT_CHECK; if (error) return; - - buffer::const_interval recv_buffer = receive_buffer(); - boost::shared_ptr t = associated_torrent().lock(); - - switch(m_state) - { - case read_protocol_length: - { + + if (in_handshake()) m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; + +#ifndef TORRENT_DISABLE_ENCRYPTION + if (m_rc4_encrypted && m_encrypted) + { + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt((wr_buf.end - bytes_transferred), bytes_transferred); + } +#endif + + buffer::const_interval recv_buffer = receive_buffer(); + +#ifndef TORRENT_DISABLE_ENCRYPTION + // m_state is set to read_pe_dhkey in initial state + // (read_protocol_identifier) for incoming, or in constructor + // for outgoing + if (m_state == read_pe_dhkey) + { + assert (!m_encrypted); + assert (!m_rc4_encrypted); + assert (packet_size() == dh_key_len); + assert (recv_buffer == receive_buffer()); + + if (!packet_finished()) return; + + // write our dh public key. m_DH_key_exchange is + // initialized in write_pe1_2_dhkey() + if (!is_local()) + write_pe1_2_dhkey(); + + // read dh key, generate shared secret + m_DH_key_exchange->compute_secret (recv_buffer.begin); // TODO handle errors + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " received DH key\n"; +#endif + + // 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 + // of requesting the maximum possible, request the maximum + // possible to ensure we do not overshoot the standard + // handshake. + + if (is_local()) + { + m_state = read_pe_syncvc; + write_pe3_sync(); + + // initial payload is the standard handshake, this is + // always rc4 if sent here. m_rc4_encrypted is flagged + // again according to peer selection. + m_rc4_encrypted = true; + m_encrypted = true; + write_handshake(); + m_rc4_encrypted = false; + m_encrypted = false; + + // vc,crypto_select,len(pad),pad, encrypt(handshake) + // 8+4+2+0+handshake_len + reset_recv_buffer(8+4+2+0+handshake_len); + } + else + { + // already written dh key + m_state = read_pe_synchash; + // synchash,skeyhash,vc,crypto_provide,len(pad),pad,encrypt(handshake) + reset_recv_buffer(20+20+8+4+2+0+handshake_len); + } + assert(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_pe_synchash) + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(!is_local()); + assert(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 20) + { + if (packet_finished()) + { + throw protocol_error ("sync hash not found"); + } + // else + return; + } + + if (!m_sync_hash.get()) + { + assert(m_sync_bytes_read == 0); + hasher h; + + // compute synchash (hash('req1',S)) + h.update("req1", 4); + h.update(m_DH_key_exchange->get_secret(), dh_key_len); + + m_sync_hash.reset(new sha1_hash(h.final())); + } + + int syncoffset = get_syncoffset((char*)m_sync_hash->begin(), 20 + , recv_buffer.begin, recv_buffer.left()); + + // No sync + if (syncoffset == -1) + { + std::size_t bytes_processed = recv_buffer.left() - 20; + m_sync_bytes_read += bytes_processed; + if (m_sync_bytes_read >= 512) + throw protocol_error("sync hash not found within 532 bytes"); + + cut_receive_buffer(bytes_processed, std::min(packet_size(), (512+20) - m_sync_bytes_read)); + + assert(!packet_finished()); + return; + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 20; +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " sync point (hash) found at offset " + << m_sync_bytes_read + bytes_processed - 20 << "\n"; +#endif + m_state = read_pe_skey_vc; + // skey,vc - 28 bytes + m_sync_hash.reset(); + cut_receive_buffer(bytes_processed, 28); + } + } + + if (m_state == read_pe_skey_vc) + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(!is_local()); + assert(packet_size() == 28); + + if (!packet_finished()) return; + + recv_buffer = receive_buffer(); + + // only calls info_hash() on the torrent_handle's, which + // never throws. + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + std::vector active_torrents = m_ses.get_torrents(); + std::vector::const_iterator i; + hasher h; + sha1_hash skey_hash, obfs_hash; + + for (i = active_torrents.begin(); i != active_torrents.end(); ++i) + { + torrent_handle const& t_h = *i; // TODO possible errors + sha1_hash const& info_hash = t_h.info_hash(); + // TODO Does info_hash need to be checked for validity? + + h.reset(); + h.update("req2", 4); + h.update((char*)info_hash.begin(), 20); + + skey_hash = h.final(); + + h.reset(); + h.update("req3", 4); + h.update(m_DH_key_exchange->get_secret(), dh_key_len); + + obfs_hash = h.final(); + obfs_hash ^= skey_hash; + + if (std::equal (recv_buffer.begin, recv_buffer.begin + 20, + (char*)obfs_hash.begin())) + { + if (!t) + { + attach_to_torrent(info_hash); + t = associated_torrent().lock(); + assert(t); + } + + init_pe_RC4_handler(m_DH_key_exchange->get_secret(), info_hash); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " stream key found, torrent located.\n"; +#endif + continue; // TODO Check flow control with multiple torrents + } + } + + if (!m_RC4_handler.get()) + throw protocol_error("invalid streamkey identifier (info hash) in encrypted handshake"); + + // verify constant + buffer::interval wr_recv_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_recv_buf.begin + 20, 8); + wr_recv_buf.begin += 28; + + const char sh_vc[] = {0,0,0,0, 0,0,0,0}; + if (!std::equal(sh_vc, sh_vc+8, recv_buffer.begin + 20)) + { + throw protocol_error("unable to verify constant"); + } + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " verification constant found\n"; +#endif + m_state = read_pe_cryptofield; + reset_recv_buffer(4 + 2); + } + + // cannot fall through into + if (m_state == read_pe_syncvc) + { + assert(is_local()); + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(recv_buffer == receive_buffer()); + + if (recv_buffer.left() < 8) + { + if (packet_finished()) + { + throw protocol_error ("sync verification constant not found"); + } + // else + return; + } + + // generate the verification constant + if (!m_sync_vc.get()) + { + assert(m_sync_bytes_read == 0); + + m_sync_vc.reset (new char[8]); + std::fill(m_sync_vc.get(), m_sync_vc.get() + 8, 0); + m_RC4_handler->decrypt(m_sync_vc.get(), 8); + } + + assert(m_sync_vc.get()); + int syncoffset = get_syncoffset(m_sync_vc.get(), 8 + , recv_buffer.begin, recv_buffer.left()); + + // No sync + if (syncoffset == -1) + { + std::size_t bytes_processed = recv_buffer.left() - 8; + m_sync_bytes_read += bytes_processed; + if (m_sync_bytes_read >= 512) + throw protocol_error("sync verification constant not found within 520 bytes"); + + cut_receive_buffer(bytes_processed, std::min(packet_size(), (512+8) - m_sync_bytes_read)); + + assert(!packet_finished()); + return; + } + // found complete sync + else + { + std::size_t bytes_processed = syncoffset + 8; +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " sync point (verification constant) found at offset " + << m_sync_bytes_read + bytes_processed - 8 << "\n"; +#endif + cut_receive_buffer (bytes_processed, 4 + 2); + + // delete verification constant + m_sync_vc.reset(); + m_state = read_pe_cryptofield; + // fall through + } + } + + if (m_state == read_pe_cryptofield) // local/remote + { + assert(!m_encrypted); + assert(!m_rc4_encrypted); + assert(packet_size() == 4+2); + + if (!packet_finished()) return; + + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + int crypto_field = detail::read_int32(recv_buffer.begin); + +#ifdef TORRENT_VERBOSE_LOGGING + if (!is_local()) + (*m_logger) << " crypto provide : [ "; + else + (*m_logger) << " crypto select : [ "; + + if (crypto_field & 0x01) + (*m_logger) << "plaintext "; + if (crypto_field & 0x02) + (*m_logger) << "rc4 "; + (*m_logger) << "]\n"; +#endif + + if (!is_local()) + { + int crypto_select = 0; + // select a crypto method + switch (m_ses.get_pe_settings().allowed_enc_level) + { + case (pe_settings::plaintext): + { + if (!(crypto_field & 0x01)) + throw protocol_error("plaintext not provided"); + crypto_select = 0x01; + } + break; + case (pe_settings::rc4): + { + if (!(crypto_field & 0x02)) + throw protocol_error("rc4 not provided"); + crypto_select = 0x02; + } + break; + case (pe_settings::both): + { + if (m_ses.get_pe_settings().prefer_rc4) + { + if (crypto_field & 0x02) + crypto_select = 0x02; + else if (crypto_field & 0x01) + crypto_select = 0x01; + } + else + { + if (crypto_field & 0x01) + crypto_select = 0x01; + else if (crypto_field & 0x02) + crypto_select = 0x02; + } + if (!crypto_select) + throw protocol_error("rc4/plaintext not provided"); + } + } // switch + + // write the pe4 step + write_pe4_sync(crypto_select); + } + else // is_local() + { + // check if crypto select is valid + pe_settings::enc_level const& allowed_enc_level = m_ses.get_pe_settings().allowed_enc_level; + + if (crypto_field == 0x02) + { + if (allowed_enc_level == pe_settings::plaintext) + throw protocol_error("rc4 selected by peer when not provided"); + m_rc4_encrypted = true; + } + else if (crypto_field == 0x01) + { + if (allowed_enc_level == pe_settings::rc4) + throw protocol_error("plaintext selected by peer when not provided"); + m_rc4_encrypted = false; + } + else + throw protocol_error("unsupported crypto method selected by peer"); + } + + int len_pad = detail::read_int16(recv_buffer.begin); + if (len_pad < 0 || len_pad > 512) + throw protocol_error("invalid pad length"); + + m_state = read_pe_pad; + if (!is_local()) + reset_recv_buffer(len_pad + 2); // len(IA) at the end of pad + else + { + if (len_pad == 0) + { + m_encrypted = true; + m_state = init_bt_handshake; + } + else + reset_recv_buffer(len_pad); + } + } + + if (m_state == read_pe_pad) + { + assert(!m_encrypted); + if (!packet_finished()) return; + + int pad_size = is_local() ? packet_size() : packet_size() - 2; + + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_buf.begin, packet_size()); + + recv_buffer = receive_buffer(); + + if (!is_local()) + { + recv_buffer.begin += pad_size; + int len_ia = detail::read_int16(recv_buffer.begin); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " len(IA) : " << len_ia << "\n"; +#endif + if (len_ia == 0) + { + // everything after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + else + { + m_state = read_pe_ia; + reset_recv_buffer(len_ia); + } + } + else // is_local() + { + // everything that arrives after this is Encrypt2 + m_encrypted = true; + m_state = init_bt_handshake; + } + } + + if (m_state == read_pe_ia) + { + assert(!is_local()); + assert(!m_encrypted); + + if (!packet_finished()) return; + + // ia is always rc4, so decrypt it + buffer::interval wr_buf = wr_recv_buffer(); + m_RC4_handler->decrypt(wr_buf.begin, packet_size()); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " decrypted ia : " << packet_size() << " bytes\n"; +#endif + + if (!m_rc4_encrypted) + { + m_RC4_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " destroyed rc4 keys\n"; +#endif + } + + // everything that arrives after this is Encrypt2 + m_encrypted = true; + + m_state = read_protocol_identifier; + cut_receive_buffer(0, 20); + } + + if (m_state == init_bt_handshake) + { + assert(m_encrypted); + + // decrypt remaining received bytes + if (m_rc4_encrypted) + { + buffer::interval wr_buf = wr_recv_buffer(); + wr_buf.begin += packet_size(); + m_RC4_handler->decrypt(wr_buf.begin, wr_buf.left()); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " decrypted remaining " << wr_buf.left() << " bytes\n"; +#endif + } + else // !m_rc4_encrypted + { + m_RC4_handler.reset(); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " destroyed rc4 keys\n"; +#endif + } + + // payload stream, start with 20 handshake bytes + m_state = read_protocol_identifier; + reset_recv_buffer(20); + + // encrypted portion of handshake completed, toggle + // peer_info pe_support flag back to true + if (is_local() && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + assert(pi); + assert(pi->pe_support == false); + + pi->pe_support = !(pi->pe_support); + } + } + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + + if (m_state == read_protocol_identifier) + { + assert (packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); int packet_size = recv_buffer[0]; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " protocol length: " << packet_size << "\n"; -#endif - if (packet_size > 100 || packet_size <= 0) - { - std::stringstream s; - s << "incorrect protocol length (" - << packet_size - << ") should be 19."; - throw std::runtime_error(s.str()); - } - m_state = read_protocol_string; - reset_recv_buffer(packet_size); - } - break; - - case read_protocol_string: - { - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " protocol: '" << std::string(recv_buffer.begin - , recv_buffer.end) << "'\n"; -#endif const char protocol_string[] = "BitTorrent protocol"; - if (recv_buffer.end - recv_buffer.begin != 19 - || !std::equal(recv_buffer.begin, recv_buffer.end - , protocol_string)) + + if (packet_size != 19 || + !std::equal(recv_buffer.begin + 1, recv_buffer.begin + 19, protocol_string)) { +#ifndef TORRENT_DISABLE_ENCRYPTION + if (!is_local() && m_ses.get_pe_settings().in_enc_policy == pe_settings::disabled) + throw protocol_error("encrypted incoming connections disabled"); + + // Don't attempt to perform an encrypted handshake + // within an encrypted connection + if (!m_encrypted && !is_local()) + { #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "incorrect protocol name\n"; + (*m_logger) << " attempting encrypted connection\n"; #endif - std::stringstream s; - s << "got invalid protocol name: '" - << std::string(recv_buffer.begin, recv_buffer.end) - << "'"; - throw std::runtime_error(s.str()); + m_state = read_pe_dhkey; + cut_receive_buffer(0, dh_key_len); + assert(!packet_finished()); + return; + } + + assert ((!is_local() && m_encrypted) || is_local()); +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION + throw protocol_error("incorrect protocol identifier"); } +#ifndef TORRENT_DISABLE_ENCRYPTION + assert (m_state != read_pe_dhkey); + + if (!is_local() && + (m_ses.get_pe_settings().in_enc_policy == pe_settings::forced) && + !m_encrypted) + throw protocol_error("non encrypted incoming connections disabled"); +#endif + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " BitTorrent protocol\n"; +#endif + m_state = read_info_hash; reset_recv_buffer(28); } - break; - case read_info_hash: + // fall through + if (m_state == read_info_hash) { - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; + assert(packet_size() == 28); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); + #ifdef TORRENT_VERBOSE_LOGGING for (int i=0; i < 8; ++i) @@ -1093,7 +1949,7 @@ namespace libtorrent (*m_logger) << "\n"; if (recv_buffer[7] & 0x01) (*m_logger) << "supports DHT port message\n"; - if (recv_buffer[7] & 0x02) + if (recv_buffer[7] & 0x04) (*m_logger) << "supports FAST extensions\n"; if (recv_buffer[5] & 0x10) (*m_logger) << "supports extensions protocol\n"; @@ -1119,12 +1975,6 @@ namespace libtorrent attach_to_torrent(info_hash); t = associated_torrent().lock(); assert(t); - - // yes, we found the torrent - // reply with our handshake - write_handshake(); - if (t->valid_metadata()) - write_bitfield(t->pieces()); } else { @@ -1137,24 +1987,38 @@ namespace libtorrent #endif throw protocol_error("invalid info-hash in handshake"); } + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " info_hash received\n"; +#endif + t = associated_torrent().lock(); + assert(t); + } + + // Respond with handshake and bitfield + if (!is_local()) + { + write_handshake(); } assert(t->get_policy().has_connection(this)); m_state = read_peer_id; - reset_recv_buffer(20); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " info_hash received\n"; -#endif + reset_recv_buffer(20); } - break; - case read_peer_id: + // fall through + if (m_state == read_peer_id) { - if (!t) return; - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; + if (!t) + { + assert(!packet_finished()); // TODO + return; + } assert(packet_size() == 20); + + if (!packet_finished()) return; + recv_buffer = receive_buffer(); #ifdef TORRENT_VERBOSE_LOGGING { @@ -1243,18 +2107,44 @@ namespace libtorrent if (m_supports_extensions) write_extensions(); #endif +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() << " <== HANDSHAKE\n"; +#endif // consider this a successful connection, reset the failcount if (peer_info_struct()) peer_info_struct()->failcount = 0; + +#ifndef TORRENT_DISABLE_ENCRYPTION + // Toggle pe_support back to false if this is a + // standard successful connection + if (is_local() && !m_encrypted && + m_ses.get_pe_settings().out_enc_policy == pe_settings::enabled) + { + policy::peer* pi = peer_info_struct(); + assert(pi); + assert(pi->pe_support == true); + + pi->pe_support = !(pi->pe_support); + } +#endif + m_state = read_packet_size; reset_recv_buffer(4); - } - break; + if (t->valid_metadata()) + write_bitfield(t->pieces()); - case read_packet_size: + assert(!packet_finished()); + return; + } + + // cannot fall through into + if (m_state == read_packet_size) { + // Make sure this is not fallen though into + assert (recv_buffer == receive_buffer()); + if (!t) return; m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; + if (!packet_finished()) return; const char* ptr = recv_buffer.begin; int packet_size = detail::read_int32(ptr); @@ -1280,22 +2170,25 @@ namespace libtorrent m_state = read_packet; reset_recv_buffer(packet_size); } + assert(!packet_finished()); + return; } - break; - case read_packet: + if (m_state == read_packet) { + assert(recv_buffer == receive_buffer()); if (!t) return; if (dispatch_message(bytes_transferred)) { m_state = read_packet_size; reset_recv_buffer(4); } + assert(!packet_finished()); + return; } - break; - - } - } + + assert(!packet_finished()); + } // -------------------------- // SEND DATA @@ -1346,6 +2239,10 @@ namespace libtorrent #ifndef NDEBUG void bt_peer_connection::check_invariant() const { +#ifndef TORRENT_DISABLE_ENCRYPTION + assert(bool(m_state == read_pe_dhkey) == bool(m_DH_key_exchange) + || !is_local()); +#endif if (!m_in_constructor) peer_connection::check_invariant(); diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index 94b1e422a..cc039f198 100755 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -412,6 +412,10 @@ namespace libtorrent m_send_buffer += '&'; } +#ifndef TORRENT_DISABLE_ENCRYPTION + m_send_buffer += "supportcrypto=1&"; +#endif + // extension that tells the tracker that // we don't need any peer_id's in the response if (!url_has_argument(request, "no_peer_id")) diff --git a/src/pe_crypto.cpp b/src/pe_crypto.cpp new file mode 100644 index 000000000..a763e5458 --- /dev/null +++ b/src/pe_crypto.cpp @@ -0,0 +1,122 @@ +/* + +Copyright (c) 2007, Un Shyam +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_DISABLE_ENCRYPTION + +#include +#include + +#include +#include + +#include "libtorrent/pe_crypto.hpp" + +namespace libtorrent { + + + // Set the prime P and the generator, generate local public key + DH_key_exchange::DH_key_exchange () + { + m_DH = DH_new (); + + m_DH->p = BN_bin2bn (m_dh_prime, sizeof(m_dh_prime), NULL); + m_DH->g = BN_bin2bn (m_dh_generator, sizeof(m_dh_generator), NULL); + + assert (sizeof(m_dh_prime) == DH_size(m_DH)); + + DH_generate_key (m_DH); // TODO Check != 0 + + assert (m_DH->pub_key); + + // DH can generate key sizes that are smaller than the size of + // P with exponentially decreasing probability, in which case + // the msb's of m_dh_local_key need to be zeroed + // appropriately. + int key_size = get_local_key_size(); + int len_dh = sizeof(m_dh_prime); // must equal DH_size(m_DH) + if (key_size != len_dh) + { + assert(key_size > 0 && key_size < len_dh); + + int pad_zero_size = len_dh - key_size; + std::fill(m_dh_local_key, m_dh_local_key + pad_zero_size, 0); + BN_bn2bin(m_DH->pub_key, (unsigned char*)m_dh_local_key + pad_zero_size); + } + else + BN_bn2bin(m_DH->pub_key, (unsigned char*)m_dh_local_key); // TODO Check return value + } + + DH_key_exchange::~DH_key_exchange () + { + assert (m_DH); + DH_free (m_DH); + } + + char const* DH_key_exchange::get_local_key () const + { + return m_dh_local_key; + } + + + // compute shared secret given remote public key + void DH_key_exchange::compute_secret (char const* remote_pubkey) + { + assert (remote_pubkey); + BIGNUM* bn_remote_pubkey = BN_bin2bn ((unsigned char*)remote_pubkey, 96, NULL); + + int ret = + DH_compute_key ( (unsigned char*)m_dh_secret, bn_remote_pubkey, m_DH); // TODO Check for errors + + BN_free (bn_remote_pubkey); + } + + char const* DH_key_exchange::get_secret () const + { + return m_dh_secret; + } + + const unsigned char DH_key_exchange::m_dh_prime[96] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 + }; + + const unsigned char DH_key_exchange::m_dh_generator[1] = { 2 }; + +} // namespace libtorrent + +#endif // #ifndef TORRENT_DISABLE_ENCRYPTION diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 28abb9ba0..eea933e79 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -336,6 +336,9 @@ namespace libtorrent void peer_connection::announce_piece(int index) { + // dont announce during handshake + if (in_handshake()) return; + // optimization, don't send have messages // to peers that already have the piece if (!m_ses.settings().send_redundant_have @@ -1769,8 +1772,9 @@ namespace libtorrent assert(int(m_recv_buffer.size()) >= size); assert(int(m_recv_buffer.size()) >= m_recv_pos); assert(m_recv_pos >= size); - - std::memmove(&m_recv_buffer[0], &m_recv_buffer[0] + size, m_recv_pos - size); + + if (size > 0) + std::memmove(&m_recv_buffer[0], &m_recv_buffer[0] + size, m_recv_pos - size); m_recv_pos -= size; @@ -1869,6 +1873,8 @@ namespace libtorrent } } + m_statistics.second_tick(tick_interval); + // If the client sends more data // we send it data faster, otherwise, slower. // It will also depend on how much data the @@ -2071,7 +2077,7 @@ namespace libtorrent } return; } - + if (!can_write()) return; assert(!m_writing); @@ -2145,7 +2151,6 @@ namespace libtorrent assert(m_recv_pos >= 0); assert(m_packet_size > 0); - assert(max_receive > 0); assert(can_read()); #ifdef TORRENT_VERBOSE_LOGGING @@ -2169,7 +2174,7 @@ namespace libtorrent if (int(m_recv_buffer.size()) < m_packet_size) m_recv_buffer.resize(m_packet_size); } - + void peer_connection::send_buffer(char const* begin, char const* end) { std::vector& buf = m_send_buffer[m_current_send_buffer]; @@ -2213,10 +2218,11 @@ namespace libtorrent assert(m_reading); m_reading = false; + if (error) { #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "**ERROR**: " << error.message() << "\n"; + (*m_logger) << "**ERROR**: " << error.message() << "[in peer_connection::on_receive_data]\n"; #endif on_receive(error, bytes_transferred); throw std::runtime_error(error.message()); @@ -2300,7 +2306,7 @@ namespace libtorrent // all exceptions should derive from std::exception assert(false); session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); + m_ses.connection_failed(m_socket, remote(), "connection failed for unknown reason"); } bool peer_connection::can_write() const @@ -2428,10 +2434,11 @@ namespace libtorrent m_write_pos += bytes_transferred; + if (error) { #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "**ERROR**: " << error.message() << "\n"; + (*m_logger) << "**ERROR**: " << error.message() << " [in peer_connection::on_send_data]\n"; #endif throw std::runtime_error(error.message()); } @@ -2480,7 +2487,7 @@ namespace libtorrent // all exceptions should derive from std::exception assert(false); session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); + m_ses.connection_failed(m_socket, remote(), "connection failed for unknown reason"); } @@ -2633,9 +2640,19 @@ namespace libtorrent time_duration d; d = time_now() - m_last_sent; if (total_seconds(d) < m_timeout / 2) return; - + if (m_connecting) return; + if (in_handshake()) return; + // if the last send has not completed yet, do not send a keep + // alive + if (m_writing) return; + +#ifdef TORRENT_VERBOSE_LOGGING + using namespace boost::posix_time; + (*m_logger) << time_now_string() << " ==> KEEPALIVE\n"; +#endif + write_keepalive(); } diff --git a/src/policy.cpp b/src/policy.cpp index b864e4d82..169616081 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -1021,7 +1021,10 @@ namespace libtorrent // the iterator is invalid // because of the push_back() i = boost::prior(m_peers.end()); - if (flags & 0x02) p.seed = true; +#ifndef TORRENT_DISABLE_ENCRYPTION + if (flags & 0x01) p.pe_support = true; +#endif + if (flags & 0x02) i->seed = true; // try to send a DHT ping to this peer // as well, to figure out if it supports @@ -1408,6 +1411,9 @@ namespace libtorrent policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t, int src) : ip(ip_) , type(t) +#ifndef TORRENT_DISABLE_ENCRYPTION + , pe_support(true) +#endif , failcount(0) , hashfails(0) , seed(false) diff --git a/src/session.cpp b/src/session.cpp index c527f5234..55c27cb05 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -252,6 +252,18 @@ namespace libtorrent #endif +#ifndef TORRENT_DISABLE_ENCRYPTION + void session::set_pe_settings(pe_settings const& settings) + { + m_impl->set_pe_settings(settings); + } + + pe_settings const& session::get_pe_settings() const + { + return m_impl->get_pe_settings(); + } +#endif + bool session::is_listening() const { return m_impl->is_listening(); diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 0a349a97e..b48e295fa 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1755,6 +1755,14 @@ namespace libtorrent { namespace detail #endif +#ifndef TORRENT_DISABLE_ENCRYPTION + void session_impl::set_pe_settings(pe_settings const& settings) + { + mutex_t::scoped_lock l(m_mutex); + m_pe_settings = settings; + } +#endif + void session_impl::set_download_rate_limit(int bytes_per_second) { assert(bytes_per_second > 0 || bytes_per_second == -1); diff --git a/src/ut_pex.cpp b/src/ut_pex.cpp index 586cf7ccb..41df77475 100644 --- a/src/ut_pex.cpp +++ b/src/ut_pex.cpp @@ -117,14 +117,21 @@ namespace libtorrent { namespace if (di == dropped.end()) { // don't write too big of a package - if (num_added >= max_peer_entries) continue; + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + bt_peer_connection* p = dynamic_cast(i->second); + if (!p) continue; // i->first was added since the last time detail::write_endpoint(i->first, pla_out); // no supported flags to set yet // 0x01 - peer supports encryption // 0x02 - peer is a seed - int flags = i->second->is_seed() ? 2 : 0; + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif detail::write_uint8(flags, plf_out); ++num_added; } @@ -280,14 +287,21 @@ namespace libtorrent { namespace if (!send_peer(*i->second)) continue; // don't write too big of a package - if (num_added >= max_peer_entries) continue; + if (num_added >= max_peer_entries) break; + + // only send proper bittorrent peers + bt_peer_connection* p = dynamic_cast(i->second); + if (!p) continue; // i->first was added since the last time detail::write_endpoint(i->first, pla_out); // no supported flags to set yet // 0x01 - peer supports encryption // 0x02 - peer is a seed - int flags = i->second->is_seed() ? 2 : 0; + int flags = p->is_seed() ? 2 : 0; +#ifndef TORRENT_DISABLE_ENCRYPTION + flags |= p->supports_encryption() ? 1 : 0; +#endif detail::write_uint8(flags, plf_out); ++num_added; } diff --git a/test/Jamfile b/test/Jamfile index 548ea76e2..baf63129c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -15,6 +15,7 @@ test-suite libtorrent : [ run test_storage.cpp ] [ run test_piece_picker.cpp ] # [ run test_entry.cpp ] + [ run test_pe_crypto.cpp ] [ run test_bencoding.cpp ] [ run test_primitives.cpp ] [ run test_ip_filter.cpp ] diff --git a/test/Makefile.am b/test/Makefile.am index ef51efc48..a0bfc2f92 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,4 @@ -noinst_PROGRAMS = test_hasher test_bencoding test_ip_filter test_piece_picker test_storage test_metadata_extension test_buffer test_allocate_resources +noinst_PROGRAMS = test_hasher test_bencoding test_ip_filter test_piece_picker test_storage test_metadata_extension test_buffer test_allocate_resources test_pe_crypto EXTRA_DIST = Jamfile test_hasher_SOURCES = main.cpp test_hasher.cpp @@ -25,6 +25,9 @@ test_allocate_resources_LDADD = $(top_builddir)/src/libtorrent.la test_metadata_extension_SOURCES = main.cpp setup_transfer.cpp test_metadata_extension.cpp test_metadata_extension_LDADD = $(top_builddir)/src/libtorrent.la +test_pe_crypto_SOURCES = main.cpp test_pe_crypto.cpp +test_pe_crypto_LDADD = $(top_builddir)/src/libtorrent.la $(top_builddir)/test/setup_transfer.o + noinst_HEADERS = test.hpp setup_transfer.hpp AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @DEBUGFLAGS@ @PTHREAD_CFLAGS@ diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp index f03ba3ed2..fee22d21e 100644 --- a/test/setup_transfer.cpp +++ b/test/setup_transfer.cpp @@ -22,7 +22,7 @@ void sleep(int msec) using namespace libtorrent; boost::tuple setup_transfer( - session& ses1, session& ses2, bool clear_files) + session& ses1, session& ses2, bool clear_files, bool use_metadata_transfer) { using namespace boost::filesystem; @@ -59,8 +59,12 @@ boost::tuple setup_transfer( // file pool will complain if two torrents are trying to // use the same files torrent_handle tor1 = ses1.add_torrent(t, "./tmp1"); - torrent_handle tor2 = ses2.add_torrent(tracker_url + torrent_handle tor2; + if (use_metadata_transfer) + tor2 = ses2.add_torrent(tracker_url , t.info_hash(), 0, "./tmp2"); + else + tor2 = ses2.add_torrent(t, "./tmp2"); sleep(100); diff --git a/test/setup_transfer.hpp b/test/setup_transfer.hpp index dcd8a5670..1d85c672d 100644 --- a/test/setup_transfer.hpp +++ b/test/setup_transfer.hpp @@ -8,7 +8,7 @@ void sleep(int msec); boost::tuple setup_transfer(libtorrent::session& ses1, libtorrent::session& ses2 - , bool clear_files); + , bool clear_files, bool use_metadata_transfer = true); #endif diff --git a/test/test_pe_crypto.cpp b/test/test_pe_crypto.cpp new file mode 100644 index 000000000..144ee3664 --- /dev/null +++ b/test/test_pe_crypto.cpp @@ -0,0 +1,194 @@ +/* + +Copyright (c) 2007, Un Shyam +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 +#include + +#include "libtorrent/hasher.hpp" +#include "libtorrent/pe_crypto.hpp" +#include "libtorrent/session.hpp" + +#include "setup_transfer.hpp" +#include "test.hpp" + +void display_pe_policy(libtorrent::pe_settings::enc_policy policy) +{ + using namespace libtorrent; + using std::cerr; + + if (policy == pe_settings::disabled) cerr << "disabled "; + else if (policy == pe_settings::enabled) cerr << "enabled "; + else if (policy == pe_settings::forced) cerr << "forced "; +} + +void display_pe_settings(libtorrent::pe_settings s) +{ + using namespace libtorrent; + using std::cerr; + + cerr << "out_enc_policy - "; + display_pe_policy(s.out_enc_policy); + cerr << "\tin_enc_policy - "; + display_pe_policy(s.in_enc_policy); + + cerr << "\nenc_level - "; + if (s.allowed_enc_level == pe_settings::plaintext) cerr << "plaintext "; + else if (s.allowed_enc_level == pe_settings::rc4) cerr << "rc4 "; + else if (s.allowed_enc_level == pe_settings::both) cerr << "both "; + + cerr << "\t\tprefer_rc4 - "; + (s.prefer_rc4) ? cerr << "true" : cerr << "false"; + cerr << "\n\n"; +} + +void test_transfer(libtorrent::pe_settings::enc_policy policy, + libtorrent::pe_settings::enc_level level = libtorrent::pe_settings::both, + bool pref_rc4 = false) +{ + using namespace libtorrent; + using std::cerr; + + session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000)); + session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000)); + pe_settings s; + + s.out_enc_policy = libtorrent::pe_settings::enabled; + s.in_enc_policy = libtorrent::pe_settings::enabled; + + s.allowed_enc_level = pe_settings::both; + ses2.set_pe_settings(s); + + s.out_enc_policy = policy; + s.in_enc_policy = policy; + s.allowed_enc_level = level; + s.prefer_rc4 = pref_rc4; + ses1.set_pe_settings(s); + +// s = ses1.get_pe_settings(); +// cerr << " Session1 \n"; +// display_pe_settings(s); +// s = ses2.get_pe_settings(); +// cerr << " Session2 \n"; +// display_pe_settings(s); + + torrent_handle tor1; + torrent_handle tor2; + + boost::tie(tor1, tor2) = setup_transfer(ses1, ses2, true, false); + + std::cerr << "waiting for transfer to complete\n"; + + for (int i = 0; i < 50; ++i) + { + tor2.status(); + std::auto_ptr a; + a = ses1.pop_alert(); + if (a.get()) + std::cerr << "ses1: " << a->msg() << "\n"; + + a = ses2.pop_alert(); + if (a.get()) + std::cerr << "ses2: " << a->msg() << "\n"; + + if (tor2.is_seed()) break; + sleep(100); + } + + TEST_CHECK(tor2.is_seed()); + if (tor2.is_seed()) std::cerr << "done\n"; +} + + +int test_main() +{ + using namespace libtorrent; + int repcount = 64; + + for (int rep = 0; rep < repcount; ++rep) + { + DH_key_exchange DH1, DH2; + + DH1.compute_secret(DH2.get_local_key()); + DH2.compute_secret(DH1.get_local_key()); + + TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret())); + } + + DH_key_exchange DH1, DH2; + DH1.compute_secret(DH2.get_local_key()); + DH2.compute_secret(DH1.get_local_key()); + + TEST_CHECK(std::equal(DH1.get_secret(), DH1.get_secret() + 96, DH2.get_secret())); + + sha1_hash test1_key = hasher("test1_key",8).final(); + sha1_hash test2_key = hasher("test2_key",8).final(); + + RC4_handler RC41 (test2_key, test1_key); + RC4_handler RC42 (test1_key, test2_key); + + for (int rep = 0; rep < repcount; ++rep) + { + std::size_t buf_len = rand() % (512 * 1024); + char* buf = new char[buf_len]; + char* zero_buf = new char[buf_len]; + + std::fill(buf, buf + buf_len, 0); + std::fill(zero_buf, zero_buf + buf_len, 0); + + RC41.encrypt(buf, buf_len); + RC42.decrypt(buf, buf_len); + TEST_CHECK(std::equal(buf, buf + buf_len, zero_buf)); + + RC42.encrypt(buf, buf_len); + RC41.decrypt(buf, buf_len); + TEST_CHECK(std::equal(buf, buf + buf_len, zero_buf)); + + delete[] buf; + delete[] zero_buf; + } + + + test_transfer(pe_settings::disabled); + + test_transfer(pe_settings::forced, pe_settings::plaintext); + test_transfer(pe_settings::forced, pe_settings::rc4); + test_transfer(pe_settings::forced, pe_settings::both, false); + test_transfer(pe_settings::forced, pe_settings::both, true); + + test_transfer(pe_settings::enabled, pe_settings::plaintext); + test_transfer(pe_settings::enabled, pe_settings::rc4); + test_transfer(pe_settings::enabled, pe_settings::both, false); + test_transfer(pe_settings::enabled, pe_settings::both, true); + + return 0; +} +