basic support for bittorrent connections over SSL

This commit is contained in:
Arvid Norberg 2011-09-10 05:52:07 +00:00
parent 38a4b58c3a
commit 675721d971
13 changed files with 177 additions and 23 deletions

View File

@ -40,7 +40,11 @@ namespace libtorrent
{ {
struct proxy_settings; struct proxy_settings;
struct utp_socket_manager; struct utp_socket_manager;
struct socket_type;
// if from is specified, a new socket will be created
// using the same underlying socket object as 'from'.
// this can be used to "upgrade" a socket into an SSL socket
TORRENT_EXPORT bool instantiate_connection(io_service& ios TORRENT_EXPORT bool instantiate_connection(io_service& ios
, proxy_settings const& ps, socket_type& s , proxy_settings const& ps, socket_type& s
, void* ssl_context = 0 , void* ssl_context = 0

View File

@ -105,6 +105,18 @@ namespace libtorrent
value = max3<temp1, temp2, temp3>::value value = max3<temp1, temp2, temp3>::value
}; };
}; };
template<int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9>
struct max9
{
enum
{
temp1 = max3<v1,v2, v3>::value,
temp2 = max3<v4,v5,v6>::value,
temp3 = max3<v7,v8,v9>::value,
value = max3<temp1, temp2, temp3>::value
};
};
} }
#endif #endif

View File

@ -46,6 +46,7 @@ class proxy_base : boost::noncopyable
{ {
public: public:
typedef stream_socket next_layer_type;
typedef stream_socket::lowest_layer_type lowest_layer_type; typedef stream_socket::lowest_layer_type lowest_layer_type;
typedef stream_socket::endpoint_type endpoint_type; typedef stream_socket::endpoint_type endpoint_type;
typedef stream_socket::protocol_type protocol_type; typedef stream_socket::protocol_type protocol_type;
@ -217,6 +218,11 @@ public:
return m_sock.lowest_layer(); return m_sock.lowest_layer();
} }
next_layer_type& next_layer()
{
return m_sock;
}
bool is_open() const { return m_sock.is_open(); } bool is_open() const { return m_sock.is_open(); }
protected: protected:

View File

@ -72,7 +72,9 @@ POSSIBILITY OF SUCH DAMAGE.
case socket_type_int_impl<ssl_stream<socks5_stream> >::value: \ case socket_type_int_impl<ssl_stream<socks5_stream> >::value: \
get<ssl_stream<socks5_stream> >()->x; break; \ get<ssl_stream<socks5_stream> >()->x; break; \
case socket_type_int_impl<ssl_stream<http_stream> >::value: \ case socket_type_int_impl<ssl_stream<http_stream> >::value: \
get<ssl_stream<http_stream> >()->x; break; get<ssl_stream<http_stream> >()->x; break; \
case socket_type_int_impl<ssl_stream<utp_stream> >::value: \
get<ssl_stream<utp_stream> >()->x; break;
#define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \ #define TORRENT_SOCKTYPE_SSL_FORWARD_RET(x, def) \
case socket_type_int_impl<ssl_stream<stream_socket> >::value: \ case socket_type_int_impl<ssl_stream<stream_socket> >::value: \
@ -80,7 +82,9 @@ POSSIBILITY OF SUCH DAMAGE.
case socket_type_int_impl<ssl_stream<socks5_stream> >::value: \ case socket_type_int_impl<ssl_stream<socks5_stream> >::value: \
return get<ssl_stream<socks5_stream> >()->x; \ return get<ssl_stream<socks5_stream> >()->x; \
case socket_type_int_impl<ssl_stream<http_stream> >::value: \ case socket_type_int_impl<ssl_stream<http_stream> >::value: \
return get<ssl_stream<http_stream> >()->x; return get<ssl_stream<http_stream> >()->x; \
case socket_type_int_impl<ssl_stream<utp_stream> >::value: \
return get<ssl_stream<utp_stream> >()->x;
#else #else
@ -160,6 +164,10 @@ namespace libtorrent
template <> template <>
struct socket_type_int_impl<ssl_stream<http_stream> > struct socket_type_int_impl<ssl_stream<http_stream> >
{ enum { value = 8 }; }; { enum { value = 8 }; };
template <>
struct socket_type_int_impl<ssl_stream<utp_stream> >
{ enum { value = 9 }; };
#endif #endif
struct TORRENT_EXPORT socket_type struct TORRENT_EXPORT socket_type
@ -260,7 +268,7 @@ namespace libtorrent
io_service& m_io_service; io_service& m_io_service;
int m_type; int m_type;
enum { storage_size = max8< enum { storage_size = max9<
sizeof(stream_socket) sizeof(stream_socket)
, sizeof(socks5_stream) , sizeof(socks5_stream)
, sizeof(http_stream) , sizeof(http_stream)
@ -274,8 +282,9 @@ namespace libtorrent
, sizeof(ssl_stream<stream_socket>) , sizeof(ssl_stream<stream_socket>)
, sizeof(ssl_stream<socks5_stream>) , sizeof(ssl_stream<socks5_stream>)
, sizeof(ssl_stream<http_stream>) , sizeof(ssl_stream<http_stream>)
, sizeof(ssl_stream<utp_stream>)
#else #else
, 0, 0, 0 , 0, 0, 0, 0
#endif #endif
>::value >::value
}; };

