diff --git a/libi2pd_client/BOB.cpp b/libi2pd_client/BOB.cpp index a8520e9b..f956186f 100644 --- a/libi2pd_client/BOB.cpp +++ b/libi2pd_client/BOB.cpp @@ -156,7 +156,7 @@ namespace client { if (stream) { - auto conn = std::make_shared (this, stream, std::make_shared (GetService ()), m_Endpoint, m_IsQuiet); + auto conn = std::make_shared (this, stream, m_Endpoint, m_IsQuiet); AddHandler (conn); conn->Connect (); } diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 3f670f06..a5e1c67c 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -726,7 +726,8 @@ namespace client std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, ""); bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); - + bool ssl = section.second.get(I2P_SERVER_TUNNEL_SSL, false); + // I2CP std::map options; ReadI2CPOptions (section, true, options); @@ -799,11 +800,13 @@ namespace client if (!address.empty ()) serverTunnel->SetLocalAddress (address); - if(!isUniqueLocal) + if (!isUniqueLocal) { LogPrint(eLogInfo, "Clients: Disabling loopback address mapping"); serverTunnel->SetUniqueLocal(isUniqueLocal); } + if (ssl) + serverTunnel->SetSSL (true); if (accessList.length () > 0) { std::set idents; diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index d512a55b..ded8ea75 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2021, The PurpleI2P Project +* Copyright (c) 2013-2022, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -61,7 +61,7 @@ namespace client const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword"; const char I2P_SERVER_TUNNEL_ADDRESS[] = "address"; const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal"; - + const char I2P_SERVER_TUNNEL_SSL[] = "ssl"; class ClientContext { diff --git a/libi2pd_client/I2PTunnel.cpp b/libi2pd_client/I2PTunnel.cpp index 12c6485f..ab73c672 100644 --- a/libi2pd_client/I2PTunnel.cpp +++ b/libi2pd_client/I2PTunnel.cpp @@ -21,7 +21,7 @@ namespace client { /** set standard socket options */ - static void I2PTunnelSetSocketOptions(std::shared_ptr socket) + static void I2PTunnelSetSocketOptions (std::shared_ptr socket) { if (socket && socket->is_open()) { @@ -46,10 +46,13 @@ namespace client } I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, bool quiet): - I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), - m_RemoteEndpoint (target), m_IsQuiet (quiet) + const boost::asio::ip::tcp::endpoint& target, bool quiet, + std::shared_ptr sslCtx): + I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsQuiet (quiet) { + m_Socket = std::make_shared (owner->GetService ()); + if (sslCtx) + m_SSL = std::make_shared > (*m_Socket, *sslCtx); } I2PTunnelConnection::~I2PTunnelConnection () @@ -80,24 +83,26 @@ namespace client } #ifdef __linux__ - static void MapToLoopback(const std::shared_ptr & sock, const i2p::data::IdentHash & addr) + static void MapToLoopback(std::shared_ptr sock, const i2p::data::IdentHash & addr) { - // bind to 127.x.x.x address - // where x.x.x are first three bytes from ident - auto ourIP = GetLoopbackAddressFor(addr); - boost::system::error_code ec; - sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); - if (ec) - LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); - + if (sock) + { + // bind to 127.x.x.x address + // where x.x.x are first three bytes from ident + auto ourIP = GetLoopbackAddressFor(addr); + boost::system::error_code ec; + sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0), ec); + if (ec) + LogPrint (eLogError, "I2PTunnel: Can't bind ourIP to ", ourIP.to_string (), ": ", ec.message ()); + } } #endif void I2PTunnelConnection::Connect (bool isUniqueLocal) { - I2PTunnelSetSocketOptions(m_Socket); if (m_Socket) - { + { + I2PTunnelSetSocketOptions (m_Socket); #ifdef __linux__ if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () && m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127) @@ -127,10 +132,11 @@ namespace client } Connect (false); } - + void I2PTunnelConnection::Terminate () { if (Kill()) return; + if (m_SSL) m_SSL = nullptr; if (m_Stream) { m_Stream->Close (); @@ -145,12 +151,17 @@ namespace client void I2PTunnelConnection::Receive () { - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), - std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); + if (m_SSL) + m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + else + m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE), + std::bind(&I2PTunnelConnection::HandleReceive, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); } - void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) + void I2PTunnelConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) { @@ -239,8 +250,12 @@ namespace client void I2PTunnelConnection::Write (const uint8_t * buf, size_t len) { - boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), - std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); + if (m_SSL) + boost::asio::async_write (*m_SSL, boost::asio::buffer (buf, len), boost::asio::transfer_all (), + std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); + else + boost::asio::async_write (*m_Socket, boost::asio::buffer (buf, len), boost::asio::transfer_all (), + std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1)); } void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode) @@ -253,22 +268,45 @@ namespace client else { LogPrint (eLogDebug, "I2PTunnel: Connected"); - if (m_IsQuiet) - StreamReceive (); - else - { - // send destination first like received from I2P - std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 (); - dest += "\n"; - if(sizeof(m_StreamBuffer) >= dest.size()) { - memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); - } - HandleStreamReceive (boost::system::error_code (), dest.size ()); - } - Receive (); + if (m_SSL) + m_SSL->async_handshake (boost::asio::ssl::stream_base::client, + std::bind (&I2PTunnelConnection::HandleHandshake, shared_from_this (), std::placeholders::_1)); + else + Established (); } } + void I2PTunnelConnection::HandleHandshake (const boost::system::error_code& ecode) + { + if (ecode) + { + LogPrint (eLogError, "I2PTunnel: Handshake error: ", ecode.message ()); + Terminate (); + } + else + { + LogPrint (eLogDebug, "I2PTunnel: SSL connected"); + Established (); + } + } + + void I2PTunnelConnection::Established () + { + if (m_IsQuiet) + StreamReceive (); + else + { + // send destination first like received from I2P + std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 (); + dest += "\n"; + if(sizeof(m_StreamBuffer) >= dest.size()) { + memcpy (m_StreamBuffer, dest.c_str (), dest.size ()); + } + HandleStreamReceive (boost::system::error_code (), dest.size ()); + } + Receive (); + } + void I2PClientTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) { if (m_HeaderSent) @@ -332,11 +370,13 @@ namespace client } I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& host): - I2PTunnelConnection (owner, stream, socket, target), m_Host (host), + const boost::asio::ip::tcp::endpoint& target, const std::string& host, + std::shared_ptr sslCtx): + I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host), m_HeaderSent (false), m_ResponseHeaderSent (false), m_From (stream->GetRemoteIdentity ()) { + if (sslCtx) + SSL_set_tlsext_host_name(GetSSL ()->native_handle(), host.c_str ()); } void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len) @@ -474,9 +514,8 @@ namespace client } I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass): - I2PTunnelConnection (owner, stream, socket, target), m_From (stream->GetRemoteIdentity ()), + I2PTunnelConnection (owner, stream, target), m_From (stream->GetRemoteIdentity ()), m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass) { } @@ -487,7 +526,8 @@ namespace client if (m_NeedsWebIrc) { m_NeedsWebIrc = false; - m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl; + m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) + << " " << GetSocket ()->local_endpoint ().address () << std::endl; } m_InPacket.clear (); @@ -753,6 +793,17 @@ namespace client LogPrint (eLogError, "I2PTunnel: Can't set local address ", localAddress); } + void I2PServerTunnel::SetSSL (bool ssl) + { + if (ssl) + { + m_SSLCtx = std::make_shared (boost::asio::ssl::context::sslv23); + m_SSLCtx->set_verify_mode(boost::asio::ssl::context::verify_none); + } + else + m_SSLCtx = nullptr; + } + void I2PServerTunnel::Accept () { if (m_PortDestination) @@ -793,7 +844,7 @@ namespace client std::shared_ptr I2PServerTunnel::CreateI2PConnection (std::shared_ptr stream) { - return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint ()); + return std::make_shared (this, stream, GetEndpoint (), true, m_SSLCtx); } @@ -807,8 +858,7 @@ namespace client std::shared_ptr I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr stream) { - return std::make_shared (this, stream, - std::make_shared (GetService ()), GetEndpoint (), m_Host); + return std::make_shared (this, stream, GetEndpoint (), m_Host, GetSSLCtx ()); } I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address, @@ -821,7 +871,7 @@ namespace client std::shared_ptr I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr stream) { - return std::make_shared (this, stream, std::make_shared (GetService ()), GetEndpoint (), this->m_WebircPass); + return std::make_shared (this, stream, GetEndpoint (), m_WebircPass); } void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) diff --git a/libi2pd_client/I2PTunnel.h b/libi2pd_client/I2PTunnel.h index 90c5864e..4f61ef91 100644 --- a/libi2pd_client/I2PTunnel.h +++ b/libi2pd_client/I2PTunnel.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "Identity.h" #include "Destination.h" #include "Datagram.h" @@ -44,8 +45,9 @@ namespace client std::shared_ptr leaseSet, int port = 0); // to I2P I2PTunnelConnection (I2PService * owner, std::shared_ptr socket, std::shared_ptr stream); // to I2P using simplified API - I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P + I2PTunnelConnection (I2PService * owner, std::shared_ptr stream, + const boost::asio::ip::tcp::endpoint& target, bool quiet = true, + std::shared_ptr sslCtx = nullptr); // from I2P ~I2PTunnelConnection (); void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); void Connect (bool isUniqueLocal = true); @@ -56,21 +58,27 @@ namespace client void Terminate (); void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void StreamReceive (); virtual void Write (const uint8_t * buf, size_t len); // can be overloaded - void HandleWrite (const boost::system::error_code& ecode); virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded - void StreamReceive (); - void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandleConnect (const boost::system::error_code& ecode); + std::shared_ptr GetSocket () const { return m_Socket; }; + std::shared_ptr > GetSSL () const { return m_SSL; }; - std::shared_ptr GetSocket () const { return m_Socket; }; + private: + void HandleConnect (const boost::system::error_code& ecode); + void HandleHandshake (const boost::system::error_code& ecode); + void Established (); + void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandleWrite (const boost::system::error_code& ecode); + void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); + private: uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; std::shared_ptr m_Socket; + std::shared_ptr > m_SSL; std::shared_ptr m_Stream; boost::asio::ip::tcp::endpoint m_RemoteEndpoint; bool m_IsQuiet; // don't send destination @@ -100,8 +108,8 @@ namespace client public: I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, - const boost::asio::ip::tcp::endpoint& target, const std::string& host); + const boost::asio::ip::tcp::endpoint& target, const std::string& host, + std::shared_ptr sslCtx = nullptr); protected: @@ -121,7 +129,6 @@ namespace client public: I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr stream, - std::shared_ptr socket, const boost::asio::ip::tcp::endpoint& target, const std::string& m_WebircPass); protected: @@ -343,6 +350,9 @@ namespace client void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; } bool IsUniqueLocal () const { return m_IsUniqueLocal; } + void SetSSL (bool ssl); + std::shared_ptr GetSSLCtx () const { return m_SSLCtx; }; + void SetLocalAddress (const std::string& localAddress); const std::string& GetAddress() const { return m_Address; } @@ -371,6 +381,7 @@ namespace client std::set m_AccessList; bool m_IsAccessList; std::unique_ptr m_LocalAddress; + std::shared_ptr m_SSLCtx; }; class I2PServerTunnelHTTP: public I2PServerTunnel