initial BitTorrent over SSL support
This commit is contained in:
parent
90372b6caf
commit
469414d486
|
@ -2310,6 +2310,11 @@ Its declaration looks like this::
|
|||
|
||||
sha1_hash info_hash() const;
|
||||
|
||||
void set_ssl_certificate(std::string const& cert
|
||||
, std::string const& private_key
|
||||
, std::string const& dh_params
|
||||
, std::string const& passphrase = "");
|
||||
|
||||
bool operator==(torrent_handle const&) const;
|
||||
bool operator!=(torrent_handle const&) const;
|
||||
bool operator<(torrent_handle const&) const;
|
||||
|
@ -3300,6 +3305,35 @@ somehow invalid or if the filenames are not allowed (and hence cannot be opened/
|
|||
your filesystem. If such an error occurs, a file_error_alert_ is generated and all handles
|
||||
that refers to that torrent will become invalid.
|
||||
|
||||
set_ssl_certificate()
|
||||
---------------------
|
||||
|
||||
::
|
||||
|
||||
void set_ssl_certificate(std::string const& cert, std::string const& private_key
|
||||
, std::string const& dh_params, std::string const& passphrase = "");
|
||||
|
||||
For SSL torrents, use this to specify a path to a .pem file to use as this client's certificate.
|
||||
The certificate must be signed by the certificate in the .torrent file to be valid.
|
||||
|
||||
``cert`` is a path to the (signed) certificate in .pem format corresponding to this torrent.
|
||||
|
||||
``private_key`` is a path to the private key for the specified certificate. This must be in .pem
|
||||
format.
|
||||
|
||||
``dh_params`` is a path to the Diffie-Hellman parameter file, which needs to be in .pem format.
|
||||
You can generate this file using the openssl command like this:
|
||||
``openssl dhparam -outform PEM -out dhparams.pem 512``.
|
||||
|
||||
``passphrase`` may be specified if the private key is encrypted and requires a passphrase to
|
||||
be decrypted.
|
||||
|
||||
Note that when a torrent first starts up, and it needs a certificate, it will suspend connecting
|
||||
to any peers until it has one. It's typically desirable to resume the torrent after setting the
|
||||
ssl certificate.
|
||||
|
||||
If you receive a torrent_need_cert_alert_, you need to call this to provide a valid cert. If you
|
||||
don't have a cert you won't be allowed to connect to any peers.
|
||||
|
||||
torrent_status
|
||||
==============
|
||||
|
@ -3423,6 +3457,8 @@ It contains the following fields::
|
|||
bool ip_filter_applies;
|
||||
|
||||
sha1_hash info_hash;
|
||||
|
||||
int listen_port;
|
||||
};
|
||||
|
||||
``handle`` is a handle to the torrent whose status the object represents.
|
||||
|
@ -3702,6 +3738,11 @@ to this torrent. This defaults to true.
|
|||
|
||||
``info_hash`` is the info-hash of the torrent.
|
||||
|
||||
``listen_port`` is the listen port this torrent is listening on for new
|
||||
connections, if the torrent has its own listen socket. Only SSL torrents
|
||||
have their own listen sockets. If the torrent doesn't have one, and is
|
||||
accepting connections on the single listen socket, this is 0.
|
||||
|
||||
peer_info
|
||||
=========
|
||||
|
||||
|
@ -7128,6 +7169,21 @@ cache flush is complete and the torrent does no longer have any files open.
|
|||
// ...
|
||||
};
|
||||
|
||||
torrent_need_cert_alert
|
||||
-----------------------
|
||||
|
||||
This is always posted for SSL torrents. This is a reminder to the client that
|
||||
the torrent won't work unless torrent_handle::set_ssl_certificate() is called with
|
||||
a valid certificate. Valid certificates MUST be signed by the SSL certificate
|
||||
in the .torrent file.
|
||||
|
||||
::
|
||||
|
||||
struct torrent_need_cert_alert: tracker_alert
|
||||
{
|
||||
// ...
|
||||
};
|
||||
|
||||
dht_announce_alert
|
||||
------------------
|
||||
|
||||
|
@ -8844,6 +8900,39 @@ This threshold is controlled by ``session_settings::whole_pieces_threshold``.
|
|||
*TODO: piece affinity by speed category*
|
||||
*TODO: piece priorities*
|
||||
|
||||
SSL torrents
|
||||
============
|
||||
|
||||
Torrents may have an SSL root (CA) certificate embedded in them. Such torrents
|
||||
are called *SSL torrents*. An SSL torrent talks to all bittorrent peers over SSL.
|
||||
The protocols are layered like this::
|
||||
|
||||
+-----------------------+
|
||||
| BitTorrent protocol |
|
||||
+-----------------------+
|
||||
| SSL |
|
||||
+-----------+-----------+
|
||||
| TCP | uTP |
|
||||
| +-----------+
|
||||
| | UDP |
|
||||
+-----------+-----------+
|
||||
|
||||
During the SSL handshake, both peers need to authenticate by providing a certificate
|
||||
that is signed by the private counterpart of the CA certificate found in the
|
||||
.torrent file. These peer certificates are expected to be privided to peers through
|
||||
some other means than bittorrent. Typically by a peer generating a certificate request
|
||||
which is sent to the publisher of the torrent, and the publisher returning a signed
|
||||
certificate.
|
||||
|
||||
In libtorrent, `set_ssl_certificate()`_ in torrent_handle_ is used to tell libtorrent where
|
||||
to find the peer certificate and the private key for it. When an SSL torrent is loaded,
|
||||
the torrent_need_cert_alert_ is posted to remind the user to provide a certificate.
|
||||
|
||||
In order for the client to know which torrent an incoming connection belongs to, in order
|
||||
to provide the correct certificate, each SSL torrent opens their own dedicated listen socket.
|
||||
|
||||
This feature is only available if libtorrent is build with openssl support (``TORRENT_USE_OPENSSL``).
|
||||
|
||||
filename checks
|
||||
===============
|
||||
|
||||
|
|
|
@ -850,6 +850,40 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a
|
|||
{
|
||||
using namespace libtorrent;
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (torrent_need_cert_alert* p = alert_cast<torrent_need_cert_alert>(a))
|
||||
{
|
||||
torrent_handle h = p->handle;
|
||||
error_code ec;
|
||||
file_status st;
|
||||
std::string cert = combine_path("certificates", to_hex(h.info_hash().to_string())) + ".pem";
|
||||
std::string priv = combine_path("certificates", to_hex(h.info_hash().to_string())) + "_key.pem";
|
||||
stat_file(cert, &st, ec);
|
||||
if (ec)
|
||||
{
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "ERROR. could not load certificate %s: %s\n", cert.c_str(), ec.message().c_str());
|
||||
if (g_log_file) fprintf(g_log_file, "[%s] %s\n", time_now_string(), msg);
|
||||
return;
|
||||
}
|
||||
stat_file(priv, &st, ec);
|
||||
if (ec)
|
||||
{
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "ERROR. could not load private key %s: %s\n", priv.c_str(), ec.message().c_str());
|
||||
if (g_log_file) fprintf(g_log_file, "[%s] %s\n", time_now_string(), msg);
|
||||
return;
|
||||
}
|
||||
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "loaded certificate %s and key %s\n", cert.c_str(), priv.c_str());
|
||||
if (g_log_file) fprintf(g_log_file, "[%s] %s\n", time_now_string(), msg);
|
||||
|
||||
h.set_ssl_certificate(cert, priv, "certificates/dhparams.pem", "test");
|
||||
h.resume();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (torrent_finished_alert* p = alert_cast<torrent_finished_alert>(a))
|
||||
{
|
||||
p->handle.set_max_connections(max_connections_per_torrent / 2);
|
||||
|
|
|
@ -1263,6 +1263,21 @@ namespace libtorrent
|
|||
error_code error;
|
||||
};
|
||||
|
||||
struct TORRENT_EXPORT torrent_need_cert_alert: torrent_alert
|
||||
{
|
||||
torrent_need_cert_alert(torrent_handle const& h)
|
||||
: torrent_alert(h)
|
||||
{}
|
||||
|
||||
TORRENT_DEFINE_ALERT(torrent_need_cert_alert);
|
||||
|
||||
const static int static_category = alert::status_notification;
|
||||
virtual std::string message() const;
|
||||
virtual bool discardable() const { return false; }
|
||||
|
||||
error_code error;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,27 @@ namespace libtorrent
|
|||
|
||||
struct bencode_map_entry;
|
||||
|
||||
struct listen_socket_t
|
||||
{
|
||||
listen_socket_t(): external_port(0) {}
|
||||
|
||||
// this is typically empty but can be set
|
||||
// to the WAN IP address of NAT-PMP or UPnP router
|
||||
address external_address;
|
||||
|
||||
// this is typically set to the same as the local
|
||||
// listen port. In case a NAT port forward was
|
||||
// successfully opened, this will be set to the
|
||||
// port that is open on the external (NAT) interface
|
||||
// on the NAT box itself. This is the port that has
|
||||
// to be published to peers, since this is the port
|
||||
// the client is reachable through.
|
||||
int external_port;
|
||||
|
||||
// the actual socket
|
||||
boost::shared_ptr<socket_acceptor> sock;
|
||||
};
|
||||
|
||||
namespace aux
|
||||
{
|
||||
struct session_impl;
|
||||
|
@ -349,7 +370,7 @@ namespace libtorrent
|
|||
|
||||
torrent_handle find_torrent_handle(sha1_hash const& info_hash);
|
||||
|
||||
void announce_lsd(sha1_hash const& ih, bool broadcast = false);
|
||||
void announce_lsd(sha1_hash const& ih, int port, bool broadcast = false);
|
||||
|
||||
void save_state(entry* e, boost::uint32_t flags) const;
|
||||
void load_state(lazy_entry const* e);
|
||||
|
@ -661,26 +682,6 @@ namespace libtorrent
|
|||
tcp::endpoint m_ipv6_interface;
|
||||
tcp::endpoint m_ipv4_interface;
|
||||
|
||||
struct listen_socket_t
|
||||
{
|
||||
listen_socket_t(): external_port(0) {}
|
||||
|
||||
// this is typically empty but can be set
|
||||
// to the WAN IP address of NAT-PMP or UPnP router
|
||||
address external_address;
|
||||
|
||||
// this is typically set to the same as the local
|
||||
// listen port. In case a NAT port forward was
|
||||
// successfully opened, this will be set to the
|
||||
// port that is open on the external (NAT) interface
|
||||
// on the NAT box itself. This is the port that has
|
||||
// to be published to peers, since this is the port
|
||||
// the client is reachable through.
|
||||
int external_port;
|
||||
|
||||
// the actual socket
|
||||
boost::shared_ptr<socket_acceptor> sock;
|
||||
};
|
||||
// since we might be listening on multiple interfaces
|
||||
// we might need more than one listen socket
|
||||
std::list<listen_socket_t> m_listen_sockets;
|
||||
|
@ -697,7 +698,7 @@ namespace libtorrent
|
|||
boost::shared_ptr<socket_type> m_i2p_listen_socket;
|
||||
#endif
|
||||
|
||||
listen_socket_t setup_listener(tcp::endpoint ep, int retries
|
||||
void setup_listener(listen_socket_t* s, tcp::endpoint ep, int retries
|
||||
, bool v6_only, int flags, error_code& ec);
|
||||
|
||||
// the proxy used for bittorrent
|
||||
|
|
|
@ -90,7 +90,8 @@ namespace libtorrent
|
|||
, boost::weak_ptr<torrent> t
|
||||
, boost::shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& remote
|
||||
, policy::peer* peerinfo);
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing = true);
|
||||
|
||||
// with this constructor we have been contacted and we still don't
|
||||
// know which torrent the connection belongs to
|
||||
|
@ -98,7 +99,8 @@ namespace libtorrent
|
|||
aux::session_impl& ses
|
||||
, boost::shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& remote
|
||||
, policy::peer* peerinfo);
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing = false);
|
||||
|
||||
void start();
|
||||
|
||||
|
|
|
@ -171,7 +171,8 @@ namespace libtorrent
|
|||
, boost::weak_ptr<torrent> t
|
||||
, boost::shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& remote
|
||||
, policy::peer* peerinfo);
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing = true);
|
||||
|
||||
// with this constructor we have been contacted and we still don't
|
||||
// know which torrent the connection belongs to
|
||||
|
@ -179,7 +180,8 @@ namespace libtorrent
|
|||
aux::session_impl& ses
|
||||
, boost::shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& remote
|
||||
, policy::peer* peerinfo);
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing = false);
|
||||
|
||||
// this function is called after it has been constructed and properly
|
||||
// reference counted. It is safe to call self() in this function
|
||||
|
|
|
@ -94,6 +94,7 @@ namespace libtorrent
|
|||
struct add_torrent_params;
|
||||
struct storage_interface;
|
||||
class bt_peer_connection;
|
||||
struct listen_socket_t;
|
||||
|
||||
namespace aux
|
||||
{
|
||||
|
@ -839,7 +840,10 @@ namespace libtorrent
|
|||
void dequeue_torrent_check();
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
void set_ssl_cert(std::string const& certificate, error_code& ec);
|
||||
void set_ssl_cert(std::string const& certificate
|
||||
, std::string const& private_key
|
||||
, std::string const& dh_params
|
||||
, std::string const& passphrase);
|
||||
bool is_ssl_torrent() const { return m_ssl_ctx; }
|
||||
boost::asio::ssl::context* ssl_ctx() const { return m_ssl_ctx.get(); }
|
||||
#endif
|
||||
|
@ -928,7 +932,27 @@ namespace libtorrent
|
|||
piece_manager* m_storage;
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
// TODO: in order to save space, stick the ssl context
|
||||
// and the listen_socket_t in a single struct and have
|
||||
// a shared_pointer to that (or intrusive_ptr even)
|
||||
|
||||
boost::shared_ptr<asio::ssl::context> m_ssl_ctx;
|
||||
|
||||
// listen socket used to accept incoming ssl connections
|
||||
boost::shared_ptr<listen_socket_t> m_ssl_acceptor;
|
||||
|
||||
// SSL torrents have their own listen socket, so that
|
||||
// we know which certificate to use for incoming connections
|
||||
// these function are used for handling the torrent specific
|
||||
// listen socket
|
||||
void async_accept(boost::shared_ptr<socket_acceptor> const& listener);
|
||||
|
||||
void on_accept_ssl_connection(boost::shared_ptr<socket_type> const& s
|
||||
, boost::weak_ptr<socket_acceptor> listen_socket, error_code const& e);
|
||||
|
||||
void ssl_handshake(error_code const& ec, boost::shared_ptr<socket_type> s);
|
||||
|
||||
void init_ssl(std::string const& cert);
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
|
|
|
@ -252,8 +252,10 @@ namespace libtorrent
|
|||
bool resolve_countries() const;
|
||||
#endif
|
||||
|
||||
void set_ssl_certificates(std::string const& certificate
|
||||
, error_code& ec);
|
||||
void set_ssl_certificate(std::string const& certificate
|
||||
, std::string const& private_key
|
||||
, std::string const& dh_params
|
||||
, std::string const& passphrase = "");
|
||||
|
||||
storage_interface* get_storage_impl() const;
|
||||
|
||||
|
@ -485,6 +487,7 @@ namespace libtorrent
|
|||
, need_save_resume(false)
|
||||
, ip_filter_applies(true)
|
||||
, info_hash(0)
|
||||
, listen_port(0)
|
||||
{}
|
||||
|
||||
// handle to the torrent
|
||||
|
@ -701,6 +704,10 @@ namespace libtorrent
|
|||
|
||||
// the info-hash for this torrent
|
||||
sha1_hash info_hash;
|
||||
|
||||
// if this torrent has its own listen socket, this is
|
||||
// the port it's listening on. Otherwise it's 0
|
||||
int listen_port;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -561,5 +561,10 @@ namespace libtorrent {
|
|||
return torrent_alert::message() + " removed";
|
||||
}
|
||||
|
||||
std::string torrent_need_cert_alert::message() const
|
||||
{
|
||||
return torrent_alert::message() + " needs SSL certificate";
|
||||
}
|
||||
|
||||
} // namespace libtorrent
|
||||
|
||||
|
|
|
@ -99,9 +99,10 @@ namespace libtorrent
|
|||
, boost::weak_ptr<torrent> tor
|
||||
, shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& remote
|
||||
, policy::peer* peerinfo)
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing)
|
||||
: peer_connection(ses, tor, s, remote
|
||||
, peerinfo)
|
||||
, peerinfo, outgoing)
|
||||
, m_state(read_protocol_identifier)
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
, m_upload_only_id(0)
|
||||
|
@ -152,8 +153,9 @@ namespace libtorrent
|
|||
session_impl& ses
|
||||
, boost::shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& remote
|
||||
, policy::peer* peerinfo)
|
||||
: peer_connection(ses, s, remote, peerinfo)
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing)
|
||||
: peer_connection(ses, s, remote, peerinfo, outgoing)
|
||||
, m_state(read_protocol_identifier)
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
, m_upload_only_id(0)
|
||||
|
@ -225,6 +227,10 @@ namespace libtorrent
|
|||
boost::shared_ptr<torrent> t = associated_torrent().lock();
|
||||
std::string const key = t->torrent_file().encryption_key();
|
||||
if (key.size() == 32) out_enc_policy = pe_settings::disabled;
|
||||
|
||||
// never try an encrypted connection when already using SSL
|
||||
if (get_socket()->get<ssl_stream<stream_socket> >() || get_socket()->get<ssl_stream<utp_stream> >())
|
||||
out_enc_policy = pe_settings::disabled;
|
||||
#endif
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
char const* policy_name[] = {"forced", "enabled", "disabled"};
|
||||
|
@ -2937,6 +2943,18 @@ namespace libtorrent
|
|||
peer_log("*** unrecognized protocol header");
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (get_socket()->get<ssl_stream<stream_socket> >()
|
||||
|| get_socket()->get<ssl_stream<utp_stream> >())
|
||||
{
|
||||
#ifdef TORRENT_VERBOSE_LOGGING
|
||||
peer_log("*** SSL peers are not allowed to use any other encryption");
|
||||
#endif
|
||||
disconnect(errors::invalid_info_hash, 1);
|
||||
return;
|
||||
}
|
||||
#endif // TORRENT_USE_OPENSSL
|
||||
|
||||
bool found_encrypted_torrent = false;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (!is_local())
|
||||
|
@ -3078,7 +3096,13 @@ namespace libtorrent
|
|||
std::copy(recv_buffer.begin + 8, recv_buffer.begin + 28
|
||||
, (char*)info_hash.begin());
|
||||
|
||||
attach_to_torrent(info_hash, m_encrypted && m_rc4_encrypted);
|
||||
#ifndef TORRENT_DISABLE_ENCRYPTION
|
||||
bool allow_encrypted = m_encrypted && m_rc4_encrypted;
|
||||
#else
|
||||
bool allow_encrypted = true;
|
||||
#endif
|
||||
|
||||
attach_to_torrent(info_hash, allow_encrypted);
|
||||
if (is_disconnecting()) return;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -87,6 +87,10 @@ http_connection::http_connection(io_service& ios, connection_queue& cc
|
|||
, m_abort(false)
|
||||
{
|
||||
TORRENT_ASSERT(!m_handler.empty());
|
||||
// TODO: if we were handed an SSL context, we should really
|
||||
// verify the hostname of the web server as well. This is supported
|
||||
// in boost starting with version 1.47.0. See ssl::rfc2818_verification
|
||||
// and ssl::context::set_verify_callback
|
||||
}
|
||||
|
||||
http_connection::~http_connection()
|
||||
|
|
|
@ -79,7 +79,8 @@ namespace libtorrent
|
|||
, boost::weak_ptr<torrent> tor
|
||||
, shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& endp
|
||||
, policy::peer* peerinfo)
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing)
|
||||
:
|
||||
#ifdef TORRENT_DEBUG
|
||||
m_last_choke(time_now() - hours(1))
|
||||
|
@ -139,7 +140,7 @@ namespace libtorrent
|
|||
, m_choke_rejects(0)
|
||||
, m_read_recurse(0)
|
||||
, m_fast_reconnect(false)
|
||||
, m_active(true)
|
||||
, m_active(outgoing)
|
||||
, m_peer_interested(false)
|
||||
, m_peer_choked(true)
|
||||
, m_interesting(false)
|
||||
|
@ -149,8 +150,8 @@ namespace libtorrent
|
|||
, m_ignore_unchoke_slots(false)
|
||||
, m_have_all(false)
|
||||
, m_disconnecting(false)
|
||||
, m_connecting(true)
|
||||
, m_queued(true)
|
||||
, m_connecting(outgoing)
|
||||
, m_queued(outgoing)
|
||||
, m_request_large_blocks(false)
|
||||
, m_share_mode(false)
|
||||
, m_upload_only(false)
|
||||
|
@ -203,9 +204,12 @@ namespace libtorrent
|
|||
error_code ec;
|
||||
m_logger = m_ses.create_log(m_remote.address().to_string(ec) + "_"
|
||||
+ to_string(m_remote.port()).elems, m_ses.listen_port());
|
||||
peer_log(">>> OUTGOING_CONNECTION [ ep: %s transport: %s seed: %d p: %p]"
|
||||
peer_log(">>> %s [ ep: %s transport: %s seed: %d p: %p ]"
|
||||
, outgoing ? "OUTGOING_CONNECTION" : "INCOMING CONNECTION"
|
||||
, print_endpoint(m_remote).c_str()
|
||||
, (m_socket->get<utp_stream>()) ? "uTP connection" : "TCP connection"
|
||||
, m_socket->get<ssl_stream<stream_socket> >() ? "SSL/TCP"
|
||||
: m_socket->get<ssl_stream<utp_stream> >() ? "SSL/uTP"
|
||||
: m_socket->get<utp_stream>() ? "uTP" : "TCP"
|
||||
, m_peer_info ? m_peer_info->seed : 0, m_peer_info);
|
||||
#endif
|
||||
#ifdef TORRENT_DEBUG
|
||||
|
@ -223,7 +227,8 @@ namespace libtorrent
|
|||
session_impl& ses
|
||||
, shared_ptr<socket_type> s
|
||||
, tcp::endpoint const& endp
|
||||
, policy::peer* peerinfo)
|
||||
, policy::peer* peerinfo
|
||||
, bool outgoing)
|
||||
:
|
||||
#ifdef TORRENT_DEBUG
|
||||
m_last_choke(time_now() - hours(1))
|
||||
|
@ -282,7 +287,7 @@ namespace libtorrent
|
|||
, m_choke_rejects(0)
|
||||
, m_read_recurse(0)
|
||||
, m_fast_reconnect(false)
|
||||
, m_active(false)
|
||||
, m_active(outgoing)
|
||||
, m_peer_interested(false)
|
||||
, m_peer_choked(true)
|
||||
, m_interesting(false)
|
||||
|
@ -292,8 +297,8 @@ namespace libtorrent
|
|||
, m_ignore_unchoke_slots(false)
|
||||
, m_have_all(false)
|
||||
, m_disconnecting(false)
|
||||
, m_connecting(false)
|
||||
, m_queued(false)
|
||||
, m_connecting(outgoing)
|
||||
, m_queued(outgoing)
|
||||
, m_request_large_blocks(false)
|
||||
, m_share_mode(false)
|
||||
, m_upload_only(false)
|
||||
|
@ -347,7 +352,8 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_socket->remote_endpoint(ec) == m_remote || ec);
|
||||
m_logger = m_ses.create_log(remote().address().to_string(ec) + "_"
|
||||
+ to_string(remote().port()).elems, m_ses.listen_port());
|
||||
peer_log("<<< INCOMING_CONNECTION [ ep: %s transport: %s ]"
|
||||
peer_log("<<< %s [ ep: %s transport: %s ]"
|
||||
, outgoing ? "OUTGOING_CONNECTION" : "INCOMING CONNECTION"
|
||||
, print_endpoint(m_remote).c_str()
|
||||
, (m_socket->get<utp_stream>()) ? "uTP connection" : "TCP connection");
|
||||
#endif
|
||||
|
@ -536,7 +542,7 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_peer_info == 0 || m_peer_info->connection == this);
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||
|
||||
if (!t)
|
||||
if (!m_active)
|
||||
{
|
||||
tcp::socket::non_blocking_io ioc(true);
|
||||
error_code ec;
|
||||
|
@ -556,7 +562,8 @@ namespace libtorrent
|
|||
if (m_remote.address().is_v4())
|
||||
m_socket->set_option(type_of_service(m_ses.settings().peer_tos), ec);
|
||||
}
|
||||
else if (t->ready_for_connections())
|
||||
|
||||
if (t && t->ready_for_connections())
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
@ -1132,6 +1139,16 @@ namespace libtorrent
|
|||
t.reset();
|
||||
}
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (t && t->is_ssl_torrent())
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
peer_log("*** can't attach to an ssl torrent");
|
||||
#endif
|
||||
t.reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!t)
|
||||
{
|
||||
// we couldn't find the torrent!
|
||||
|
@ -3320,7 +3337,7 @@ namespace libtorrent
|
|||
peer_log("CONNECTION FAILED: %s", print_endpoint(m_remote).c_str());
|
||||
#endif
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
(*m_ses.m_logger) << "CONNECTION FAILED: " << print_endpoint(m_remote) << "\n";
|
||||
(*m_ses.m_logger) << time_now_string() << " CONNECTION FAILED: " << print_endpoint(m_remote) << "\n";
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
|
@ -5234,7 +5251,7 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
#if defined TORRENT_VERBOSE_LOGGING
|
||||
peer_log(">>> ASYNC_CONENCT [ dst: %s ]", print_endpoint(m_remote).c_str());
|
||||
peer_log(">>> ASYNC_CONNECT [ dst: %s ]", print_endpoint(m_remote).c_str());
|
||||
#endif
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("peer_connection::on_connection_complete");
|
||||
|
@ -5279,7 +5296,11 @@ namespace libtorrent
|
|||
if (m_disconnecting) return;
|
||||
m_last_receive = time_now();
|
||||
|
||||
if (m_socket->get<utp_stream>() && m_peer_info)
|
||||
if ((m_socket->get<utp_stream>()
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
|| m_socket->get<ssl_stream<utp_stream> >()
|
||||
#endif
|
||||
) && m_peer_info)
|
||||
{
|
||||
m_peer_info->confirmed_supports_utp = true;
|
||||
m_peer_info->supports_utp = false;
|
||||
|
|
|
@ -1771,41 +1771,40 @@ namespace aux {
|
|||
return m_ipv4_interface;
|
||||
}
|
||||
|
||||
session_impl::listen_socket_t session_impl::setup_listener(tcp::endpoint ep
|
||||
void session_impl::setup_listener(listen_socket_t* s, tcp::endpoint ep
|
||||
, int retries, bool v6_only, int flags, error_code& ec)
|
||||
{
|
||||
listen_socket_t s;
|
||||
s.sock.reset(new socket_acceptor(m_io_service));
|
||||
s.sock->open(ep.protocol(), ec);
|
||||
s->sock.reset(new socket_acceptor(m_io_service));
|
||||
s->sock->open(ep.protocol(), ec);
|
||||
if (ec)
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
(*m_logger) << "failed to open socket: " << print_endpoint(ep)
|
||||
<< ": " << ec.message() << "\n" << "\n";
|
||||
#endif
|
||||
return listen_socket_t();
|
||||
return;
|
||||
}
|
||||
if (flags & session::listen_reuse_address)
|
||||
{
|
||||
error_code err; // ignore errors here
|
||||
s.sock->set_option(socket_acceptor::reuse_address(true), err);
|
||||
s->sock->set_option(socket_acceptor::reuse_address(true), err);
|
||||
}
|
||||
#if TORRENT_USE_IPV6
|
||||
if (ep.protocol() == tcp::v6())
|
||||
{
|
||||
error_code err; // ignore errors here
|
||||
s.sock->set_option(v6only(v6_only), err);
|
||||
s->sock->set_option(v6only(v6_only), err);
|
||||
#ifdef TORRENT_WINDOWS
|
||||
|
||||
#ifndef PROTECTION_LEVEL_UNRESTRICTED
|
||||
#define PROTECTION_LEVEL_UNRESTRICTED 10
|
||||
#endif
|
||||
// enable Teredo on windows
|
||||
s.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err);
|
||||
s->sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
s.sock->bind(ep, ec);
|
||||
s->sock->bind(ep, ec);
|
||||
while (ec && retries > 0)
|
||||
{
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
|
@ -1818,7 +1817,7 @@ namespace aux {
|
|||
TORRENT_ASSERT_VAL(!ec, ec);
|
||||
--retries;
|
||||
ep.port(ep.port() + 1);
|
||||
s.sock->bind(ep, ec);
|
||||
s->sock->bind(ep, ec);
|
||||
}
|
||||
if (ec && !(flags & session::listen_no_system_port))
|
||||
{
|
||||
|
@ -1826,7 +1825,7 @@ namespace aux {
|
|||
// let the OS pick a port
|
||||
ep.port(0);
|
||||
ec = error_code();
|
||||
s.sock->bind(ep, ec);
|
||||
s->sock->bind(ep, ec);
|
||||
}
|
||||
if (ec)
|
||||
{
|
||||
|
@ -1839,10 +1838,10 @@ namespace aux {
|
|||
, print_endpoint(ep).c_str(), ec.message().c_str());
|
||||
(*m_logger) << time_now_string() << msg << "\n";
|
||||
#endif
|
||||
return listen_socket_t();
|
||||
return;
|
||||
}
|
||||
s.external_port = s.sock->local_endpoint(ec).port();
|
||||
if (!ec) s.sock->listen(m_settings.listen_queue_size, ec);
|
||||
s->external_port = s->sock->local_endpoint(ec).port();
|
||||
if (!ec) s->sock->listen(m_settings.listen_queue_size, ec);
|
||||
if (ec)
|
||||
{
|
||||
if (m_alerts.should_post<listen_failed_alert>())
|
||||
|
@ -1853,22 +1852,21 @@ namespace aux {
|
|||
, print_endpoint(ep).c_str(), ec.message().c_str());
|
||||
(*m_logger) << time_now_string() << msg << "\n";
|
||||
#endif
|
||||
return listen_socket_t();
|
||||
return;
|
||||
}
|
||||
|
||||
// if we asked the system to listen on port 0, which
|
||||
// socket did it end up choosing?
|
||||
if (ep.port() == 0)
|
||||
ep.port(s.sock->local_endpoint().port());
|
||||
ep.port(s->sock->local_endpoint().port());
|
||||
|
||||
if (m_alerts.should_post<listen_succeeded_alert>())
|
||||
m_alerts.post_alert(listen_succeeded_alert(ep));
|
||||
|
||||
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING
|
||||
(*m_logger) << time_now_string() << " listening on: " << ep
|
||||
<< " external port: " << s.external_port << "\n";
|
||||
<< " external port: " << s->external_port << "\n";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
void session_impl::open_listen_port(int flags, error_code& ec)
|
||||
|
@ -1887,8 +1885,8 @@ namespace aux {
|
|||
// this means we should open two listen sockets
|
||||
// one for IPv4 and one for IPv6
|
||||
|
||||
listen_socket_t s = setup_listener(
|
||||
tcp::endpoint(address_v4::any(), m_listen_interface.port())
|
||||
listen_socket_t s;
|
||||
setup_listener(&s, tcp::endpoint(address_v4::any(), m_listen_interface.port())
|
||||
, m_listen_port_retries, false, flags, ec);
|
||||
|
||||
if (s.sock)
|
||||
|
@ -1906,8 +1904,7 @@ namespace aux {
|
|||
// only try to open the IPv6 port if IPv6 is installed
|
||||
if (supports_ipv6())
|
||||
{
|
||||
s = setup_listener(
|
||||
tcp::endpoint(address_v6::any(), m_listen_interface.port())
|
||||
setup_listener(&s, tcp::endpoint(address_v6::any(), m_listen_interface.port())
|
||||
, m_listen_port_retries, true, flags, ec);
|
||||
|
||||
if (s.sock)
|
||||
|
@ -1936,8 +1933,8 @@ namespace aux {
|
|||
// we should only open a single listen socket, that
|
||||
// binds to the given interface
|
||||
|
||||
listen_socket_t s = setup_listener(
|
||||
m_listen_interface, m_listen_port_retries, false, flags, ec);
|
||||
listen_socket_t s;
|
||||
setup_listener(&s, m_listen_interface, m_listen_port_retries, false, flags, ec);
|
||||
|
||||
if (s.sock)
|
||||
{
|
||||
|
@ -4470,11 +4467,11 @@ namespace aux {
|
|||
return m_listen_sockets.front().external_port;
|
||||
}
|
||||
|
||||
void session_impl::announce_lsd(sha1_hash const& ih, bool broadcast)
|
||||
void session_impl::announce_lsd(sha1_hash const& ih, int port, bool broadcast)
|
||||
{
|
||||
// use internal listen port for local peers
|
||||
if (m_lsd.get())
|
||||
m_lsd->announce(ih, m_listen_interface.port(), broadcast);
|
||||
m_lsd->announce(ih, port, broadcast);
|
||||
}
|
||||
|
||||
void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih)
|
||||
|
|
476
src/torrent.cpp
476
src/torrent.cpp
|
@ -1264,15 +1264,290 @@ namespace libtorrent
|
|||
|
||||
#endif
|
||||
|
||||
/*
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
/*
|
||||
bool verify_function(bool preverified, boost::asio::ssl::verify_context& ctx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
void torrent::init_ssl(std::string const& cert)
|
||||
{
|
||||
using boost::asio::ssl::context;
|
||||
|
||||
// this is needed for openssl < 1.0 to decrypt keys created by openssl 1.0+
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
// TODO: come up with something better
|
||||
RAND_seed(&info_hash()[0], 20);
|
||||
TORRENT_ASSERT(RAND_status() == 1);
|
||||
|
||||
// create the SSL context for this torrent. We need to
|
||||
// inject the root certificate, and no other, to
|
||||
// verify other peers against
|
||||
boost::shared_ptr<context> ctx(
|
||||
new (std::nothrow) context(m_ses.m_io_service, context::sslv23));
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
set_error(asio::error::no_memory, "SSL context");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->set_options(context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::single_dh_use);
|
||||
|
||||
error_code ec;
|
||||
ctx->set_verify_mode(context::verify_peer
|
||||
| context::verify_fail_if_no_peer_cert
|
||||
| context::verify_client_once, ec);
|
||||
if (ec)
|
||||
{
|
||||
set_error(ec, "SSL verify mode");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// this is used for debugging
|
||||
/*
|
||||
#error there's a bug where the async_handshake on the ssl_stream always succeeds, regardless of the certificate failing. It's not a trivial bug in asio, that's been tested with a small repro program.
|
||||
ctx->set_verify_callback(verify_function, ec);
|
||||
if (ec)
|
||||
{
|
||||
set_error(ec, "SSL verify callback");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
SSL_CTX* ssl_ctx = ctx->impl();
|
||||
|
||||
// create a new x.509 certificate store
|
||||
X509_STORE* cert_store = X509_STORE_new();
|
||||
if (!cert_store)
|
||||
{
|
||||
set_error(asio::error::no_memory, "x.509 certificate store");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// wrap the PEM certificate in a BIO, for openssl to read
|
||||
BIO* bp = BIO_new_mem_buf((void*)cert.c_str(), cert.size());
|
||||
|
||||
// parse the certificate into OpenSSL's internal
|
||||
// representation
|
||||
X509* certificate = PEM_read_bio_X509_AUX(bp, 0, 0, 0);
|
||||
|
||||
BIO_free(bp);
|
||||
|
||||
if (!certificate)
|
||||
{
|
||||
X509_STORE_free(cert_store);
|
||||
set_error(asio::error::no_memory, "x.509 certificate");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// add cert to cert_store
|
||||
X509_STORE_add_cert(cert_store, certificate);
|
||||
|
||||
// and lastly, replace the default cert store with ours
|
||||
SSL_CTX_set_cert_store(ssl_ctx, cert_store);
|
||||
#if 0
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename), "/tmp/%d.pem", rand());
|
||||
FILE* f = fopen(filename, "w+");
|
||||
fwrite(cert.c_str(), cert.size(), 1, f);
|
||||
fclose(f);
|
||||
ctx->load_verify_file(filename);
|
||||
#endif
|
||||
// if all went well, set the torrent ssl context to this one
|
||||
m_ssl_ctx = ctx;
|
||||
|
||||
// tell the client we need a cert for this torrent
|
||||
alerts().post_alert(torrent_need_cert_alert(get_handle()));
|
||||
|
||||
m_ssl_acceptor.reset(new listen_socket_t);
|
||||
m_ses.setup_listener(m_ssl_acceptor.get()
|
||||
, tcp::endpoint(address_v4::any(), m_ses.m_listen_interface.port())
|
||||
, m_ses.m_listen_port_retries + 10, false, 0, ec);
|
||||
if (!m_ssl_acceptor->sock)
|
||||
{
|
||||
set_error(ec, "ssl listen port");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: issue UPnP and NAT-PMP for this socket
|
||||
|
||||
async_accept(m_ssl_acceptor->sock);
|
||||
|
||||
set_allow_peers(false);
|
||||
}
|
||||
|
||||
void torrent::async_accept(boost::shared_ptr<socket_acceptor> const& listener)
|
||||
{
|
||||
boost::shared_ptr<socket_type> c(new socket_type(m_ses.m_io_service));
|
||||
c->instantiate<ssl_stream<stream_socket> >(m_ses.m_io_service, m_ssl_ctx.get());
|
||||
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("torrent::on_accept_ssl_connection");
|
||||
#endif
|
||||
listener->async_accept(c->get<ssl_stream<stream_socket> >()->next_layer()
|
||||
, boost::bind(&torrent::on_accept_ssl_connection, shared_from_this(), c
|
||||
, boost::weak_ptr<socket_acceptor>(listener), _1));
|
||||
}
|
||||
|
||||
void torrent::on_accept_ssl_connection(boost::shared_ptr<socket_type> const& s
|
||||
, boost::weak_ptr<socket_acceptor> listen_socket, error_code const& e)
|
||||
{
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
complete_async("torrent::on_accept_ssl_connection");
|
||||
#endif
|
||||
// TODO: there's some code duplication with session_impl::on_accept_connection
|
||||
|
||||
boost::shared_ptr<socket_acceptor> listener = listen_socket.lock();
|
||||
if (!listener) return;
|
||||
|
||||
if (e == asio::error::operation_aborted) return;
|
||||
|
||||
if (m_abort) return;
|
||||
|
||||
error_code ec;
|
||||
if (e)
|
||||
{
|
||||
tcp::endpoint ep = listener->local_endpoint(ec);
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
std::string msg = "error accepting connection on '"
|
||||
+ print_endpoint(ep) + "' " + e.message();
|
||||
(*m_ses.m_logger) << msg << "\n";
|
||||
#endif
|
||||
#ifdef TORRENT_WINDOWS
|
||||
// Windows sometimes generates this error. It seems to be
|
||||
// non-fatal and we have to do another async_accept.
|
||||
if (e.value() == ERROR_SEM_TIMEOUT)
|
||||
{
|
||||
async_accept(listener);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef TORRENT_BSD
|
||||
// Leopard sometimes generates an "invalid argument" error. It seems to be
|
||||
// non-fatal and we have to do another async_accept.
|
||||
if (e.value() == EINVAL)
|
||||
{
|
||||
async_accept(listener);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (alerts().should_post<listen_failed_alert>())
|
||||
alerts().post_alert(listen_failed_alert(ep, e));
|
||||
return;
|
||||
}
|
||||
async_accept(listener);
|
||||
|
||||
if (is_paused()) return;
|
||||
|
||||
// we got a connection request!
|
||||
tcp::endpoint endp = s->remote_endpoint(ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
(*m_ses.m_logger) << endp << " <== INCOMING CONNECTION FAILED, could "
|
||||
"not retrieve remote endpoint " << ec.message() << "\n";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settings().enable_incoming_tcp)
|
||||
{
|
||||
if (alerts().should_post<peer_blocked_alert>())
|
||||
alerts().post_alert(peer_blocked_alert(torrent_handle(), endp.address()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_apply_ip_filter
|
||||
&& m_ses.m_ip_filter.access(endp.address()) & ip_filter::blocked)
|
||||
{
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
(*m_ses.m_logger) << "filtered blocked ip\n";
|
||||
#endif
|
||||
if (alerts().should_post<peer_blocked_alert>())
|
||||
alerts().post_alert(peer_blocked_alert(get_handle(), endp.address()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_connections.size() >= m_max_connections)
|
||||
{
|
||||
if (alerts().should_post<peer_disconnected_alert>())
|
||||
{
|
||||
alerts().post_alert(
|
||||
peer_disconnected_alert(get_handle(), endp, peer_id()
|
||||
, error_code(errors::too_many_connections, get_libtorrent_category())));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_ses.setup_socket_buffers(*s);
|
||||
|
||||
s->get<ssl_stream<stream_socket> >()->async_accept_handshake(boost::bind(&torrent::ssl_handshake
|
||||
, shared_from_this(), _1, s));
|
||||
}
|
||||
|
||||
void torrent::ssl_handshake(error_code const& ec, boost::shared_ptr<socket_type> s)
|
||||
{
|
||||
error_code e;
|
||||
tcp::endpoint endp = s->remote_endpoint(e);
|
||||
if (e) return;
|
||||
|
||||
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
|
||||
(*m_ses.m_logger) << time_now_string() << " *** peer SSL handshake done [ ip: "
|
||||
<< endp << " ec: " << ec.message() << "]\n";
|
||||
#endif
|
||||
|
||||
if (ec)
|
||||
{
|
||||
if (alerts().should_post<peer_error_alert>())
|
||||
{
|
||||
alerts().post_alert(peer_error_alert(get_handle(), endp
|
||||
, peer_id(), ec));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
boost::intrusive_ptr<peer_connection> c(
|
||||
new bt_peer_connection(m_ses, shared_from_this(), s, endp, 0, false));
|
||||
#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS
|
||||
c->m_in_constructor = false;
|
||||
#endif
|
||||
|
||||
if (c->is_disconnecting()) return;
|
||||
|
||||
if (!m_policy.new_connection(*c, m_ses.session_time()))
|
||||
{
|
||||
#if defined TORRENT_LOGGING
|
||||
(*m_ses.m_logger) << time_now_string() << " CLOSING CONNECTION "
|
||||
<< p->remote() << " policy::new_connection returned false (i.e. peer list full)\n";
|
||||
#endif
|
||||
c->disconnect(errors::too_many_connections);
|
||||
return;
|
||||
}
|
||||
|
||||
// add the newly connected peer to this torrent's peer list
|
||||
m_connections.insert(boost::get_pointer(c));
|
||||
m_ses.m_connections.insert(c);
|
||||
c->start();
|
||||
if (settings().default_peer_upload_rate)
|
||||
c->set_upload_limit(settings().default_peer_upload_rate);
|
||||
if (settings().default_peer_download_rate)
|
||||
c->set_download_limit(settings().default_peer_download_rate);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// this may not be called from a constructor because of the call to
|
||||
// shared_from_this()
|
||||
void torrent::init()
|
||||
|
@ -1284,88 +1559,7 @@ namespace libtorrent
|
|||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
std::string cert = m_torrent_file->ssl_cert();
|
||||
if (!cert.empty())
|
||||
{
|
||||
using boost::asio::ssl::context;
|
||||
|
||||
// create the SSL context for this torrent. We need to
|
||||
// inject the root certificate, and no other, to
|
||||
// verify other peers against
|
||||
boost::shared_ptr<context> ctx(
|
||||
new (std::nothrow) context(m_ses.m_io_service, context::sslv23));
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
set_error(asio::error::no_memory, "SSL context");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
error_code ec;
|
||||
ctx->set_verify_mode(context::verify_peer
|
||||
| context::verify_fail_if_no_peer_cert, ec);
|
||||
if (ec)
|
||||
{
|
||||
set_error(ec, "SSL context");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// this is used for debugging
|
||||
/*
|
||||
#error there's a bug where the async_handshake on the ssl_stream always succeeds, regardless of the certificate failing. It's not a trivial bug in asio, that's been tested with a small repro program.
|
||||
ctx->set_verify_callback(verify_function, ec);
|
||||
if (ec)
|
||||
{
|
||||
set_error(ec, "SSL verify callback");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
SSL_CTX* ssl_ctx = ctx->impl();
|
||||
|
||||
// create a new x.509 certificate store
|
||||
X509_STORE* cert_store = X509_STORE_new();
|
||||
if (!cert_store)
|
||||
{
|
||||
set_error(asio::error::no_memory, "x.509 certificate store");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// wrap the PEM certificate in a BIO, for openssl to read
|
||||
BIO* bp = BIO_new_mem_buf((void*)cert.c_str(), cert.size());
|
||||
|
||||
// parse the certificate into OpenSSL's internal
|
||||
// representation
|
||||
X509* certificate = PEM_read_bio_X509_AUX(bp, 0, 0, 0);
|
||||
|
||||
BIO_free(bp);
|
||||
|
||||
if (!certificate)
|
||||
{
|
||||
X509_STORE_free(cert_store);
|
||||
set_error(asio::error::no_memory, "x.509 certificate");
|
||||
pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// add cert to cert_store
|
||||
X509_STORE_add_cert(cert_store, certificate);
|
||||
|
||||
// and lastly, replace the default cert store with ours
|
||||
SSL_CTX_set_cert_store(ssl_ctx, cert_store);
|
||||
#if 0
|
||||
char filename[100];
|
||||
snprintf(filename, sizeof(filename), "/tmp/%d.pem", rand());
|
||||
FILE* f = fopen(filename, "w+");
|
||||
fwrite(cert.c_str(), cert.size(), 1, f);
|
||||
fclose(f);
|
||||
ctx->load_verify_file(filename);
|
||||
#endif
|
||||
// if all went well, set the torrent ssl context to this one
|
||||
m_ssl_ctx = ctx;
|
||||
}
|
||||
if (!cert.empty()) init_ssl(cert);
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
|
@ -1968,8 +2162,14 @@ namespace libtorrent
|
|||
|
||||
if (is_paused()) return;
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
int port = is_ssl_torrent() ? m_ssl_acceptor->external_port : m_ses.listen_port();
|
||||
#else
|
||||
int port = m_ses.listen_port();
|
||||
#endif
|
||||
|
||||
// announce with the local discovery service
|
||||
m_ses.announce_lsd(m_torrent_file->info_hash()
|
||||
m_ses.announce_lsd(m_torrent_file->info_hash(), port
|
||||
, m_ses.settings().broadcast_lsd && m_lsd_seq == 0);
|
||||
++m_lsd_seq;
|
||||
}
|
||||
|
@ -1984,9 +2184,15 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(m_allow_peers);
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
int port = is_ssl_torrent() ? m_ssl_acceptor->external_port : m_ses.listen_port();
|
||||
#else
|
||||
int port = m_ses.listen_port();
|
||||
#endif
|
||||
|
||||
boost::weak_ptr<torrent> self(shared_from_this());
|
||||
m_ses.m_dht->announce(m_torrent_file->info_hash()
|
||||
, m_ses.listen_port(), is_seed()
|
||||
, port, is_seed()
|
||||
, boost::bind(&torrent::on_dht_announce_response_disp, self, _1));
|
||||
}
|
||||
|
||||
|
@ -2073,6 +2279,14 @@ namespace libtorrent
|
|||
req.num_want = (req.event == tracker_request::stopped)
|
||||
?0:settings().num_want;
|
||||
|
||||
// SSL torrents use their own listen socket
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
// TODO: this pattern is repeated in a few places. Factor this into
|
||||
// a function and generalize the concept of a torrent having a
|
||||
// dedicated listen port
|
||||
if (is_ssl_torrent()) req.listen_port = m_ssl_acceptor->external_port;
|
||||
else
|
||||
#endif
|
||||
req.listen_port = m_ses.listen_port();
|
||||
req.key = m_ses.m_key;
|
||||
|
||||
|
@ -3207,6 +3421,12 @@ namespace libtorrent
|
|||
m_ses.m_encrypted_torrents.erase(shared_from_this());
|
||||
m_in_encrypted_list = false;
|
||||
}
|
||||
|
||||
if (m_ssl_acceptor && m_ssl_acceptor->sock)
|
||||
{
|
||||
error_code ec;
|
||||
m_ssl_acceptor->sock->close(ec);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_abort = true;
|
||||
|
@ -3973,22 +4193,51 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
// certificate is a filename to a .pem file which is our
|
||||
// certificate. root_cert is a filename to a certificate
|
||||
// on disk which is the trusted certificate authority (CA)
|
||||
// for this torrent. 'certificate' must be signed by the
|
||||
// 'root_cert', and any peer we connect to or that connect
|
||||
// to use must present a valid certificate signed by 'root_cert'
|
||||
void torrent::set_ssl_cert(std::string const& certificate, error_code& ec)
|
||||
std::string password_callback(int length, boost::asio::ssl::context::password_purpose p
|
||||
, std::string pw)
|
||||
{
|
||||
ec.clear();
|
||||
if (!m_ssl_ctx)
|
||||
{
|
||||
ec = asio::error::operation_not_supported;
|
||||
return;
|
||||
}
|
||||
if (p != boost::asio::ssl::context::for_reading) return "";
|
||||
return pw;
|
||||
}
|
||||
|
||||
// certificate is a filename to a .pem file which is our
|
||||
// certificate. The certificate must be signed by the root
|
||||
// cert of the torrent file. any peer we connect to or that
|
||||
// connect to use must present a valid certificate signed
|
||||
// by the torrent root cert as well
|
||||
void torrent::set_ssl_cert(std::string const& certificate
|
||||
, std::string const& private_key
|
||||
, std::string const& dh_params
|
||||
, std::string const& passphrase)
|
||||
{
|
||||
if (!m_ssl_ctx) return;
|
||||
|
||||
using boost::asio::ssl::context;
|
||||
error_code ec;
|
||||
m_ssl_ctx->set_password_callback(boost::bind(&password_callback, _1, _2, passphrase), ec);
|
||||
if (ec)
|
||||
{
|
||||
if (alerts().should_post<torrent_error_alert>())
|
||||
alerts().post_alert(torrent_error_alert(get_handle(), ec));
|
||||
}
|
||||
m_ssl_ctx->use_certificate_file(certificate, context::pem, ec);
|
||||
if (ec)
|
||||
{
|
||||
if (alerts().should_post<torrent_error_alert>())
|
||||
alerts().post_alert(torrent_error_alert(get_handle(), ec));
|
||||
}
|
||||
m_ssl_ctx->use_private_key_file(private_key, context::pem, ec);
|
||||
if (ec)
|
||||
{
|
||||
if (alerts().should_post<torrent_error_alert>())
|
||||
alerts().post_alert(torrent_error_alert(get_handle(), ec));
|
||||
}
|
||||
m_ssl_ctx->use_tmp_dh_file(dh_params, ec);
|
||||
if (ec)
|
||||
{
|
||||
if (alerts().should_post<torrent_error_alert>())
|
||||
alerts().post_alert(torrent_error_alert(get_handle(), ec));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -4022,7 +4271,7 @@ namespace libtorrent
|
|||
if (m_picker.get())
|
||||
{
|
||||
bitfield const& pieces = p->get_bitfield();
|
||||
TORRENT_ASSERT(pieces.count() < int(pieces.size()));
|
||||
TORRENT_ASSERT(pieces.count() <= int(pieces.size()));
|
||||
m_picker->dec_refcount(pieces);
|
||||
}
|
||||
}
|
||||
|
@ -5159,6 +5408,9 @@ namespace libtorrent
|
|||
}
|
||||
#endif
|
||||
|
||||
// extend connect timeout by this many seconds
|
||||
int timeout_extend = 0;
|
||||
|
||||
TORRENT_ASSERT(want_more_peers() || ignore_limit);
|
||||
TORRENT_ASSERT(m_ses.num_connections() < m_ses.settings().connections_limit || ignore_limit);
|
||||
|
||||
|
@ -5178,6 +5430,8 @@ namespace libtorrent
|
|||
s->get<i2p_stream>()->set_destination(static_cast<policy::i2p_peer*>(peerinfo)->destination);
|
||||
s->get<i2p_stream>()->set_command(i2p_stream::cmd_connect);
|
||||
s->get<i2p_stream>()->set_session_id(m_ses.m_i2p_conn.session_id());
|
||||
// i2p setups are slow
|
||||
timeout_extend = 20;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
@ -5196,7 +5450,20 @@ namespace libtorrent
|
|||
// don't make a TCP connection if it's disabled
|
||||
if (sm == 0 && !m_ses.m_settings.enable_outgoing_tcp) return false;
|
||||
|
||||
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, 0, sm, true);
|
||||
void* userdata = 0;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (is_ssl_torrent())
|
||||
{
|
||||
userdata = m_ssl_ctx.get();
|
||||
// SSL handshakes are slow
|
||||
timeout_extend = 10;
|
||||
|
||||
// we don't support SSL over uTP yet
|
||||
sm = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata, sm, true);
|
||||
(void)ret;
|
||||
TORRENT_ASSERT(ret);
|
||||
}
|
||||
|
@ -5239,6 +5506,7 @@ namespace libtorrent
|
|||
|
||||
int timeout = settings().peer_connect_timeout;
|
||||
if (peerinfo) timeout += 3 * peerinfo->failcount;
|
||||
timeout += timeout_extend;
|
||||
|
||||
TORRENT_TRY
|
||||
{
|
||||
|
@ -5379,6 +5647,7 @@ namespace libtorrent
|
|||
(*m_ses.m_logger) << time_now_string() << " CLOSING CONNECTION "
|
||||
<< p->remote() << " policy::new_connection returned false (i.e. peer list full)\n";
|
||||
#endif
|
||||
p->disconnect(errors::too_many_connections);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -7675,6 +7944,11 @@ namespace libtorrent
|
|||
st->handle = get_handle();
|
||||
st->info_hash = info_hash();
|
||||
|
||||
st->listen_port = 0;
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (is_ssl_torrent() && m_ssl_acceptor) st->listen_port = m_ssl_acceptor->external_port;
|
||||
#endif
|
||||
|
||||
st->has_incoming = m_has_incoming;
|
||||
if (m_error) st->error = m_error.message() + ": " + m_error_file;
|
||||
st->seed_mode = m_seed_mode;
|
||||
|
|
|
@ -112,6 +112,12 @@ namespace libtorrent
|
|||
session_impl& ses = t->session(); \
|
||||
ses.m_io_service.post(boost::bind(&torrent:: x, t, a1, a2, a3))
|
||||
|
||||
#define TORRENT_ASYNC_CALL4(x, a1, a2, a3, a4) \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) return; \
|
||||
session_impl& ses = t->session(); \
|
||||
ses.m_io_service.post(boost::bind(&torrent:: x, t, a1, a2, a3, a4))
|
||||
|
||||
#define TORRENT_SYNC_CALL(x) \
|
||||
boost::shared_ptr<torrent> t = m_torrent.lock(); \
|
||||
if (!t) return; \
|
||||
|
@ -375,13 +381,14 @@ namespace libtorrent
|
|||
TORRENT_ASYNC_CALL(flush_cache);
|
||||
}
|
||||
|
||||
void torrent_handle::set_ssl_certificates(
|
||||
std::string const& certificate, error_code& ec)
|
||||
void torrent_handle::set_ssl_certificate(
|
||||
std::string const& certificate
|
||||
, std::string const& private_key
|
||||
, std::string const& dh_params
|
||||
, std::string const& passphrase)
|
||||
{
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
TORRENT_ASYNC_CALL2(set_ssl_cert, certificate, boost::ref(ec));
|
||||
#else
|
||||
ec = boost::asio::error::operation_not_supported;
|
||||
TORRENT_ASYNC_CALL4(set_ssl_cert, certificate, private_key, dh_params, passphrase);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue