diff --git a/libi2pd/SSU2.cpp b/libi2pd/SSU2.cpp index 7041543b..35334aeb 100644 --- a/libi2pd/SSU2.cpp +++ b/libi2pd/SSU2.cpp @@ -8,6 +8,7 @@ #include #include +#include "Log.h" #include "RouterContext.h" #include "Transports.h" #include "SSU2.h" @@ -19,7 +20,7 @@ namespace transport SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr in_RemoteRouter, std::shared_ptr addr, bool peerTest): TransportSession (in_RemoteRouter, SSU2_TERMINATION_TIMEOUT), - m_Server (server), m_Address (addr) + m_Server (server), m_Address (addr), m_DestConnID (0), m_SourceConnID (0) { m_NoiseState.reset (new i2p::crypto::NoiseSymmetricState); if (in_RemoteRouter && addr) @@ -42,27 +43,28 @@ namespace transport m_NoiseState->MixKey (sharedSecret); Header header; - uint64_t headerX[6]; - uint8_t payload[1200]; // TODO: correct payload size + uint8_t headerX[48], payload[1200]; // TODO: correct payload size size_t payloadSize = 8; // fill packet - RAND_bytes (header.h.connID, 8); + RAND_bytes ((uint8_t *)&m_DestConnID, 8); + header.h.connID = m_DestConnID; // dest id memset (header.h.packetNum, 0, 4); header.h.type = eSSU2SessionRequest; header.h.flags[0] = 2; // ver header.h.flags[1] = 2; // netID TODO: header.h.flags[2] = 0; // flag - RAND_bytes ((uint8_t *)headerX, 8); // source id - memset (headerX + 1, 0, 8); // token - memcpy (headerX + 2, m_EphemeralKeys->GetPublicKey (), 32); // X - m_Server.AddSession (headerX[0], shared_from_this ()); + RAND_bytes ((uint8_t *)&m_SourceConnID, 8); + memcpy (headerX, &m_SourceConnID, 8); // source id + memset (headerX + 8, 0, 8); // token + memcpy (headerX + 16, m_EphemeralKeys->GetPublicKey (), 32); // X + m_Server.AddPendingOutgoingSession (boost::asio::ip::udp::endpoint (m_Address->host, m_Address->port), shared_from_this ()); // encrypt const uint8_t nonce[12] = {0}; i2p::crypto::AEADChaCha20Poly1305 (payload, payloadSize, m_NoiseState->m_H, 32, m_NoiseState->m_CK + 32, nonce, payload, payloadSize + 16, true); payloadSize += 16; CreateHeaderMask (m_Address->i, payload + (payloadSize - 24), m_Address->i, payload + (payloadSize - 12)); EncryptHeader (header); - i2p::crypto::ChaCha20 ((uint8_t *)headerX, 48, m_Address->i, nonce, (uint8_t *)headerX); + i2p::crypto::ChaCha20 (headerX, 48, m_Address->i, nonce, headerX); } @@ -80,12 +82,39 @@ namespace transport i2p::crypto::ChaCha20 (data, 8, kh2, nonce2, m_HeaderMask.buf + 8); } + SSU2Server::SSU2Server (int port): + m_Socket (m_Service), m_Endpoint (boost::asio::ip::udp::v6 (), port) + { + } + + void SSU2Server::OpenSocket () + { + try + { + m_Socket.open (boost::asio::ip::udp::v6()); + m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU2_SOCKET_RECEIVE_BUFFER_SIZE)); + m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU2_SOCKET_SEND_BUFFER_SIZE)); + m_Socket.bind (m_Endpoint); + LogPrint (eLogInfo, "SSU2: Start listening port ", m_Endpoint.port()); + } + catch (std::exception& ex ) + { + LogPrint (eLogError, "SSU2: Failed to bind to port ", m_Endpoint.port(), ": ", ex.what()); + ThrowFatal ("Unable to start SSU2 transport at port ", m_Endpoint.port(), ": ", ex.what ()); + } + } + void SSU2Server::AddSession (uint64_t connID, std::shared_ptr session) { m_Sessions.emplace (connID, session); } - void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len) + void SSU2Server::AddPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, std::shared_ptr session) + { + m_PendingOutgoingSessions.emplace (ep, session); + } + + void SSU2Server::ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint) { uint64_t key = 0, connID; i2p::crypto::ChaCha20 ((uint8_t *)&key, 8, i2p::context.GetNTCP2IV (), buf + (len - 24), (uint8_t *)&key); // TODO: use SSU2 intro key @@ -95,6 +124,33 @@ namespace transport if (it != m_Sessions.end ()) { } + else + { + // check pending sessions if it's SessionCreated + auto it1 = m_PendingOutgoingSessions.find (senderEndpoint); + if (it1 != m_PendingOutgoingSessions.end ()) + { + m_PendingOutgoingSessions.erase (it1); + } + else + { + // assume new incoming session + } + } } + + void SSU2Server::Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, + const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to) + { + std::vector bufs + { + boost::asio::buffer (header, headerLen), + boost::asio::buffer (headerX, headerXLen), + boost::asio::buffer (payload, payloadLen) + }; + boost::system::error_code ec; + m_Socket.send_to (bufs, to, 0, ec); + } + } } diff --git a/libi2pd/SSU2.h b/libi2pd/SSU2.h index 9897078d..8b015fb1 100644 --- a/libi2pd/SSU2.h +++ b/libi2pd/SSU2.h @@ -10,7 +10,9 @@ #define SSU2_H__ #include +#include #include +#include #include "Crypto.h" #include "RouterInfo.h" #include "TransportSession.h" @@ -20,7 +22,9 @@ namespace i2p namespace transport { const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes - + const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K + const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K + enum SSU2MessageType { eSSU2SessionRequest = 0 @@ -35,7 +39,7 @@ namespace transport uint8_t buf[16]; struct { - uint8_t connID[8]; + uint64_t connID; uint8_t packetNum[4]; uint8_t type; uint8_t flags[3]; @@ -60,7 +64,8 @@ namespace transport std::shared_ptr m_EphemeralKeys; std::unique_ptr m_NoiseState; std::shared_ptr m_Address; - + uint64_t m_DestConnID, m_SourceConnID; + union { uint64_t ll[2]; @@ -72,18 +77,27 @@ namespace transport { public: - SSU2Server (int port) {}; + SSU2Server (int port); ~SSU2Server () {}; void AddSession (uint64_t connID, std::shared_ptr session); + void AddPendingOutgoingSession (const boost::asio::ip::udp::endpoint& ep, std::shared_ptr session); + void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen, + const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to); + private: - void ProcessNextPacket (uint8_t * buf, size_t len); + void OpenSocket (); + void ProcessNextPacket (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); private: + boost::asio::io_service m_Service; + boost::asio::ip::udp::socket m_Socket; + boost::asio::ip::udp::endpoint m_Endpoint; std::unordered_map > m_Sessions; + std::map > m_PendingOutgoingSessions; }; } }