From faae2709d9e8ba7fede3ae73228dec493250cd17 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 3 Oct 2020 21:58:20 -0400 Subject: [PATCH] removed NTCP --- build/CMakeLists.txt | 1 - daemon/Daemon.cpp | 60 +- daemon/HTTPServer.cpp | 7 - libi2pd/NTCP2.cpp | 2 +- libi2pd/NTCPSession.cpp | 1317 --------------------------------------- libi2pd/NTCPSession.h | 242 ------- libi2pd/Transports.cpp | 70 +-- libi2pd/Transports.h | 6 +- qt/i2pd_qt/i2pd_qt.pro | 2 - 9 files changed, 30 insertions(+), 1677 deletions(-) delete mode 100644 libi2pd/NTCPSession.cpp delete mode 100644 libi2pd/NTCPSession.h diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 8271ce3a..4825855b 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -68,7 +68,6 @@ set(LIBI2PD_SRC "${LIBI2PD_SRC_DIR}/NetDb.cpp" "${LIBI2PD_SRC_DIR}/NetDbRequests.cpp" "${LIBI2PD_SRC_DIR}/NTCP2.cpp" - "${LIBI2PD_SRC_DIR}/NTCPSession.cpp" "${LIBI2PD_SRC_DIR}/Poly1305.cpp" "${LIBI2PD_SRC_DIR}/Profiling.cpp" "${LIBI2PD_SRC_DIR}/Reseed.cpp" diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 264013cf..5f98d47a 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -1,11 +1,3 @@ -/* -* Copyright (c) 2013-2020, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - #include #include @@ -17,7 +9,6 @@ #include "Base.h" #include "version.h" #include "Transports.h" -#include "NTCPSession.h" #include "RouterInfo.h" #include "RouterContext.h" #include "Tunnel.h" @@ -135,8 +126,8 @@ namespace i2p i2p::context.SetNetID (netID); i2p::context.Init (); - bool ipv6; i2p::config::GetOption("ipv6", ipv6); - bool ipv4; i2p::config::GetOption("ipv4", ipv4); + bool ipv6; i2p::config::GetOption("ipv6", ipv6); + bool ipv4; i2p::config::GetOption("ipv4", ipv4); #ifdef MESHNET // manual override for meshnet ipv4 = false; @@ -151,8 +142,8 @@ namespace i2p i2p::context.SetSupportsV6 (ipv6); i2p::context.SetSupportsV4 (ipv4); - bool ntcp; i2p::config::GetOption("ntcp", ntcp); - i2p::context.PublishNTCPAddress (ntcp, !ipv6); + bool ntcp; i2p::config::GetOption("ntcp", ntcp); + i2p::context.PublishNTCPAddress (ntcp, !ipv6); bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) { @@ -180,13 +171,10 @@ namespace i2p SetMaxNumTransitTunnels (transitTunnels); bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); - if (isFloodfill) - { + if (isFloodfill) { LogPrint(eLogInfo, "Daemon: router will be floodfill"); i2p::context.SetFloodfill (true); - } - else - { + } else { i2p::context.SetFloodfill (false); } @@ -254,8 +242,7 @@ namespace i2p i2p::transport::transports.RestrictRoutesToFamilies(fams); restricted = fams.size() > 0; } - if (routers.length() > 0) - { + if (routers.length() > 0) { std::set idents; size_t pos = 0, comma; do @@ -291,8 +278,7 @@ namespace i2p i2p::data::netdb.Start(); bool upnp; i2p::config::GetOption("upnp.enabled", upnp); - if (upnp) - { + if (upnp) { d.UPnP = std::unique_ptr(new i2p::transport::UPnP); d.UPnP->Start (); } @@ -304,16 +290,16 @@ namespace i2p d.m_NTPSync->Start (); } - bool ntcp; i2p::config::GetOption("ntcp", ntcp); - bool ssu; i2p::config::GetOption("ssu", ssu); + bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); + bool ssu; i2p::config::GetOption("ssu", ssu); LogPrint(eLogInfo, "Daemon: starting Transports"); if(!ssu) LogPrint(eLogInfo, "Daemon: ssu disabled"); - if(!ntcp) LogPrint(eLogInfo, "Daemon: ntcp disabled"); + if(!ntcp2) LogPrint(eLogInfo, "Daemon: ntcp2 disabled"); - i2p::transport::transports.Start(ntcp, ssu); - if (i2p::transport::transports.IsBoundNTCP() || i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) + i2p::transport::transports.Start(ntcp2, ssu); + if (i2p::transport::transports.IsBoundSSU() || i2p::transport::transports.IsBoundNTCP2()) LogPrint(eLogInfo, "Daemon: Transports started"); - else + else { LogPrint(eLogError, "Daemon: failed to start Transports"); /** shut down netdb right away */ @@ -323,23 +309,23 @@ namespace i2p } bool http; i2p::config::GetOption("http.enabled", http); - if (http) - { + if (http) { std::string httpAddr; i2p::config::GetOption("http.address", httpAddr); uint16_t httpPort; i2p::config::GetOption("http.port", httpPort); LogPrint(eLogInfo, "Daemon: starting webconsole at ", httpAddr, ":", httpPort); - try + try { d.httpServer = std::unique_ptr(new i2p::http::HTTPServer(httpAddr, httpPort)); d.httpServer->Start(); - } - catch (std::exception& ex) + } + catch (std::exception& ex) { LogPrint (eLogError, "Daemon: failed to start webconsole: ", ex.what ()); ThrowFatal ("Unable to start webconsole at ", httpAddr, ":", httpPort, ": ", ex.what ()); } } + LogPrint(eLogInfo, "Daemon: starting Tunnels"); i2p::tunnel::tunnels.Start(); @@ -352,12 +338,12 @@ namespace i2p std::string i2pcpAddr; i2p::config::GetOption("i2pcontrol.address", i2pcpAddr); uint16_t i2pcpPort; i2p::config::GetOption("i2pcontrol.port", i2pcpPort); LogPrint(eLogInfo, "Daemon: starting I2PControl at ", i2pcpAddr, ":", i2pcpPort); - try + try { d.m_I2PControlService = std::unique_ptr(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort)); d.m_I2PControlService->Start (); - } - catch (std::exception& ex) + } + catch (std::exception& ex) { LogPrint (eLogError, "Daemon: failed to start I2PControl: ", ex.what ()); ThrowFatal ("Unable to start I2PControl service at ", i2pcpAddr, ":", i2pcpPort, ": ", ex.what ()); @@ -374,7 +360,7 @@ namespace i2p LogPrint(eLogInfo, "Daemon: stopping Tunnels"); i2p::tunnel::tunnels.Stop(); - if (d.UPnP) + if (d.UPnP) { d.UPnP->Stop (); d.UPnP = nullptr; diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 52b655b9..19734038 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -732,13 +732,6 @@ namespace http { void ShowTransports (std::stringstream& s) { s << "Transports:
\r\n"; - auto ntcpServer = i2p::transport::transports.GetNTCPServer (); - if (ntcpServer) - { - auto sessions = ntcpServer->GetNTCPSessions (); - if (!sessions.empty ()) - ShowNTCPTransports (s, sessions, "NTCP"); - } auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); if (ntcp2Server) { diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index 4e3b219c..cdf660b7 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -1435,7 +1435,7 @@ namespace transport { auto timer = std::make_shared(GetService()); - auto timeout = NTCP_CONNECT_TIMEOUT * 5; + auto timeout = NTCP2_CONNECT_TIMEOUT * 5; conn->SetTerminationTimeout(timeout * 2); timer->expires_from_now (boost::posix_time::seconds(timeout)); timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp deleted file mode 100644 index 4d3f1da6..00000000 --- a/libi2pd/NTCPSession.cpp +++ /dev/null @@ -1,1317 +0,0 @@ -/* -* Copyright (c) 2013-2020, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#include -#include -#include - -#include "I2PEndian.h" -#include "Base.h" -#include "Crypto.h" -#include "Log.h" -#include "Timestamp.h" -#include "I2NPProtocol.h" -#include "RouterContext.h" -#include "Transports.h" -#include "NetDb.hpp" -#include "NTCPSession.h" -#include "HTTP.h" -#include "util.h" - -using namespace i2p::crypto; - -namespace i2p -{ -namespace transport -{ - - struct NTCPWork - { - std::shared_ptr session; - }; - - NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter): - TransportSession (in_RemoteRouter, NTCP_ESTABLISH_TIMEOUT), - m_Server (server), m_Socket (m_Server.GetService ()), - m_IsEstablished (false), m_IsTerminated (false), - m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false) - { - m_Establisher = new Establisher; - } - - NTCPSession::~NTCPSession () - { - delete m_Establisher; - } - - void NTCPSession::CreateAESKey (uint8_t * pubKey) - { - uint8_t sharedKey[256]; - m_DHKeysPair->Agree (pubKey, sharedKey); // time consuming operation - - i2p::crypto::AESKey aesKey; - if (sharedKey[0] & 0x80) - { - aesKey[0] = 0; - memcpy (aesKey + 1, sharedKey, 31); - } - else if (sharedKey[0]) - memcpy (aesKey, sharedKey, 32); - else - { - // find first non-zero byte - uint8_t * nonZero = sharedKey + 1; - while (!*nonZero) - { - nonZero++; - if (nonZero - sharedKey > 32) - { - LogPrint (eLogWarning, "NTCP: First 32 bytes of shared key is all zeros, ignored"); - return; - } - } - memcpy (aesKey, nonZero, 32); - } - - m_Decryption.SetKey (aesKey); - m_Encryption.SetKey (aesKey); - } - - void NTCPSession::Done () - { - m_Server.GetService ().post (std::bind (&NTCPSession::Terminate, shared_from_this ())); - } - - void NTCPSession::Terminate () - { - if (!m_IsTerminated) - { - m_IsTerminated = true; - m_IsEstablished = false; - m_Socket.close (); - transports.PeerDisconnected (shared_from_this ()); - m_Server.RemoveNTCPSession (shared_from_this ()); - m_SendQueue.clear (); - m_NextMessage = nullptr; - LogPrint (eLogDebug, "NTCP: session terminated"); - } - } - - void NTCPSession::Connected () - { - m_IsEstablished = true; - - delete m_Establisher; - m_Establisher = nullptr; - - m_DHKeysPair = nullptr; - - SetTerminationTimeout (NTCP_TERMINATION_TIMEOUT); - SendTimeSyncMessage (); - transports.PeerConnected (shared_from_this ()); - } - - boost::asio::io_service & NTCPSession::GetService() - { - return m_Server.GetService(); - } - - void NTCPSession::ClientLogin () - { - if (!m_DHKeysPair) - m_DHKeysPair = transports.GetNextDHKeysPair (); - // send Phase1 - const uint8_t * x = m_DHKeysPair->GetPublicKey (); - memcpy (m_Establisher->phase1.pubKey, x, 256); - SHA256(x, 256, m_Establisher->phase1.HXxorHI); - const uint8_t * ident = m_RemoteIdentity->GetIdentHash (); - for (int i = 0; i < 32; i++) - m_Establisher->phase1.HXxorHI[i] ^= ident[i]; - - boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - - void NTCPSession::ServerLogin () - { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - // receive Phase1 - boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogInfo, "NTCP: couldn't send Phase 1 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - } - - void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogInfo, "NTCP: phase 1 read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - // verify ident - uint8_t digest[32]; - SHA256(m_Establisher->phase1.pubKey, 256, digest); - const uint8_t * ident = i2p::context.GetIdentHash (); - for (int i = 0; i < 32; i++) - { - if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i]) - { - LogPrint (eLogError, "NTCP: phase 1 error: ident mismatch"); - Terminate (); - return; - } - } - // TODO: check for number of pending keys - auto work = new NTCPWork{shared_from_this()}; - m_Server.Work(work->session, [work, this]() -> std::function { - if (!work->session->m_DHKeysPair) - work->session->m_DHKeysPair = transports.GetNextDHKeysPair (); - work->session->CreateAESKey (work->session->m_Establisher->phase1.pubKey); - return std::bind(&NTCPSession::SendPhase2, work->session, work); - }); - } - } - - void NTCPSession::SendPhase2 (NTCPWork * work) - { - if(work) - delete work; - const uint8_t * y = m_DHKeysPair->GetPublicKey (); - memcpy (m_Establisher->phase2.pubKey, y, 256); - uint8_t xy[512]; - memcpy (xy, m_Establisher->phase1.pubKey, 256); - memcpy (xy + 256, y, 256); - SHA256(xy, 512, m_Establisher->phase2.encrypted.hxy); - uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ()); - memcpy (m_Establisher->phase2.encrypted.timestamp, &tsB, 4); - RAND_bytes (m_Establisher->phase2.encrypted.filler, 12); - - m_Encryption.SetIV (y + 240); - m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16); - - m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); - boost::asio::async_write(m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all(), - std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this(), std::placeholders::_1, std::placeholders::_2, tsB)); - } - - void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogInfo, "NTCP: Couldn't send Phase 2 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, tsB)); - } - } - - void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogInfo, "NTCP: Phase 2 read error: ", ecode.message (), ". Wrong ident assumed"); - if (ecode != boost::asio::error::operation_aborted) - { - // this RI is not valid - i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); - transports.ReuseDHKeysPair (m_DHKeysPair); - m_DHKeysPair = nullptr; - Terminate (); - } - } - else - { - auto work = new NTCPWork{shared_from_this()}; - m_Server.Work(work->session, [work, this]() -> std::function { - work->session->CreateAESKey (work->session->m_Establisher->phase2.pubKey); - return std::bind(&NTCPSession::HandlePhase2, work->session, work); - }); - } - } - - void NTCPSession::HandlePhase2 (NTCPWork * work) - { - if(work) delete work; - m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); - m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); - - m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); - // verify - uint8_t xy[512]; - memcpy (xy, m_DHKeysPair->GetPublicKey (), 256); - memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); - uint8_t digest[32]; - SHA256 (xy, 512, digest); - if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32)) - { - LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash"); - transports.ReuseDHKeysPair (m_DHKeysPair); - m_DHKeysPair = nullptr; - Terminate (); - return ; - } - SendPhase3 (); - } - - void NTCPSession::SendPhase3 () - { - auto& keys = i2p::context.GetPrivateKeys (); - uint8_t * buf = m_ReceiveBuffer; - htobe16buf (buf, keys.GetPublic ()->GetFullLen ()); - buf += 2; - buf += i2p::context.GetIdentity ()->ToBuffer (buf, NTCP_BUFFER_SIZE); - uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ()); - htobuf32(buf,tsA); - buf += 4; - size_t signatureLen = keys.GetPublic ()->GetSignatureLen (); - size_t len = (buf - m_ReceiveBuffer) + signatureLen; - size_t paddingSize = len & 0x0F; // %16 - if (paddingSize > 0) - { - paddingSize = 16 - paddingSize; - // fill padding with random data - RAND_bytes(buf, paddingSize); - buf += paddingSize; - len += paddingSize; - } - - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB - s.Sign (keys, buf); - - m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer); - boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA)); - } - - void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogInfo, "NTCP: Couldn't send Phase 3 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - // wait for phase4 - auto signatureLen = m_RemoteIdentity->GetSignatureLen (); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase4Received, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, tsA)); - } - } - - void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB) - { - if (ecode) - { - LogPrint (eLogInfo, "NTCP: Phase 3 read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); - uint8_t * buf = m_ReceiveBuffer; - uint16_t size = bufbe16toh (buf); - auto identity = std::make_shared (buf + 2, size); - if (m_Server.FindNTCPSession (identity->GetIdentHash ())) - { - LogPrint (eLogInfo, "NTCP: session already exists"); - Terminate (); - } - auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already - SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity); - - size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen (); - size_t paddingLen = expectedSize & 0x0F; - if (paddingLen) paddingLen = (16 - paddingLen); - if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE) - { - // we need more bytes for Phase3 - expectedSize += paddingLen; - boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize - NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2, tsB, paddingLen)); - } - else - HandlePhase3 (tsB, paddingLen); - } - } - - void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen) - { - if (ecode) - { - LogPrint (eLogInfo, "NTCP: Phase 3 extra read error: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE); - HandlePhase3 (tsB, paddingLen); - } - } - - void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen) - { - uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity->GetFullLen () + 2 /*size*/; - uint32_t tsA = buf32toh(buf); - buf += 4; - buf += paddingLen; - - // check timestamp - auto ts = i2p::util::GetSecondsSinceEpoch (); - uint32_t tsA1 = be32toh (tsA); - if (tsA1 < ts - NTCP_CLOCK_SKEW || tsA1 > ts + NTCP_CLOCK_SKEW) - { - LogPrint (eLogError, "NTCP: Phase3 time difference ", (int)(ts - tsA1), " exceeds clock skew"); - Terminate (); - return; - } - - // check signature - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (tsB); // tsB - if (!s.Verify (m_RemoteIdentity, buf)) - { - LogPrint (eLogError, "NTCP: signature verification failed"); - Terminate (); - return; - } - - SendPhase4 (tsA, tsB); - } - - void NTCPSession::SendPhase4 (uint32_t tsA, uint32_t tsB) - { - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (tsB); // tsB - auto& keys = i2p::context.GetPrivateKeys (); - auto signatureLen = keys.GetPublic ()->GetSignatureLen (); - s.Sign (keys, m_ReceiveBuffer); - size_t paddingSize = signatureLen & 0x0F; // %16 - if (paddingSize > 0) signatureLen += (16 - paddingSize); - m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer); - - boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } - - void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - (void) bytes_transferred; - if (ecode) - { - LogPrint (eLogWarning, "NTCP: Couldn't send Phase 4 message: ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP: Server session from ", m_Socket.remote_endpoint (), " connected"); - m_Server.AddNTCPSession (shared_from_this ()); - - Connected (); - m_ReceiveBufferOffset = 0; - m_NextMessage = nullptr; - Receive (); - } - } - - void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA) - { - if (ecode) - { - LogPrint (eLogError, "NTCP: Phase 4 read error: ", ecode.message (), ". Check your clock"); - if (ecode != boost::asio::error::operation_aborted) - { - // this router doesn't like us - i2p::data::netdb.SetUnreachable (GetRemoteIdentity ()->GetIdentHash (), true); - Terminate (); - } - } - else - { - m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer); - - // check timestamp - uint32_t tsB = bufbe32toh (m_Establisher->phase2.encrypted.timestamp); - auto ts = i2p::util::GetSecondsSinceEpoch (); - if (tsB < ts - NTCP_CLOCK_SKEW || tsB > ts + NTCP_CLOCK_SKEW) - { - LogPrint (eLogError, "NTCP: Phase4 time difference ", (int)(ts - tsB), " exceeds clock skew"); - Terminate (); - return; - } - - // verify signature - SignedData s; - s.Insert (m_Establisher->phase1.pubKey, 256); // x - s.Insert (m_Establisher->phase2.pubKey, 256); // y - s.Insert (i2p::context.GetIdentHash (), 32); // ident - s.Insert (tsA); // tsA - s.Insert (m_Establisher->phase2.encrypted.timestamp, 4); // tsB - - if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer)) - { - LogPrint (eLogError, "NTCP: Phase 4 process error: signature verification failed"); - Terminate (); - return; - } - LogPrint (eLogDebug, "NTCP: session to ", m_Socket.remote_endpoint (), " connected"); - Connected (); - - m_ReceiveBufferOffset = 0; - m_NextMessage = nullptr; - Receive (); - } - } - - void NTCPSession::Receive () - { - m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, NTCP_BUFFER_SIZE - m_ReceiveBufferOffset), - std::bind(&NTCPSession::HandleReceived, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } - - void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) - { - if (ecode) - { - if (ecode != boost::asio::error::operation_aborted) - LogPrint (eLogDebug, "NTCP: Read error: ", ecode.message ()); - //if (ecode != boost::asio::error::operation_aborted) - Terminate (); - } - else - { - m_NumReceivedBytes += bytes_transferred; - i2p::transport::transports.UpdateReceivedBytes (bytes_transferred); - m_ReceiveBufferOffset += bytes_transferred; - - if (m_ReceiveBufferOffset >= 16) - { - // process received data - uint8_t * nextBlock = m_ReceiveBuffer; - while (m_ReceiveBufferOffset >= 16) - { - if (!DecryptNextBlock (nextBlock)) // 16 bytes - { - Terminate (); - return; - } - nextBlock += 16; - m_ReceiveBufferOffset -= 16; - } - if (m_ReceiveBufferOffset > 0) - memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); - } - - // read and process more is available - boost::system::error_code ec; - size_t moreBytes = m_Socket.available(ec); - if (moreBytes && !ec) - { - uint8_t * buf = nullptr, * moreBuf = m_ReceiveBuffer; - if (moreBytes + m_ReceiveBufferOffset > NTCP_BUFFER_SIZE) - { - buf = new uint8_t[moreBytes + m_ReceiveBufferOffset + 16]; - moreBuf = buf; - uint8_t rem = ((size_t)buf) & 0x0f; - if (rem) moreBuf += (16 - rem); // align 16 - if (m_ReceiveBufferOffset) - memcpy (moreBuf, m_ReceiveBuffer, m_ReceiveBufferOffset); - } - moreBytes = m_Socket.read_some (boost::asio::buffer (moreBuf + m_ReceiveBufferOffset, moreBytes), ec); - if (ec) - { - LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ()); - delete[] buf; - Terminate (); - return; - } - m_ReceiveBufferOffset += moreBytes; - m_NumReceivedBytes += moreBytes; - i2p::transport::transports.UpdateReceivedBytes (moreBytes); - // process more data - uint8_t * nextBlock = moreBuf; - while (m_ReceiveBufferOffset >= 16) - { - if (!DecryptNextBlock (nextBlock)) // 16 bytes - { - delete[] buf; - Terminate (); - return; - } - nextBlock += 16; - m_ReceiveBufferOffset -= 16; - } - if (m_ReceiveBufferOffset > 0) - memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); // nextBlock points to memory inside buf - delete[] buf; - } - m_Handler.Flush (); - - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - Receive (); - } - } - - bool NTCPSession::DecryptNextBlock (const uint8_t * encrypted) // 16 bytes - { - if (!m_NextMessage) // new message, header expected - { - // decrypt header and extract length - uint8_t buf[16]; - m_Decryption.Decrypt (encrypted, buf); - uint16_t dataSize = bufbe16toh (buf); - if (dataSize) - { - // new message - if (dataSize + 16U + 15U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding - { - LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size"); - return false; - } - m_NextMessage = (dataSize + 16U + 15U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage (); - m_NextMessage->Align (16); - m_NextMessage->offset += 2; // size field - m_NextMessage->len = m_NextMessage->offset + dataSize; - memcpy (m_NextMessage->GetBuffer () - 2, buf, 16); - m_NextMessageOffset = 16; - } - else - { - // timestamp - int diff = (int)bufbe32toh (buf + 2) - (int)i2p::util::GetSecondsSinceEpoch (); - LogPrint (eLogInfo, "NTCP: Timestamp. Time difference ", diff, " seconds"); - return true; - } - } - else // message continues - { - m_Decryption.Decrypt (encrypted, m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset); - m_NextMessageOffset += 16; - } - - if (m_NextMessageOffset >= m_NextMessage->GetLength () + 2 + 4) // +checksum - { - // we have a complete I2NP message - uint8_t checksum[4]; - htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->GetBuffer () - 2, m_NextMessageOffset - 4)); - if (!memcmp (m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset - 4, checksum, 4)) - { - if (!m_NextMessage->IsExpired ()) - { - m_Handler.PutNextMessage (m_NextMessage); - } - else - LogPrint (eLogInfo, "NTCP: message expired"); - } - else - LogPrint (eLogWarning, "NTCP: Incorrect adler checksum of message, dropped"); - m_NextMessage = nullptr; - } - return true; - } - - void NTCPSession::Send (std::shared_ptr msg) - { - m_IsSending = true; - boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector >{ msg })); - } - - boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr msg) - { - uint8_t * sendBuffer; - int len; - - if (msg) - { - // regular I2NP - if (msg->offset < 2) - LogPrint (eLogError, "NTCP: Malformed I2NP message"); // TODO: - sendBuffer = msg->GetBuffer () - 2; - len = msg->GetLength (); - htobe16buf (sendBuffer, len); - } - else - { - // prepare timestamp - sendBuffer = m_TimeSyncBuffer; - len = 4; - htobuf16(sendBuffer, 0); - htobe32buf (sendBuffer + 2, i2p::util::GetSecondsSinceEpoch ()); - } - int rem = (len + 6) & 0x0F; // %16 - int padding = 0; - if (rem > 0) { - padding = 16 - rem; - // fill with random padding - RAND_bytes(sendBuffer + len + 2, padding); - } - htobe32buf (sendBuffer + len + 2 + padding, adler32 (adler32 (0, Z_NULL, 0), sendBuffer, len + 2+ padding)); - - int l = len + padding + 6; - m_Encryption.Encrypt(sendBuffer, l, sendBuffer); - return boost::asio::buffer ((const uint8_t *)sendBuffer, l); - } - - - void NTCPSession::Send (const std::vector >& msgs) - { - m_IsSending = true; - std::vector bufs; - for (const auto& it: msgs) - bufs.push_back (CreateMsgBuffer (it)); - boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (), - std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs)); - } - - void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs) - { - (void) msgs; - m_IsSending = false; - if (ecode) - { - LogPrint (eLogWarning, "NTCP: Couldn't send msgs: ", ecode.message ()); - // we shouldn't call Terminate () here, because HandleReceive takes care - // TODO: 'delete this' statement in Terminate () must be eliminated later - // Terminate (); - } - else - { - m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); - m_NumSentBytes += bytes_transferred; - i2p::transport::transports.UpdateSentBytes (bytes_transferred); - if (!m_SendQueue.empty()) - { - Send (m_SendQueue); - m_SendQueue.clear (); - } - } - } - - void NTCPSession::SendTimeSyncMessage () - { - Send (nullptr); - } - - void NTCPSession::SendI2NPMessages (const std::vector >& msgs) - { - m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs)); - } - - void NTCPSession::PostI2NPMessages (std::vector > msgs) - { - if (m_IsTerminated) return; - if (m_IsSending) - { - if (m_SendQueue.size () < NTCP_MAX_OUTGOING_QUEUE_SIZE) - { - for (const auto& it: msgs) - m_SendQueue.push_back (it); - } - else - { - LogPrint (eLogWarning, "NTCP: outgoing messages queue size exceeds ", NTCP_MAX_OUTGOING_QUEUE_SIZE); - Terminate (); - } - } - else - Send (msgs); - } - -//----------------------------------------- - NTCPServer::NTCPServer (int workers): - m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), - m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr), - m_ProxyType(eNoProxy), m_Resolver(m_Service), m_ProxyEndpoint(nullptr), - m_SoftLimit(0), m_HardLimit(0) - { - if(workers <= 0) workers = 1; - m_CryptoPool = std::make_shared(workers); - } - - NTCPServer::~NTCPServer () - { - Stop (); - } - - void NTCPServer::Start () - { - if (!m_IsRunning) - { - m_IsRunning = true; - m_Thread = new std::thread (std::bind (&NTCPServer::Run, this)); - // we are using a proxy, don't create any acceptors - if(UsingProxy()) - { - // TODO: resolve proxy until it is resolved - boost::asio::ip::tcp::resolver::query q(m_ProxyAddress, std::to_string(m_ProxyPort)); - boost::system::error_code e; - auto itr = m_Resolver.resolve(q, e); - if(e) - { - LogPrint(eLogError, "NTCP: Failed to resolve proxy ", e.message()); - } - else - { - m_ProxyEndpoint = new boost::asio::ip::tcp::endpoint(*itr); - } - } - else - { - // create acceptors - auto& addresses = context.GetRouterInfo ().GetAddresses (); - for (const auto& address: addresses) - { - if (!address) continue; - if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP && !address->IsNTCP2 ()) - { - if (address->host.is_v4()) - { - try - { - m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address->port)); - LogPrint (eLogInfo, "NTCP: Start listening v6 TCP port ", address->port); - } - catch ( std::exception & ex ) - { - /** fail to bind ip4 */ - LogPrint(eLogError, "NTCP: Failed to bind to v4 port ", address->port, ": ", ex.what()); - ThrowFatal ("Unable to start IPv4 NTCP transport at port ", address->port, ": ", ex.what ()); - continue; - } - - LogPrint (eLogInfo, "NTCP: Start listening TCP port ", address->port); - auto conn = std::make_shared(*this); - m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, conn, std::placeholders::_1)); - } - else if (address->host.is_v6() && context.SupportsV6 ()) - { - m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service); - try - { - m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6()); - m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true)); - m_NTCPV6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); - m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port)); - m_NTCPV6Acceptor->listen (); - - LogPrint (eLogInfo, "NTCP: Start listening v6 TCP port ", address->port); - auto conn = std::make_shared (*this); - m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this, conn, std::placeholders::_1)); - } - catch ( std::exception & ex ) - { - LogPrint(eLogError, "NTCP: failed to bind to v6 port ", address->port, ": ", ex.what()); - ThrowFatal (eLogError, "Unable to start IPv6 NTCP transport at port ", address->port, ": ", ex.what ()); - continue; - } - } - } - } - } - ScheduleTermination (); - } - } - - void NTCPServer::Stop () - { - { - // we have to copy it because Terminate changes m_NTCPSessions - auto ntcpSessions = m_NTCPSessions; - for (auto& it: ntcpSessions) - it.second->Terminate (); - for (auto& it: m_PendingIncomingSessions) - it->Terminate (); - } - m_NTCPSessions.clear (); - - if (m_IsRunning) - { - m_IsRunning = false; - m_TerminationTimer.cancel (); - if (m_NTCPAcceptor) - { - delete m_NTCPAcceptor; - m_NTCPAcceptor = nullptr; - } - if (m_NTCPV6Acceptor) - { - delete m_NTCPV6Acceptor; - m_NTCPV6Acceptor = nullptr; - } - m_Service.stop (); - if (m_Thread) - { - m_Thread->join (); - delete m_Thread; - m_Thread = nullptr; - } - if(m_ProxyEndpoint) - { - delete m_ProxyEndpoint; - m_ProxyEndpoint = nullptr; - } - } - } - - void NTCPServer::Run () - { - while (m_IsRunning) - { - try - { - m_Service.run (); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "NTCP: runtime exception: ", ex.what ()); - } - } - } - - bool NTCPServer::AddNTCPSession (std::shared_ptr session) - { - if (!session || !session->GetRemoteIdentity ()) return false; - auto& ident = session->GetRemoteIdentity ()->GetIdentHash (); - auto it = m_NTCPSessions.find (ident); - if (it != m_NTCPSessions.end ()) - { - LogPrint (eLogWarning, "NTCP: session to ", ident.ToBase64 (), " already exists"); - session->Terminate(); - return false; - } - m_NTCPSessions.insert (std::pair >(ident, session)); - return true; - } - - void NTCPServer::RemoveNTCPSession (std::shared_ptr session) - { - if (session && session->GetRemoteIdentity ()) - m_NTCPSessions.erase (session->GetRemoteIdentity ()->GetIdentHash ()); - } - - std::shared_ptr NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident) - { - auto it = m_NTCPSessions.find (ident); - if (it != m_NTCPSessions.end ()) - return it->second; - return nullptr; - } - - void NTCPServer::HandleAccept (std::shared_ptr conn, const boost::system::error_code& error) - { - if (!error) - { - boost::system::error_code ec; - auto ep = conn->GetSocket ().remote_endpoint(ec); - if (!ec) - { - if(ShouldLimit()) - { - // hit limit, close premature - LogPrint(eLogWarning, "NTCP: limiting with backoff session from ", ep); - conn->Terminate(); - return; - } - LogPrint (eLogDebug, "NTCP: Connected from ", ep); - if (conn) - { - conn->ServerLogin (); - m_PendingIncomingSessions.push_back (conn); - } - } - else - LogPrint (eLogError, "NTCP: Connected from error ", ec.message ()); - } - - - if (error != boost::asio::error::operation_aborted) - { - conn = std::make_shared (*this); - m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this, - conn, std::placeholders::_1)); - } - } - - void NTCPServer::HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error) - { - if (!error) - { - boost::system::error_code ec; - auto ep = conn->GetSocket ().remote_endpoint(ec); - if (!ec) - { - if(ShouldLimit()) - { - // hit limit, close premature - LogPrint(eLogWarning, "NTCP: limiting with backoff on session from ", ep); - conn->Terminate(); - return; - } - - LogPrint (eLogDebug, "NTCP: Connected from ", ep); - if (conn) - { - conn->ServerLogin (); - m_PendingIncomingSessions.push_back (conn); - } - } - else - LogPrint (eLogError, "NTCP: Connected from error ", ec.message ()); - } - - if (error != boost::asio::error::operation_aborted) - { - conn = std::make_shared (*this); - m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this, - conn, std::placeholders::_1)); - } - } - - void NTCPServer::Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn) - { - LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port); - m_Service.post([=]() { - if (this->AddNTCPSession (conn)) - { - - auto timer = std::make_shared(m_Service); - timer->expires_from_now (boost::posix_time::seconds(NTCP_CONNECT_TIMEOUT)); - timer->async_wait ([conn](const boost::system::error_code& ecode) { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogInfo, "NTCP: Not connected in ", NTCP_CONNECT_TIMEOUT, " seconds"); - conn->Terminate (); - } - }); - conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port), std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn, timer)); - } - }); - } - - void NTCPServer::ConnectWithProxy (const std::string& host, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn) - { - if(m_ProxyEndpoint == nullptr) - { - return; - } - m_Service.post([=]() { - if (this->AddNTCPSession (conn)) - { - - auto timer = std::make_shared(m_Service); - auto timeout = NTCP_CONNECT_TIMEOUT * 5; - conn->SetTerminationTimeout(timeout * 2); - timer->expires_from_now (boost::posix_time::seconds(timeout)); - timer->async_wait ([conn, timeout](const boost::system::error_code& ecode) { - if (ecode != boost::asio::error::operation_aborted) - { - LogPrint (eLogInfo, "NTCP: Not connected in ", timeout, " seconds"); - i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); - conn->Terminate (); - } - }); - conn->GetSocket ().async_connect (*m_ProxyEndpoint, std::bind (&NTCPServer::HandleProxyConnect, this, std::placeholders::_1, conn, timer, host, port, addrtype)); - } - }); - } - - void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer) - { - timer->cancel (); - if (ecode) - { - LogPrint (eLogInfo, "NTCP: Connect error ", ecode.message ()); - if (ecode != boost::asio::error::operation_aborted) - i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); - conn->Terminate (); - } - else - { - LogPrint (eLogDebug, "NTCP: Connected to ", conn->GetSocket ().remote_endpoint ()); - conn->ClientLogin (); - } - } - - void NTCPServer::UseProxy(ProxyType proxytype, const std::string & addr, uint16_t port) - { - m_ProxyType = proxytype; - m_ProxyAddress = addr; - m_ProxyPort = port; - } - - void NTCPServer::HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) - { - if(ecode) - { - LogPrint(eLogWarning, "NTCP: failed to connect to proxy ", ecode.message()); - timer->cancel(); - conn->Terminate(); - return; - } - if(m_ProxyType == eSocksProxy) - { - // TODO: support username/password auth etc - uint8_t buff[3] = {0x05, 0x01, 0x00}; - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, 3), boost::asio::transfer_all(), [=] (const boost::system::error_code & ec, std::size_t transferred) { - (void) transferred; - if(ec) - { - LogPrint(eLogWarning, "NTCP: socks5 write error ", ec.message()); - } - }); - uint8_t readbuff[2]; - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff, 2), [=](const boost::system::error_code & ec, std::size_t transferred) { - if(ec) - { - LogPrint(eLogError, "NTCP: socks5 read error ", ec.message()); - timer->cancel(); - conn->Terminate(); - return; - } - else if(transferred == 2) - { - if(readbuff[1] == 0x00) - { - AfterSocksHandshake(conn, timer, host, port, addrtype); - return; - } - else if (readbuff[1] == 0xff) - { - LogPrint(eLogError, "NTCP: socks5 proxy rejected authentication"); - timer->cancel(); - conn->Terminate(); - return; - } - } - LogPrint(eLogError, "NTCP: socks5 server gave invalid response"); - timer->cancel(); - conn->Terminate(); - }); - } - else if(m_ProxyType == eHTTPProxy) - { - i2p::http::HTTPReq req; - req.method = "CONNECT"; - req.version ="HTTP/1.1"; - if(addrtype == eIP6Address) - req.uri = "[" + host + "]:" + std::to_string(port); - else - req.uri = host + ":" + std::to_string(port); - - boost::asio::streambuf writebuff; - std::ostream out(&writebuff); - out << req.to_string(); - - boost::asio::async_write(conn->GetSocket(), writebuff.data(), boost::asio::transfer_all(), [=](const boost::system::error_code & ec, std::size_t transferred) { - (void) transferred; - if(ec) - LogPrint(eLogError, "NTCP: http proxy write error ", ec.message()); - }); - - boost::asio::streambuf * readbuff = new boost::asio::streambuf; - boost::asio::async_read_until(conn->GetSocket(), *readbuff, "\r\n\r\n", [=] (const boost::system::error_code & ec, std::size_t transferred) { - if(ec) - { - LogPrint(eLogError, "NTCP: http proxy read error ", ec.message()); - timer->cancel(); - conn->Terminate(); - } - else - { - readbuff->commit(transferred); - i2p::http::HTTPRes res; - if(res.parse(boost::asio::buffer_cast(readbuff->data()), readbuff->size()) > 0) - { - if(res.code == 200) - { - timer->cancel(); - conn->ClientLogin(); - delete readbuff; - return; - } - else - { - LogPrint(eLogError, "NTCP: http proxy rejected request ", res.code); - } - } - else - LogPrint(eLogError, "NTCP: http proxy gave malformed response"); - timer->cancel(); - conn->Terminate(); - delete readbuff; - } - }); - } - else - LogPrint(eLogError, "NTCP: unknown proxy type, invalid state"); - } - - void NTCPServer::AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType addrtype) - { - // build request - size_t sz = 0; - uint8_t buff[256]; - uint8_t readbuff[256]; - buff[0] = 0x05; - buff[1] = 0x01; - buff[2] = 0x00; - - if(addrtype == eIP4Address) - { - buff[3] = 0x01; - auto addr = boost::asio::ip::address::from_string(host).to_v4(); - auto addrbytes = addr.to_bytes(); - auto addrsize = addrbytes.size(); - memcpy(buff+4, addrbytes.data(), addrsize); - } - else if (addrtype == eIP6Address) - { - buff[3] = 0x04; - auto addr = boost::asio::ip::address::from_string(host).to_v6(); - auto addrbytes = addr.to_bytes(); - auto addrsize = addrbytes.size(); - memcpy(buff+4, addrbytes.data(), addrsize); - } - else if (addrtype == eHostname) - { - buff[3] = 0x03; - size_t addrsize = host.size(); - sz = addrsize + 1 + 4; - if (2 + sz > sizeof(buff)) - { - // too big - return; - } - buff[4] = (uint8_t) addrsize; - memcpy(buff+5, host.c_str(), addrsize); - } - htobe16buf(buff+sz, port); - sz += 2; - boost::asio::async_write(conn->GetSocket(), boost::asio::buffer(buff, sz), boost::asio::transfer_all(), [=](const boost::system::error_code & ec, std::size_t written) { - if(ec) - { - LogPrint(eLogError, "NTCP: failed to write handshake to socks proxy ", ec.message()); - return; - } - }); - - boost::asio::async_read(conn->GetSocket(), boost::asio::buffer(readbuff, 10), [=](const boost::system::error_code & e, std::size_t transferred) { - if(e) - { - LogPrint(eLogError, "NTCP: socks proxy read error ", e.message()); - } - else if(transferred == sz) - { - if( readbuff[1] == 0x00) - { - timer->cancel(); - conn->ClientLogin(); - return; - } - } - if(!e) - i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true); - timer->cancel(); - conn->Terminate(); - }); - } - - void NTCPServer::ScheduleTermination () - { - m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_CHECK_TIMEOUT)); - m_TerminationTimer.async_wait (std::bind (&NTCPServer::HandleTerminationTimer, - this, std::placeholders::_1)); - } - - void NTCPServer::HandleTerminationTimer (const boost::system::error_code& ecode) - { - if (ecode != boost::asio::error::operation_aborted) - { - auto ts = i2p::util::GetSecondsSinceEpoch (); - // established - for (auto& it: m_NTCPSessions) - if (it.second->IsTerminationTimeoutExpired (ts)) - { - auto session = it.second; - // Terminate modifies m_NTCPSession, so we postpone it - m_Service.post ([session] { - LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds"); - session->Terminate (); - }); - } - // pending - for (auto it = m_PendingIncomingSessions.begin (); it != m_PendingIncomingSessions.end ();) - { - if ((*it)->IsEstablished () || (*it)->IsTerminated ()) - it = m_PendingIncomingSessions.erase (it); // established or terminated - else if ((*it)->IsTerminationTimeoutExpired (ts)) - { - (*it)->Terminate (); - it = m_PendingIncomingSessions.erase (it); // expired - } - else - it++; - } - - ScheduleTermination (); - } - } -} -} diff --git a/libi2pd/NTCPSession.h b/libi2pd/NTCPSession.h deleted file mode 100644 index d3aa6f7c..00000000 --- a/libi2pd/NTCPSession.h +++ /dev/null @@ -1,242 +0,0 @@ -/* -* Copyright (c) 2013-2020, The PurpleI2P Project -* -* This file is part of Purple i2pd project and licensed under BSD3 -* -* See full license text in LICENSE file at top of project tree -*/ - -#ifndef NTCP_SESSION_H__ -#define NTCP_SESSION_H__ - -#include -#include -#include -#include -#include -#include -#include "Crypto.h" -#include "Identity.h" -#include "RouterInfo.h" -#include "I2NPProtocol.h" -#include "TransportSession.h" -#include "CryptoWorker.h" - -namespace i2p -{ -namespace transport -{ - struct NTCPPhase1 - { - uint8_t pubKey[256]; - uint8_t HXxorHI[32]; - }; - - struct NTCPPhase2 - { - uint8_t pubKey[256]; - struct - { - uint8_t hxy[32]; - uint8_t timestamp[4]; - uint8_t filler[12]; - } encrypted; - }; - - struct NTCPWork; - - const size_t NTCP_MAX_MESSAGE_SIZE = 16384; - const size_t NTCP_BUFFER_SIZE = 1028; // fits 1 tunnel data message - const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds - const int NTCP_ESTABLISH_TIMEOUT = 10; // 10 seconds - const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes - const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds - const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448 - const int NTCP_CLOCK_SKEW = 60; // in seconds - const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up - - class NTCPServer; - class NTCPSession: public TransportSession, public std::enable_shared_from_this - { - public: - - NTCPSession (NTCPServer& server, std::shared_ptr in_RemoteRouter = nullptr); - ~NTCPSession (); - void Terminate (); - void Done (); - - boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; - boost::asio::io_service & GetService(); - bool IsEstablished () const { return m_IsEstablished; }; - bool IsTerminated () const { return m_IsTerminated; }; - - void ClientLogin (); - void ServerLogin (); - void SendI2NPMessages (const std::vector >& msgs); - - private: - - void PostI2NPMessages (std::vector > msgs); - void Connected (); - void SendTimeSyncMessage (); - void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; } - - void CreateAESKey (uint8_t * pubKey); - - // client - void SendPhase3 (); - void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase2 (NTCPWork * work=nullptr); - void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); - void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); - - //server - void SendPhase2 (NTCPWork * work=nullptr); - void SendPhase4 (uint32_t tsA, uint32_t tsB); - void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB); - void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB); - void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen); - void HandlePhase3 (uint32_t tsB, size_t paddingLen); - void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); - - // common - void Receive (); - void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - bool DecryptNextBlock (const uint8_t * encrypted); - - void Send (std::shared_ptr msg); - boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr msg); - void Send (const std::vector >& msgs); - void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector > msgs); - - private: - - NTCPServer& m_Server; - boost::asio::ip::tcp::socket m_Socket; - bool m_IsEstablished, m_IsTerminated; - - i2p::crypto::CBCDecryption m_Decryption; - i2p::crypto::CBCEncryption m_Encryption; - - struct Establisher - { - NTCPPhase1 phase1; - NTCPPhase2 phase2; - } * m_Establisher; - - i2p::crypto::AESAlignedBuffer m_ReceiveBuffer; - i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer; - int m_ReceiveBufferOffset; - - std::shared_ptr m_NextMessage; - size_t m_NextMessageOffset; - i2p::I2NPMessagesHandler m_Handler; - - bool m_IsSending; - std::vector > m_SendQueue; - }; - - // TODO: move to NTCP.h/.cpp - class NTCPServer - { - public: - - typedef i2p::worker::ThreadPool Pool; - - enum RemoteAddressType - { - eIP4Address, - eIP6Address, - eHostname - }; - - enum ProxyType - { - eNoProxy, - eSocksProxy, - eHTTPProxy - }; - - - NTCPServer (int workers=4); - ~NTCPServer (); - - void Start (); - void Stop (); - - bool AddNTCPSession (std::shared_ptr session); - void RemoveNTCPSession (std::shared_ptr session); - std::shared_ptr FindNTCPSession (const i2p::data::IdentHash& ident); - void ConnectWithProxy (const std::string& addr, uint16_t port, RemoteAddressType addrtype, std::shared_ptr conn); - void Connect(const boost::asio::ip::address & address, uint16_t port, std::shared_ptr conn); - - bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; }; - bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; }; - bool NetworkIsReady() const { return IsBoundV4() || IsBoundV6() || UsingProxy(); }; - bool UsingProxy() const { return m_ProxyType != eNoProxy; }; - - void UseProxy(ProxyType proxy, const std::string & address, uint16_t port); - - boost::asio::io_service& GetService () { return m_Service; }; - - void SetSessionLimits(uint16_t softLimit, uint16_t hardLimit) { m_SoftLimit = softLimit; m_HardLimit = hardLimit; } - bool ShouldLimit() const { return ShouldHardLimit() || ShouldSoftLimit(); } - void Work(std::shared_ptr conn, Pool::WorkFunc work) - { - m_CryptoPool->Offer({conn, work}); - } - private: - - /** @brief return true for hard limit */ - bool ShouldHardLimit() const { return m_HardLimit && m_NTCPSessions.size() >= m_HardLimit; } - - /** @brief return true for probabalistic soft backoff */ - bool ShouldSoftLimit() const - { - auto sessions = m_NTCPSessions.size(); - return sessions && m_SoftLimit && m_SoftLimit < sessions && ( rand() % sessions ) <= m_SoftLimit; - } - void Run (); - void HandleAccept (std::shared_ptr conn, const boost::system::error_code& error); - void HandleAcceptV6 (std::shared_ptr conn, const boost::system::error_code& error); - - void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer); - - void HandleProxyConnect(const boost::system::error_code& ecode, std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype); - void AfterSocksHandshake(std::shared_ptr conn, std::shared_ptr timer, const std::string & host, uint16_t port, RemoteAddressType adddrtype); - - // timer - void ScheduleTermination (); - void HandleTerminationTimer (const boost::system::error_code& ecode); - - private: - - bool m_IsRunning; - std::thread * m_Thread; - boost::asio::io_service m_Service; - boost::asio::io_service::work m_Work; - boost::asio::deadline_timer m_TerminationTimer; - boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor; - std::map > m_NTCPSessions; // access from m_Thread only - std::list > m_PendingIncomingSessions; - - ProxyType m_ProxyType; - std::string m_ProxyAddress; - uint16_t m_ProxyPort; - boost::asio::ip::tcp::resolver m_Resolver; - boost::asio::ip::tcp::endpoint * m_ProxyEndpoint; - - std::shared_ptr m_CryptoPool; - - uint16_t m_SoftLimit, m_HardLimit; - public: - - // for HTTP/I2PControl - const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; }; - }; -} -} - -#endif diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index 54bf3fd9..19e17f52 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -133,7 +133,7 @@ namespace transport Transports::Transports (): m_IsOnline (true), m_IsRunning (false), m_IsNAT (true), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr), - m_NTCPServer (nullptr), m_SSUServer (nullptr), m_NTCP2Server (nullptr), + m_SSUServer (nullptr), m_NTCP2Server (nullptr), m_DHKeysPairSupplier (5), m_X25519KeysPairSupplier (5), // 5 pre-generated keys m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_TotalTransitTransmittedBytes (0), m_InBandwidth (0), m_OutBandwidth (0), m_TransitBandwidth(0), @@ -154,7 +154,7 @@ namespace transport } } - void Transports::Start (bool enableNTCP, bool enableSSU) + void Transports::Start (bool enableNTCP2, bool enableSSU) { if (!m_Service) { @@ -169,50 +169,10 @@ namespace transport m_X25519KeysPairSupplier.Start (); m_IsRunning = true; m_Thread = new std::thread (std::bind (&Transports::Run, this)); - std::string ntcpproxy; i2p::config::GetOption("ntcpproxy", ntcpproxy); std::string ntcp2proxy; i2p::config::GetOption("ntcp2.proxy", ntcp2proxy); - i2p::http::URL proxyurl; - uint16_t softLimit, hardLimit, threads; - i2p::config::GetOption("limits.ntcpsoft", softLimit); - i2p::config::GetOption("limits.ntcphard", hardLimit); - i2p::config::GetOption("limits.ntcpthreads", threads); - if(softLimit > 0 && hardLimit > 0 && softLimit >= hardLimit) - { - LogPrint(eLogError, "ntcp soft limit must be less than ntcp hard limit"); - return; - } - if(ntcpproxy.size() && enableNTCP) - { - if(proxyurl.parse(ntcpproxy)) - { - if(proxyurl.schema == "socks" || proxyurl.schema == "http") - { - m_NTCPServer = new NTCPServer(threads); - m_NTCPServer->SetSessionLimits(softLimit, hardLimit); - NTCPServer::ProxyType proxytype = NTCPServer::eSocksProxy; - - if (proxyurl.schema == "http") - proxytype = NTCPServer::eHTTPProxy; - m_NTCPServer->UseProxy(proxytype, proxyurl.host, proxyurl.port); - m_NTCPServer->Start(); - if(!m_NTCPServer->NetworkIsReady()) - { - LogPrint(eLogError, "Transports: NTCP failed to start with proxy"); - m_NTCPServer->Stop(); - delete m_NTCPServer; - m_NTCPServer = nullptr; - } - } - else - LogPrint(eLogError, "Transports: unsupported NTCP proxy URL ", ntcpproxy); - } - else - LogPrint(eLogError, "Transports: invalid NTCP proxy url ", ntcpproxy); - return; - } + i2p::http::URL proxyurl; // create NTCP2. TODO: move to acceptor - bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); - if (ntcp2) + if (enableNTCP2) { if(!ntcp2proxy.empty()) { @@ -248,20 +208,6 @@ namespace transport for (const auto& address : addresses) { if (!address) continue; - if (m_NTCPServer == nullptr && enableNTCP) - { - m_NTCPServer = new NTCPServer (threads); - m_NTCPServer->SetSessionLimits(softLimit, hardLimit); - m_NTCPServer->Start (); - if (!(m_NTCPServer->IsBoundV6() || m_NTCPServer->IsBoundV4())) { - /** failed to bind to NTCP */ - LogPrint(eLogError, "Transports: failed to bind to TCP"); - m_NTCPServer->Stop(); - delete m_NTCPServer; - m_NTCPServer = nullptr; - } - } - if (address->transportStyle == RouterInfo::eTransportSSU) { if (m_SSUServer == nullptr && enableSSU) @@ -306,13 +252,7 @@ namespace transport delete m_SSUServer; m_SSUServer = nullptr; } - if (m_NTCPServer) - { - m_NTCPServer->Stop (); - delete m_NTCPServer; - m_NTCPServer = nullptr; - } - + if (m_NTCP2Server) { m_NTCP2Server->Stop (); diff --git a/libi2pd/Transports.h b/libi2pd/Transports.h index 48eee4b4..c3008b09 100644 --- a/libi2pd/Transports.h +++ b/libi2pd/Transports.h @@ -21,7 +21,6 @@ #include #include #include "TransportSession.h" -#include "NTCPSession.h" #include "SSU.h" #include "NTCP2.h" #include "RouterInfo.h" @@ -88,10 +87,9 @@ namespace transport Transports (); ~Transports (); - void Start (bool enableNTCP=true, bool enableSSU=true); + void Start (bool enableNTCP2=true, bool enableSSU=true); void Stop (); - bool IsBoundNTCP() const { return m_NTCPServer != nullptr; } bool IsBoundSSU() const { return m_SSUServer != nullptr; } bool IsBoundNTCP2() const { return m_NTCP2Server != nullptr; } @@ -159,7 +157,6 @@ namespace transport boost::asio::io_service::work * m_Work; boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer; - NTCPServer * m_NTCPServer; SSUServer * m_SSUServer; NTCP2Server * m_NTCP2Server; mutable std::mutex m_PeersMutex; @@ -186,7 +183,6 @@ namespace transport public: // for HTTP only - const NTCPServer * GetNTCPServer () const { return m_NTCPServer; }; const SSUServer * GetSSUServer () const { return m_SSUServer; }; const NTCP2Server * GetNTCP2Server () const { return m_NTCP2Server; }; const decltype(m_Peers)& GetPeers () const { return m_Peers; }; diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 6eec3a16..468d2d00 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -42,7 +42,6 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \ ../../libi2pd/NetDb.cpp \ ../../libi2pd/NetDbRequests.cpp \ ../../libi2pd/NTCP2.cpp \ - ../../libi2pd/NTCPSession.cpp \ ../../libi2pd/Poly1305.cpp \ ../../libi2pd/Profiling.cpp \ ../../libi2pd/Reseed.cpp \ @@ -123,7 +122,6 @@ HEADERS += DaemonQT.h mainwindow.h \ ../../libi2pd/NetDb.hpp \ ../../libi2pd/NetDbRequests.h \ ../../libi2pd/NTCP2.h \ - ../../libi2pd/NTCPSession.h \ ../../libi2pd/Poly1305.h \ ../../libi2pd/Profiling.h \ ../../libi2pd/Queue.h \