View File

@ -56,11 +56,11 @@ public:
{ {
} }
typedef asio::ssl::stream<Stream> next_layer_type; typedef typename asio::ssl::stream<Stream> sock_type;
typedef typename sock_type::next_layer_type next_layer_type;
typedef typename Stream::lowest_layer_type lowest_layer_type; typedef typename Stream::lowest_layer_type lowest_layer_type;
typedef typename Stream::endpoint_type endpoint_type; typedef typename Stream::endpoint_type endpoint_type;
typedef typename Stream::protocol_type protocol_type; typedef typename Stream::protocol_type protocol_type;
typedef typename asio::ssl::stream<Stream> sock_type;
typedef boost::function<void(error_code const&)> handler_type; typedef boost::function<void(error_code const&)> handler_type;
@ -79,6 +79,33 @@ public:
, boost::bind(&ssl_stream::connected, this, _1, h)); , boost::bind(&ssl_stream::connected, this, _1, h));
} }
template <class Handler>
void async_accept_handshake(Handler const& handler)
{
// this is used for accepting SSL connections
boost::shared_ptr<handler_type> h(new handler_type(handler));
m_sock.async_handshake(asio::ssl::stream_base::server
, boost::bind(&ssl_stream::handshake, this, _1, h));
}
void accept_handshake(error_code& ec)
{
// this is used for accepting SSL connections
m_sock.handshake(asio::ssl::stream_base::server, ec);
}
template <class Handler>
void async_shutdown(Handler const& handler)
{
boost::shared_ptr<handler_type> h(new handler_type(handler));
m_sock.async_shutdown( boost::bind(&ssl_stream::on_shutdown, this, _1, h));
}
void shutdown(error_code& ec)
{
m_sock.shutdown(ec);
}
template <class Mutable_Buffers, class Handler> template <class Mutable_Buffers, class Handler>
void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) void async_read_some(Mutable_Buffers const& buffers, Handler const& handler)
{ {
@ -222,7 +249,7 @@ public:
next_layer_type& next_layer() next_layer_type& next_layer()
{ {
return m_sock; return m_sock.next_layer();
} }
private: private:

View File

@ -838,6 +838,12 @@ namespace libtorrent
void queue_torrent_check(); void queue_torrent_check();
void dequeue_torrent_check(); void dequeue_torrent_check();
#ifdef TORRENT_USE_OPENSSL
void set_ssl_cert(std::string const& certificate, error_code& ec);
bool is_ssl_torrent() const { return m_ssl_ctx; }
boost::asio::ssl::context* ssl_ctx() const { return m_ssl_ctx.get(); }
#endif
private: private:
void on_files_deleted(int ret, disk_io_job const& j); void on_files_deleted(int ret, disk_io_job const& j);

View File

@ -252,6 +252,9 @@ namespace libtorrent
bool resolve_countries() const; bool resolve_countries() const;
#endif #endif
void set_ssl_certificates(std::string const& certificate
, error_code& ec);
storage_interface* get_storage_impl() const; storage_interface* get_storage_impl() const;
// all these are deprecated, use piece // all these are deprecated, use piece

View File

@ -166,12 +166,15 @@ class TORRENT_EXPORT utp_stream
{ {
public: public:
typedef utp_stream lowest_layer_type;
typedef stream_socket::endpoint_type endpoint_type; typedef stream_socket::endpoint_type endpoint_type;
typedef stream_socket::protocol_type protocol_type; typedef stream_socket::protocol_type protocol_type;
explicit utp_stream(asio::io_service& io_service); explicit utp_stream(asio::io_service& io_service);
~utp_stream(); ~utp_stream();
lowest_layer_type& lowest_layer() { return *this; }
// used for incoming connections // used for incoming connections
void set_impl(utp_socket_impl* s); void set_impl(utp_socket_impl* s);
utp_socket_impl* get_impl(); utp_socket_impl* get_impl();
@ -239,8 +242,7 @@ public:
std::size_t available() const; std::size_t available() const;
std::size_t available(error_code& ec) const { return available(); } std::size_t available(error_code& ec) const { return available(); }
asio::io_service& io_service() asio::io_service& get_io_service() { return m_io_service; }
{ return m_io_service; }
template <class Handler> template <class Handler>
void async_connect(endpoint_type const& endpoint, Handler const& handler) void async_connect(endpoint_type const& endpoint, Handler const& handler)
@ -338,10 +340,33 @@ public:
template <class Const_Buffers> template <class Const_Buffers>
std::size_t write_some(Const_Buffers const& buffers, error_code& ec) std::size_t write_some(Const_Buffers const& buffers, error_code& ec)
{ {
TORRENT_ASSERT(false && "not implemented!");
// TODO: implement // TODO: implement
return 0; return 0;
} }
#ifndef BOOST_NO_EXCEPTIONS
template <class Mutable_Buffers>
std::size_t read_some(Mutable_Buffers const& buffers)
{
error_code ec;
std::size_t ret = read_some(buffers, ec);
if (ec)
boost::throw_exception(boost::system::system_error(ec));
return ret;
}
template <class Const_Buffers>
std::size_t write_some(Const_Buffers const& buffers)
{
error_code ec;
std::size_t ret = write_some(buffers, ec);
if (ec)
boost::throw_exception(boost::system::system_error(ec));
return ret;
}
#endif
template <class Const_Buffers, class Handler> template <class Const_Buffers, class Handler>
void async_write_some(Const_Buffers const& buffers, Handler const& handler) void async_write_some(Const_Buffers const& buffers, Handler const& handler)
{ {

View File

@ -300,12 +300,14 @@ void http_connection::start(std::string const& hostname, std::string const& port
{ {
if (m_ssl_ctx == 0) if (m_ssl_ctx == 0)
{ {
m_ssl_ctx = new boost::asio::ssl::context(m_resolver.get_io_service(), asio::ssl::context::sslv23_client); m_ssl_ctx = new (std::nothrow) boost::asio::ssl::context(
m_resolver.get_io_service(), asio::ssl::context::sslv23_client);
if (m_ssl_ctx) if (m_ssl_ctx)
{ {
m_own_ssl_context = true; m_own_ssl_context = true;
error_code ec; error_code ec;
m_ssl_ctx->set_verify_mode(asio::ssl::context::verify_none, ec); m_ssl_ctx->set_verify_mode(asio::ssl::context::verify_none, ec);
TORRENT_ASSERT(!ec);
} }
} }
userdata = m_ssl_ctx; userdata = m_ssl_ctx;
@ -536,7 +538,7 @@ void http_connection::connect(int ticket, tcp::endpoint target_address)
if (m_ssl) if (m_ssl)
{ {
TORRENT_ASSERT(m_sock.get<ssl_stream<socks5_stream> >()); TORRENT_ASSERT(m_sock.get<ssl_stream<socks5_stream> >());
m_sock.get<ssl_stream<socks5_stream> >()->next_layer().next_layer().set_dst_name(m_hostname); m_sock.get<ssl_stream<socks5_stream> >()->next_layer().set_dst_name(m_hostname);
} }
else else
#endif #endif

View File

@ -41,7 +41,6 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
TORRENT_EXPORT bool instantiate_connection(io_service& ios TORRENT_EXPORT bool instantiate_connection(io_service& ios
, proxy_settings const& ps, socket_type& s , proxy_settings const& ps, socket_type& s
, void* ssl_context , void* ssl_context
@ -49,13 +48,27 @@ namespace libtorrent
, bool peer_connection) , bool peer_connection)
{ {
if (sm) if (sm)
{
utp_stream* str;
#ifdef TORRENT_USE_OPENSSL
if (ssl_context)
{
s.instantiate<ssl_stream<utp_stream> >(ios, ssl_context);
str = &s.get<ssl_stream<utp_stream> >()->next_layer();
}
else
#endif
{ {
s.instantiate<utp_stream>(ios); s.instantiate<utp_stream>(ios);
s.get<utp_stream>()->set_impl(sm->new_utp_socket(s.get<utp_stream>())); str = s.get<utp_stream>();
}
str->set_impl(sm->new_utp_socket(str));
} }
#if TORRENT_USE_I2P #if TORRENT_USE_I2P
else if (ps.type == proxy_settings::i2p_proxy) else if (ps.type == proxy_settings::i2p_proxy)
{ {
// it doesn't make any sense to try ssl over i2p
TORRENT_ASSERT(ssl_context == 0);
s.instantiate<i2p_stream>(ios); s.instantiate<i2p_stream>(ios);
s.get<i2p_stream>()->set_proxy(ps.hostname, ps.port); s.get<i2p_stream>()->set_proxy(ps.hostname, ps.port);
} }
@ -63,12 +76,19 @@ namespace libtorrent
else if (ps.type == proxy_settings::none else if (ps.type == proxy_settings::none
|| (peer_connection && !ps.proxy_peer_connections)) || (peer_connection && !ps.proxy_peer_connections))
{ {
stream_socket* str;
#ifdef TORRENT_USE_OPENSSL #ifdef TORRENT_USE_OPENSSL
if (ssl_context) if (ssl_context)
{
s.instantiate<ssl_stream<stream_socket> >(ios, ssl_context); s.instantiate<ssl_stream<stream_socket> >(ios, ssl_context);
str = &s.get<ssl_stream<stream_socket> >()->next_layer();
}
else else
#endif #endif
{
s.instantiate<stream_socket>(ios); s.instantiate<stream_socket>(ios);
str = s.get<stream_socket>();
}
} }
else if (ps.type == proxy_settings::http else if (ps.type == proxy_settings::http
|| ps.type == proxy_settings::http_pw) || ps.type == proxy_settings::http_pw)
@ -78,7 +98,7 @@ namespace libtorrent
if (ssl_context) if (ssl_context)
{ {
s.instantiate<ssl_stream<http_stream> >(ios, ssl_context); s.instantiate<ssl_stream<http_stream> >(ios, ssl_context);
str = &s.get<ssl_stream<http_stream> >()->next_layer().next_layer(); str = &s.get<ssl_stream<http_stream> >()->next_layer();
} }
else else
#endif #endif
@ -100,7 +120,7 @@ namespace libtorrent
if (ssl_context) if (ssl_context)
{ {
s.instantiate<ssl_stream<socks5_stream> >(ios, ssl_context); s.instantiate<ssl_stream<socks5_stream> >(ios, ssl_context);
str = &s.get<ssl_stream<socks5_stream> >()->next_layer().next_layer(); str = &s.get<ssl_stream<socks5_stream> >()->next_layer();
} }
else else
#endif #endif

View File

@ -71,6 +71,9 @@ namespace libtorrent
case socket_type_int_impl<ssl_stream<http_stream> >::value: case socket_type_int_impl<ssl_stream<http_stream> >::value:
get<ssl_stream<http_stream> >()->~ssl_stream(); get<ssl_stream<http_stream> >()->~ssl_stream();
break; break;
case socket_type_int_impl<ssl_stream<utp_stream> >::value:
get<ssl_stream<utp_stream> >()->~ssl_stream();
break;
#endif #endif
default: TORRENT_ASSERT(false); default: TORRENT_ASSERT(false);
} }
@ -116,6 +119,11 @@ namespace libtorrent
new ((ssl_stream<http_stream>*)m_data) ssl_stream<http_stream>(m_io_service new ((ssl_stream<http_stream>*)m_data) ssl_stream<http_stream>(m_io_service
, *((boost::asio::ssl::context*)userdata)); , *((boost::asio::ssl::context*)userdata));
break; break;
case socket_type_int_impl<ssl_stream<utp_stream> >::value:
TORRENT_ASSERT(userdata);
new ((ssl_stream<utp_stream>*)m_data) ssl_stream<utp_stream>(m_io_service
, *((boost::asio::ssl::context*)userdata));
break;
#endif #endif
default: TORRENT_ASSERT(false); default: TORRENT_ASSERT(false);
} }

View File

@ -1292,7 +1292,7 @@ namespace libtorrent
// inject the root certificate, and no other, to // inject the root certificate, and no other, to
// verify other peers against // verify other peers against
boost::shared_ptr<context> ctx( boost::shared_ptr<context> ctx(
new (std::nothrow) context(m_ses.m_io_service, context::tlsv1)); new (std::nothrow) context(m_ses.m_io_service, context::sslv23));
if (!ctx) if (!ctx)
{ {
@ -3972,6 +3972,26 @@ 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)
{
ec.clear();
if (!m_ssl_ctx)
{
ec = asio::error::operation_not_supported;
return;
}
using boost::asio::ssl::context;
m_ssl_ctx->use_certificate_file(certificate, context::pem, ec);
}
#endif
void torrent::remove_peer(peer_connection* p) void torrent::remove_peer(peer_connection* p)
{ {
// INVARIANT_CHECK; // INVARIANT_CHECK;
@ -4313,7 +4333,11 @@ namespace libtorrent
bool ssl = string_begins_no_case("https://", web->url.c_str()); bool ssl = string_begins_no_case("https://", web->url.c_str());
void* userdata = 0; void* userdata = 0;
#ifdef TORRENT_USE_OPENSSL #ifdef TORRENT_USE_OPENSSL
if (ssl) userdata = &m_ses.m_ssl_ctx; if (ssl)
{
userdata = m_ssl_ctx.get();
if (!userdata) userdata = &m_ses.m_ssl_ctx;
}
#endif #endif
bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata, 0, true); bool ret = instantiate_connection(m_ses.m_io_service, m_ses.proxy(), *s, userdata, 0, true);
(void)ret; (void)ret;
@ -4335,13 +4359,11 @@ namespace libtorrent
{ {
// we're using a socks proxy and we're resolving // we're using a socks proxy and we're resolving
// hostnames through it // hostnames through it
socks5_stream* str =
#ifdef TORRENT_USE_OPENSSL #ifdef TORRENT_USE_OPENSSL
socks5_stream* str = ssl ssl ? &s->get<ssl_stream<socks5_stream> >()->next_layer() :
? &s->get<ssl_stream<socks5_stream> >()->next_layer().next_layer()
: s->get<socks5_stream>();
#else
socks5_stream* str = s->get<socks5_stream>();
#endif #endif
s->get<socks5_stream>();
TORRENT_ASSERT(str); TORRENT_ASSERT(str);
using boost::tuples::ignore; using boost::tuples::ignore;

View File

@ -375,6 +375,16 @@ namespace libtorrent
TORRENT_ASYNC_CALL(flush_cache); TORRENT_ASYNC_CALL(flush_cache);
} }
void torrent_handle::set_ssl_certificates(
std::string const& certificate, error_code& ec)
{
#ifdef TORRENT_USE_OPENSSL
TORRENT_ASYNC_CALL2(set_ssl_cert, certificate, boost::ref(ec));
#else
ec = boost::asio::error::operation_not_supported;
#endif
}
void torrent_handle::save_resume_data(int f) const void torrent_handle::save_resume_data(int f) const
{ {
INVARIANT_CHECK; INVARIANT_CHECK;