Compare commits

..

25 Commits

Author SHA1 Message Date
orignal
fe3e7b1f6e reduced profile persist interval 2025-03-06 16:29:14 -05:00
orignal
66a52a17c6 load profile for SSU2 priority only 2025-03-06 16:26:02 -05:00
orignal
c113241ccd support local sockets for I2PControl 2025-03-05 14:14:34 -05:00
orignal
9c97909e04 removed test crypto/signature types 2025-03-05 10:51:21 -05:00
orignal
c816d3e4cc Ed25519ph 2025-03-05 10:38:23 -05:00
orignal
4f82fe24da replace boost::lexical_cast by std::to_string. std::unique_ptr for thread 2025-03-04 18:11:58 -05:00
orignal
a1794ccd22 Solaris build added 2025-03-02 15:00:09 -05:00
orignal
5f762845f0 move BOB incoming connection logic from I2PTunnelConnection to BOB 2025-02-28 22:20:50 -05:00
orignal
539e7e988e reduce I2PTunnelConnection buffer size 2025-02-27 21:35:14 -05:00
orignal
2a4403f1e0 lazy creation of TunnelEnpoint for transit tunnel 2025-02-27 18:00:24 -05:00
orignal
dcd15cc244 use common constants for babdwidth limits 2025-02-25 14:12:10 -05:00
orignal
9432202fad check PeerTest buffer size 2025-02-24 13:58:10 -05:00
orignal
bf050ac465 fixed typo 2025-02-22 19:01:20 -05:00
orignal
81dae1997d replace boost::lexical_cast by std::from_chars and std::to_string 2025-02-21 20:34:53 -05:00
orignal
7e3d9649de use plain buffer instead stream for SignedData 2025-02-20 22:04:58 -05:00
orignal
9ba016259d use plain buffer instead stream for SignedData 2025-02-20 21:59:24 -05:00
orignal
7791b3952e check RelayRequest, RelayIntro, RelayResponse buffer size. Use assign instead memcpy 2025-02-20 16:53:49 -05:00
orignal
b97f09cc95 const ExtractString and ExtractMapping 2025-02-19 18:51:54 -05:00
orignal
aedf59d11a fixed typo 2025-02-19 13:38:47 -05:00
orignal
d09367d686 always pass RouterInfo param values as string_view 2025-02-19 11:08:47 -05:00
orignal
70f99ccc21 update router caps 2025-02-18 21:49:07 -05:00
orignal
ef72ba3f34 parse RouterInfo from buffer 2025-02-18 21:11:59 -05:00
orignal
251605e2b8 Fix the calculation of the window drop size 2025-02-18 20:22:18 -05:00
orignal
fa2178ca3e set max padding size to 32 bytes 2025-02-17 15:08:22 -05:00
orignal
3d19fa12f6 create new tunnel immediately if last one failed 2025-02-15 15:27:14 -05:00
30 changed files with 535 additions and 356 deletions

View File

@@ -69,6 +69,9 @@ else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
else ifneq (, $(findstring haiku, $(SYS))) else ifneq (, $(findstring haiku, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
include Makefile.haiku include Makefile.haiku
else ifneq (, $(findstring solaris, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
include Makefile.solaris
else # not supported else # not supported
$(error Not supported platform) $(error Not supported platform)
endif endif

9
Makefile.solaris Normal file
View File

@@ -0,0 +1,9 @@
CXX = g++
INCFLAGS = -I/usr/openssl/3/include
CXXFLAGS := -Wall -std=c++20
LDLIBS = -L/usr/openssl/3/lib/64 -lssl -lcrypto -lboost_program_options -lz -lpthread -lsocket
ifeq ($(USE_UPNP),yes)
DEFINES += -DUSE_UPNP
LDLIBS += -lminiupnpc
endif

View File

@@ -2,7 +2,6 @@
Description=I2P Router written in C++ Description=I2P Router written in C++
Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/ Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/
After=network.target After=network.target
Wants=yggdrasil.service
[Service] [Service]
User=i2pd User=i2pd

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -15,7 +15,6 @@
// Use global placeholders from boost introduced when local_time.hpp is loaded // Use global placeholders from boost introduced when local_time.hpp is loaded
#define BOOST_BIND_GLOBAL_PLACEHOLDERS #define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <boost/lexical_cast.hpp>
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
@@ -30,11 +29,24 @@ namespace i2p
namespace client namespace client
{ {
I2PControlService::I2PControlService (const std::string& address, int port): I2PControlService::I2PControlService (const std::string& address, int port):
m_IsRunning (false), m_Thread (nullptr), m_IsRunning (false),
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port)),
m_SSLContext (boost::asio::ssl::context::sslv23), m_SSLContext (boost::asio::ssl::context::sslv23),
m_ShutdownTimer (m_Service) m_ShutdownTimer (m_Service)
{ {
if (port)
m_Acceptor = std::make_unique<boost::asio::ip::tcp::acceptor>(m_Service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::make_address(address), port));
else
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
{
std::remove (address.c_str ()); // just in case
m_LocalAcceptor = std::make_unique<boost::asio::local::stream_protocol::acceptor>(m_Service,
boost::asio::local::stream_protocol::endpoint(address));
}
#else
LogPrint(eLogError, "I2PControl: Local sockets are not supported");
#endif
i2p::config::GetOption("i2pcontrol.password", m_Password); i2p::config::GetOption("i2pcontrol.password", m_Password);
// certificate / keys // certificate / keys
@@ -98,7 +110,7 @@ namespace client
{ {
Accept (); Accept ();
m_IsRunning = true; m_IsRunning = true;
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this)); m_Thread = std::make_unique<std::thread>(std::bind (&I2PControlService::Run, this));
} }
} }
@@ -107,12 +119,19 @@ namespace client
if (m_IsRunning) if (m_IsRunning)
{ {
m_IsRunning = false; m_IsRunning = false;
m_Acceptor.cancel (); if (m_Acceptor) m_Acceptor->cancel ();
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
if (m_LocalAcceptor)
{
auto path = m_LocalAcceptor->local_endpoint().path();
m_LocalAcceptor->cancel ();
std::remove (path.c_str ());
}
#endif
m_Service.stop (); m_Service.stop ();
if (m_Thread) if (m_Thread)
{ {
m_Thread->join (); m_Thread->join ();
delete m_Thread;
m_Thread = nullptr; m_Thread = nullptr;
} }
} }
@@ -134,40 +153,60 @@ namespace client
void I2PControlService::Accept () void I2PControlService::Accept ()
{ {
auto newSocket = std::make_shared<ssl_socket> (m_Service, m_SSLContext); if (m_Acceptor)
m_Acceptor.async_accept (newSocket->lowest_layer(), std::bind (&I2PControlService::HandleAccept, this, {
std::placeholders::_1, newSocket)); auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket> > (m_Service, m_SSLContext);
m_Acceptor->async_accept (newSocket->lowest_layer(),
[this, newSocket](const boost::system::error_code& ecode)
{
HandleAccepted (ecode, newSocket);
});
}
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
else if (m_LocalAcceptor)
{
auto newSocket = std::make_shared<boost::asio::ssl::stream<boost::asio::local::stream_protocol::socket> > (m_Service, m_SSLContext);
m_LocalAcceptor->async_accept (newSocket->lowest_layer(),
[this, newSocket](const boost::system::error_code& ecode)
{
HandleAccepted (ecode, newSocket);
});
}
#endif
} }
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket) template<typename ssl_socket>
void I2PControlService::HandleAccepted (const boost::system::error_code& ecode,
std::shared_ptr<ssl_socket> newSocket)
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
Accept (); Accept ();
if (ecode) { if (ecode)
{
LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ()); LogPrint (eLogError, "I2PControl: Accept error: ", ecode.message ());
return; return;
} }
LogPrint (eLogDebug, "I2PControl: New request from ", socket->lowest_layer ().remote_endpoint ()); LogPrint (eLogDebug, "I2PControl: New request from ", newSocket->lowest_layer ().remote_endpoint ());
Handshake (socket); Handshake (newSocket);
} }
template<typename ssl_socket>
void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket) void I2PControlService::Handshake (std::shared_ptr<ssl_socket> socket)
{ {
socket->async_handshake(boost::asio::ssl::stream_base::server, socket->async_handshake(boost::asio::ssl::stream_base::server,
std::bind( &I2PControlService::HandleHandshake, this, std::placeholders::_1, socket)); [this, socket](const boost::system::error_code& ecode)
} {
if (ecode)
void I2PControlService::HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket) {
{ LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ());
if (ecode) { return;
LogPrint (eLogError, "I2PControl: Handshake error: ", ecode.message ()); }
return; ReadRequest (socket);
} });
//std::this_thread::sleep_for (std::chrono::milliseconds(5));
ReadRequest (socket);
} }
template<typename ssl_socket>
void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket) void I2PControlService::ReadRequest (std::shared_ptr<ssl_socket> socket)
{ {
auto request = std::make_shared<I2PControlBuffer>(); auto request = std::make_shared<I2PControlBuffer>();
@@ -177,10 +216,13 @@ namespace client
#else #else
boost::asio::buffer (request->data (), request->size ()), boost::asio::buffer (request->data (), request->size ()),
#endif #endif
std::bind(&I2PControlService::HandleRequestReceived, this, [this, socket, request](const boost::system::error_code& ecode, size_t bytes_transferred)
std::placeholders::_1, std::placeholders::_2, socket, request)); {
HandleRequestReceived (ecode, bytes_transferred, socket, request);
});
} }
template<typename ssl_socket>
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode, void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket, size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf) std::shared_ptr<I2PControlBuffer> buf)
@@ -258,6 +300,7 @@ namespace client
} }
} }
template<typename ssl_socket>
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket, void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml) std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
{ {
@@ -267,7 +310,7 @@ namespace client
std::ostringstream header; std::ostringstream header;
header << "HTTP/1.1 200 OK\r\n"; header << "HTTP/1.1 200 OK\r\n";
header << "Connection: close\r\n"; header << "Connection: close\r\n";
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n"; header << "Content-Length: " << std::to_string(len) << "\r\n";
header << "Content-Type: application/json\r\n"; header << "Content-Type: application/json\r\n";
header << "Date: "; header << "Date: ";
std::time_t t = std::time (nullptr); std::time_t t = std::time (nullptr);
@@ -280,16 +323,11 @@ namespace client
memcpy (buf->data () + offset, response.str ().c_str (), len); memcpy (buf->data () + offset, response.str ().c_str (), len);
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
boost::asio::transfer_all (), boost::asio::transfer_all (),
std::bind(&I2PControlService::HandleResponseSent, this, [socket, buf](const boost::system::error_code& ecode, std::size_t bytes_transferred)
std::placeholders::_1, std::placeholders::_2, socket, buf)); {
} if (ecode)
LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ());
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, });
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf)
{
if (ecode) {
LogPrint (eLogError, "I2PControl: Write error: ", ecode.message ());
}
} }
// handlers // handlers

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -35,8 +35,6 @@ namespace client
class I2PControlService: public I2PControlHandlers class I2PControlService: public I2PControlHandlers
{ {
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
public: public:
I2PControlService (const std::string& address, int port); I2PControlService (const std::string& address, int port);
@@ -49,16 +47,18 @@ namespace client
void Run (); void Run ();
void Accept (); void Accept ();
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket); template<typename ssl_socket>
void HandleAccepted (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> newSocket);
template<typename ssl_socket>
void Handshake (std::shared_ptr<ssl_socket> socket); void Handshake (std::shared_ptr<ssl_socket> socket);
void HandleHandshake (const boost::system::error_code& ecode, std::shared_ptr<ssl_socket> socket); template<typename ssl_socket>
void ReadRequest (std::shared_ptr<ssl_socket> socket); void ReadRequest (std::shared_ptr<ssl_socket> socket);
template<typename ssl_socket>
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf); std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
template<typename ssl_socket>
void SendResponse (std::shared_ptr<ssl_socket> socket, void SendResponse (std::shared_ptr<ssl_socket> socket,
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml); std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
void CreateCertificate (const char *crt_path, const char *key_path); void CreateCertificate (const char *crt_path, const char *key_path);
@@ -86,10 +86,13 @@ namespace client
std::string m_Password; std::string m_Password;
bool m_IsRunning; bool m_IsRunning;
std::thread * m_Thread; std::unique_ptr<std::thread> m_Thread;
boost::asio::io_context m_Service; boost::asio::io_context m_Service;
boost::asio::ip::tcp::acceptor m_Acceptor; std::unique_ptr<boost::asio::ip::tcp::acceptor> m_Acceptor;
#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
std::unique_ptr<boost::asio::local::stream_protocol::acceptor> m_LocalAcceptor;
#endif
boost::asio::ssl::context m_SSLContext; boost::asio::ssl::context m_SSLContext;
boost::asio::deadline_timer m_ShutdownTimer; boost::asio::deadline_timer m_ShutdownTimer;
std::set<std::string> m_Tokens; std::set<std::string> m_Tokens;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -90,10 +90,10 @@ namespace data
} }
bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, bool Families::VerifyFamily (const std::string& family, const IdentHash& ident,
const char * signature, const char * key) const std::string_view signature, const char * key) const
{ {
uint8_t buf[100], signatureBuf[64]; uint8_t buf[100], signatureBuf[64];
size_t len = family.length (), signatureLen = strlen (signature); size_t len = family.length ();
if (len + 32 > 100) if (len + 32 > 100)
{ {
LogPrint (eLogError, "Family: ", family, " is too long"); LogPrint (eLogError, "Family: ", family, " is too long");
@@ -105,7 +105,7 @@ namespace data
memcpy (buf, family.c_str (), len); memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32); memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32; len += 32;
auto signatureBufLen = Base64ToByteStream (signature, signatureLen, signatureBuf, 64); auto signatureBufLen = Base64ToByteStream (signature.data (), signature.length (), signatureBuf, 64);
if (signatureBufLen) if (signatureBufLen)
{ {
EVP_MD_CTX * ctx = EVP_MD_CTX_create (); EVP_MD_CTX * ctx = EVP_MD_CTX_create ();

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -11,6 +11,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <string_view>
#include <memory> #include <memory>
#include <openssl/evp.h> #include <openssl/evp.h>
#include "Identity.h" #include "Identity.h"
@@ -28,7 +29,7 @@ namespace data
~Families (); ~Families ();
void LoadCertificates (); void LoadCertificates ();
bool VerifyFamily (const std::string& family, const IdentHash& ident, bool VerifyFamily (const std::string& family, const IdentHash& ident,
const char * signature, const char * key = nullptr) const; std::string_view signature, const char * key = nullptr) const;
FamilyID GetFamilyID (const std::string& family) const; FamilyID GetFamilyID (const std::string& family) const;
private: private:

View File

@@ -399,12 +399,8 @@ namespace data
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key); return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetEncryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key); return std::make_shared<i2p::crypto::ECIESP256Encryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
return std::make_shared<i2p::crypto::ECIESGOSTR3410Encryptor>(key);
break;
default: default:
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType); LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)keyType);
}; };
@@ -673,12 +669,8 @@ namespace data
return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key); return std::make_shared<i2p::crypto::ECIESX25519AEADRatchetDecryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key); return std::make_shared<i2p::crypto::ECIESP256Decryptor>(key);
break; break;
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
return std::make_shared<i2p::crypto::ECIESGOSTR3410Decryptor>(key);
break;
default: default:
LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType); LogPrint (eLogError, "Identity: Unknown crypto key type ", (int)cryptoType);
}; };
@@ -753,12 +745,8 @@ namespace data
i2p::crypto::GenerateElGamalKeyPair(priv, pub); i2p::crypto::GenerateElGamalKeyPair(priv, pub);
break; break;
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC: case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC:
case CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST:
i2p::crypto::CreateECIESP256RandomKeys (priv, pub); i2p::crypto::CreateECIESP256RandomKeys (priv, pub);
break; break;
case CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC:
i2p::crypto::CreateECIESGOSTR3410RandomKeys (priv, pub);
break;
case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD: case CRYPTO_KEY_TYPE_ECIES_X25519_AEAD:
i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub); i2p::crypto::CreateECIESX25519AEADRatchetRandomKeys (priv, pub);
break; break;

View File

@@ -64,8 +64,6 @@ namespace data
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0; const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1; const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC = 1;
const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4; const uint16_t CRYPTO_KEY_TYPE_ECIES_X25519_AEAD = 4;
const uint16_t CRYPTO_KEY_TYPE_ECIES_P256_SHA256_AES256CBC_TEST = 65280; // TODO: remove later
const uint16_t CRYPTO_KEY_TYPE_ECIES_GOSTR3410_CRYPTO_PRO_A_SHA256_AES256CBC = 65281; // TODO: use GOST R 34.11 instead SHA256 and GOST 28147-89 instead AES
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0; const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1; const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
@@ -75,7 +73,7 @@ namespace data
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5; const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6; const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7; const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // not implemented const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519ph = 8; // since openssl 3.0.0
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9; const uint16_t SIGNING_KEY_TYPE_GOSTR3410_CRYPTO_PRO_A_GOSTR3411_256 = 9;
const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB const uint16_t SIGNING_KEY_TYPE_GOSTR3410_TC26_A_512_GOSTR3411_512 = 10; // approved by FSB
const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only const uint16_t SIGNING_KEY_TYPE_REDDSA_SHA512_ED25519 = 11; // for LeaseSet2 only

View File

@@ -310,7 +310,7 @@ namespace data
{ {
if (it->second->IsUpdated () && ts > it->second->GetLastPersistTime () + PEER_PROFILE_PERSIST_INTERVAL) if (it->second->IsUpdated () && ts > it->second->GetLastPersistTime () + PEER_PROFILE_PERSIST_INTERVAL)
{ {
tmp.push_back (std::make_pair (it->first, it->second)); tmp.push_back (*it);
it->second->SetLastPersistTime (ts); it->second->SetLastPersistTime (ts);
it->second->SetUpdated (false); it->second->SetUpdated (false);
} }

View File

@@ -41,7 +41,7 @@ namespace data
const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes) const int PEER_PROFILE_OBSOLETE_PROFILES_CLEAN_VARIANCE = 2400; // in seconds (40 minutes)
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 330; // in seconds (5.5 minutes) const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 330; // in seconds (5.5 minutes)
const int PEER_PROFILE_MAX_DECLINED_INTERVAL = 4400; // in second (1.5 hours) const int PEER_PROFILE_MAX_DECLINED_INTERVAL = 4400; // in second (1.5 hours)
const int PEER_PROFILE_PERSIST_INTERVAL = 3300; // in seconds (55 minutes) const int PEER_PROFILE_PERSIST_INTERVAL = 1320; // in seconds (22 minutes)
const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes) const int PEER_PROFILE_UNREACHABLE_INTERVAL = 480; // in seconds (8 minutes)
const int PEER_PROFILE_USEFUL_THRESHOLD = 3; const int PEER_PROFILE_USEFUL_THRESHOLD = 3;
const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined const int PEER_PROFILE_ALWAYS_DECLINING_NUM = 5; // num declines in row to consider always declined

View File

@@ -675,12 +675,12 @@ namespace i2p
void RouterContext::SetBandwidth (int limit) void RouterContext::SetBandwidth (int limit)
{ {
if (limit > 2000) { SetBandwidth('X'); } if (limit > (int)i2p::data::EXTRA_BANDWIDTH_LIMIT) { SetBandwidth('X'); }
else if (limit > 256) { SetBandwidth('P'); } else if (limit > (int)i2p::data::HIGH_BANDWIDTH_LIMIT) { SetBandwidth('P'); }
else if (limit > 128) { SetBandwidth('O'); } else if (limit > 128) { SetBandwidth('O'); }
else if (limit > 64) { SetBandwidth('N'); } else if (limit > 64) { SetBandwidth('N'); }
else if (limit > 48) { SetBandwidth('M'); } else if (limit > (int)i2p::data::LOW_BANDWIDTH_LIMIT) { SetBandwidth('M'); }
else if (limit > 12) { SetBandwidth('L'); } else if (limit > 12) { SetBandwidth('L'); }
else { SetBandwidth('K'); } else { SetBandwidth('K'); }
m_BandwidthLimit = limit; // set precise limit m_BandwidthLimit = limit; // set precise limit
} }

View File

@@ -11,7 +11,7 @@
#include "I2PEndian.h" #include "I2PEndian.h"
#include <fstream> #include <fstream>
#include <memory> #include <memory>
#include <boost/lexical_cast.hpp> #include <charconv>
#include <boost/algorithm/string.hpp> // for boost::to_lower #include <boost/algorithm/string.hpp> // for boost::to_lower
#ifndef __cpp_lib_atomic_shared_ptr #ifndef __cpp_lib_atomic_shared_ptr
#include <boost/atomic.hpp> #include <boost/atomic.hpp>
@@ -106,8 +106,7 @@ namespace data
// skip identity // skip identity
size_t identityLen = m_RouterIdentity->GetFullLen (); size_t identityLen = m_RouterIdentity->GetFullLen ();
// read new RI // read new RI
std::stringstream str (std::string ((char *)buf + identityLen, len - identityLen)); ReadFromBuffer (buf + identityLen, len - identityLen);
ReadFromStream (str);
if (!m_IsUnreachable) if (!m_IsUnreachable)
UpdateBuffer (buf, len); // save buffer UpdateBuffer (buf, len); // save buffer
// don't delete buffer until saved to the file // don't delete buffer until saved to the file
@@ -195,39 +194,34 @@ namespace data
} }
} }
// parse RI // parse RI
std::stringstream str; if (!ReadFromBuffer (m_Buffer->data () + identityLen, bufferLen - identityLen))
str.write ((const char *)m_Buffer->data () + identityLen, bufferLen - identityLen);
ReadFromStream (str);
if (!str)
{ {
LogPrint (eLogError, "RouterInfo: Malformed message"); LogPrint (eLogError, "RouterInfo: Malformed message");
m_IsUnreachable = true; m_IsUnreachable = true;
} }
} }
void RouterInfo::ReadFromStream (std::istream& s) bool RouterInfo::ReadFromBuffer (const uint8_t * buf, size_t len)
{ {
if (!s) return; if (len < 9) return false;
m_Caps = 0; m_Congestion = eLowCongestion; m_Caps = 0; m_Congestion = eLowCongestion;
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp)); m_Timestamp = bufbe64toh (buf);
m_Timestamp = be64toh (m_Timestamp); size_t offset = 8; // timestamp
// read addresses // read addresses
auto addresses = NewAddresses (); auto addresses = NewAddresses ();
uint8_t numAddresses; uint8_t numAddresses = buf[offset]; offset++;
s.read ((char *)&numAddresses, sizeof (numAddresses));
for (int i = 0; i < numAddresses; i++) for (int i = 0; i < numAddresses; i++)
{ {
if (offset + 9 > len) return false; // 1 byte cost + 8 bytes date
uint8_t supportedTransports = 0; uint8_t supportedTransports = 0;
auto address = NewAddress (); auto address = NewAddress ();
uint8_t cost; // ignore offset++; // cost, ignore
s.read ((char *)&cost, sizeof (cost)); address->date = bufbe64toh (buf + offset); offset += 8; // date
s.read ((char *)&address->date, sizeof (address->date));
bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false; bool isHost = false, isStaticKey = false, isV2 = false, isIntroKey = false;
char transportStyle[6]; auto transportStyle = ExtractString (buf + offset, len - offset); offset += transportStyle.length () + 1;
ReadString (transportStyle, 6, s); if (!transportStyle.compare (0, 4, "NTCP")) // NTCP or NTCP2
if (!strncmp (transportStyle, "NTCP", 4)) // NTCP or NTCP2
address->transportStyle = eTransportNTCP2; address->transportStyle = eTransportNTCP2;
else if (!strncmp (transportStyle, "SSU", 3)) // SSU or SSU2 else if (!transportStyle.compare (0, 3, "SSU")) // SSU or SSU2
{ {
address->transportStyle = eTransportSSU2; address->transportStyle = eTransportSSU2;
address->ssu.reset (new SSUExt ()); address->ssu.reset (new SSUExt ());
@@ -237,24 +231,22 @@ namespace data
address->transportStyle = eTransportUnknown; address->transportStyle = eTransportUnknown;
address->caps = 0; address->caps = 0;
address->port = 0; address->port = 0;
uint16_t size, r = 0; if (offset + 2 > len) return false;
s.read ((char *)&size, sizeof (size)); if (!s) return; uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
size = be16toh (size); if (offset + size >= len) return false;
if (address->transportStyle == eTransportUnknown) if (address->transportStyle == eTransportUnknown)
{ {
// skip unknown address // skip unknown address
s.seekg (size, std::ios_base::cur); offset += size;
if (s) continue; else return; continue;
} }
size_t r = 0;
while (r < size) while (r < size)
{ {
char key[255], value[255]; auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
r += ReadString (key, 255, s); r += sz; offset += sz;
s.seekg (1, std::ios_base::cur); r++; // = if (key.empty ()) continue;
r += ReadString (value, 255, s); if (key == "host")
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
if (!strcmp (key, "host"))
{ {
boost::system::error_code ecode; boost::system::error_code ecode;
address->host = boost::asio::ip::make_address (value, ecode); address->host = boost::asio::ip::make_address (value, ecode);
@@ -268,63 +260,53 @@ namespace data
address->transportStyle = eTransportUnknown; address->transportStyle = eTransportUnknown;
} }
} }
else if (!strcmp (key, "port")) else if (key == "port")
{ {
try auto res = std::from_chars(value.data(), value.data() + value.size(), address->port);
{ if (res.ec != std::errc())
address->port = boost::lexical_cast<int>(value); LogPrint (eLogWarning, "RouterInfo: 'port' conversion error: ", std::make_error_code (res.ec).message ());
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'port' exception ", ex.what ());
}
} }
else if (!strcmp (key, "mtu")) else if (key == "mtu")
{ {
if (address->ssu) if (address->ssu)
{ {
try auto res = std::from_chars(value.data(), value.data() + value.size(), address->ssu->mtu);
{ if (res.ec != std::errc())
address->ssu->mtu = boost::lexical_cast<int>(value); LogPrint (eLogWarning, "RouterInfo: 'mtu' conversion error: ", std::make_error_code (res.ec).message ());
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'mtu' exception ", ex.what ());
}
} }
else else
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2"); LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP2");
} }
else if (!strcmp (key, "caps")) else if (key == "caps")
address->caps = ExtractAddressCaps (value); address->caps = ExtractAddressCaps (value);
else if (!strcmp (key, "s")) // ntcp2 or ssu2 static key else if (key == "s") // ntcp2 or ssu2 static key
{ {
if (Base64ToByteStream (value, strlen (value), address->s, 32) == 32 && if (Base64ToByteStream (value.data (), value.length (), address->s, 32) == 32 &&
!(address->s[31] & 0x80)) // check if x25519 public key !(address->s[31] & 0x80)) // check if x25519 public key
isStaticKey = true; isStaticKey = true;
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
} }
else if (!strcmp (key, "i")) // ntcp2 iv or ssu2 intro else if (key == "i") // ntcp2 iv or ssu2 intro
{ {
if (address->IsNTCP2 ()) if (address->IsNTCP2 ())
{ {
if (Base64ToByteStream (value, strlen (value), address->i, 16) == 16) if (Base64ToByteStream (value.data (), value.length (), address->i, 16) == 16)
address->published = true; // presence of "i" means "published" NTCP2 address->published = true; // presence of "i" means "published" NTCP2
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
} }
else if (address->IsSSU2 ()) else if (address->IsSSU2 ())
{ {
if (Base64ToByteStream (value, strlen (value), address->i, 32) == 32) if (Base64ToByteStream (value.data (), value.length (), address->i, 32) == 32)
isIntroKey = true; isIntroKey = true;
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
} }
} }
else if (!strcmp (key, "v")) else if (key == "v")
{ {
if (!strcmp (value, "2")) if (value == "2")
isV2 = true; isV2 = true;
else else
{ {
@@ -340,13 +322,11 @@ namespace data
LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped"); LogPrint (eLogError, "RouterInfo: Introducer is presented for non-SSU address. Skipped");
continue; continue;
} }
size_t l = strlen(key); unsigned char index = key[key.length () - 1] - '0'; // TODO:
unsigned char index = key[l-1] - '0'; // TODO:
key[l-1] = 0;
if (index > 9) if (index > 9)
{ {
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
if (s) continue; else return; continue;
} }
if (index >= address->ssu->introducers.size ()) if (index >= address->ssu->introducers.size ())
{ {
@@ -355,34 +335,23 @@ namespace data
address->ssu->introducers.resize (index + 1); address->ssu->introducers.resize (index + 1);
} }
Introducer& introducer = address->ssu->introducers.at (index); Introducer& introducer = address->ssu->introducers.at (index);
if (!strcmp (key, "itag")) auto key1 = key.substr(0, key.length () - 1);
if (key1 == "itag")
{ {
try auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iTag);
{ if (res.ec != std::errc())
introducer.iTag = boost::lexical_cast<uint32_t>(value); LogPrint (eLogWarning, "RouterInfo: 'itag' conversion error: ", std::make_error_code (res.ec).message ());
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'itag' exception ", ex.what ());
}
} }
else if (!strcmp (key, "ih")) else if (key1 == "ih")
Base64ToByteStream (value, strlen (value), introducer.iH, 32); Base64ToByteStream (value.data (), value.length (), introducer.iH, 32);
else if (!strcmp (key, "iexp")) else if (key1 == "iexp")
{ {
try auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iExp);
{ if (res.ec != std::errc())
introducer.iExp = boost::lexical_cast<uint32_t>(value); LogPrint (eLogWarning, "RouterInfo: 'iexp' conversion error: ", std::make_error_code (res.ec).message ());
}
catch (std::exception& ex)
{
LogPrint (eLogWarning, "RouterInfo: 'iexp' exception ", ex.what ());
}
} }
} }
if (!s) return;
} }
if (address->transportStyle == eTransportNTCP2) if (address->transportStyle == eTransportNTCP2)
{ {
if (isStaticKey) if (isStaticKey)
@@ -446,45 +415,41 @@ namespace data
boost::atomic_store (&m_Addresses, addresses); boost::atomic_store (&m_Addresses, addresses);
#endif #endif
// read peers // read peers
uint8_t numPeers; if (offset + 1 > len) return false;
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return; uint8_t numPeers = buf[offset]; offset++; // num peers
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers offset += numPeers*32; // TODO: read peers
// read properties // read properties
if (offset + 2 > len) return false;
m_Version = 0; m_Version = 0;
bool isNetId = false; bool isNetId = false;
std::string family; std::string family;
uint16_t size, r = 0; uint16_t size = bufbe16toh (buf + offset); offset += 2; // size
s.read ((char *)&size, sizeof (size)); if (!s) return; if (offset + size > len) return false;
size = be16toh (size); size_t r = 0;
while (r < size) while (r < size)
{ {
char key[255], value[255]; auto [key, value, sz] = ExtractParam (buf + offset, len - offset);
r += ReadString (key, 255, s); r += sz; offset += sz;
s.seekg (1, std::ios_base::cur); r++; // = if (key.empty ()) continue;
r += ReadString (value, 255, s);
s.seekg (1, std::ios_base::cur); r++; // ;
if (!s) return;
SetProperty (key, value); SetProperty (key, value);
// extract caps // extract caps
if (!strcmp (key, "caps")) if (key == "caps")
{ {
ExtractCaps (value); ExtractCaps (value);
m_IsFloodfill = IsDeclaredFloodfill (); m_IsFloodfill = IsDeclaredFloodfill ();
} }
// extract version // extract version
else if (!strcmp (key, ROUTER_INFO_PROPERTY_VERSION)) else if (key == ROUTER_INFO_PROPERTY_VERSION)
{ {
m_Version = 0; m_Version = 0;
char * ch = value; for (auto ch: value)
while (*ch)
{ {
if (*ch >= '0' && *ch <= '9') if (ch >= '0' && ch <= '9')
{ {
m_Version *= 10; m_Version *= 10;
m_Version += (*ch - '0'); m_Version += (ch - '0');
} }
ch++;
} }
if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6))) if (m_Version < NETDB_MIN_PEER_TEST_VERSION && (m_SupportedTransports & (eSSU2V4 | eSSU2V6)))
{ {
@@ -497,24 +462,26 @@ namespace data
} }
} }
// check netId // check netId
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID)) else if (key == ROUTER_INFO_PROPERTY_NETID)
{ {
isNetId = true; isNetId = true;
if (atoi (value) != i2p::context.GetNetID ()) int netID;
auto res = std::from_chars(value.data(), value.data() + value.size(), netID);
if (res.ec != std::errc() || netID != i2p::context.GetNetID ())
{ {
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value); LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
m_IsUnreachable = true; m_IsUnreachable = true;
} }
} }
// family // family
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY)) else if (key == ROUTER_INFO_PROPERTY_FAMILY)
{ {
family = value; family = value;
boost::to_lower (family); boost::to_lower (family);
} }
else if (!strcmp (key, ROUTER_INFO_PROPERTY_FAMILY_SIG)) else if (key == ROUTER_INFO_PROPERTY_FAMILY_SIG)
{ {
if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) if (netdb.GetFamilies ().VerifyFamily (family, GetIdentHash (), value)) // TODO
m_FamilyID = netdb.GetFamilies ().GetFamilyID (family); m_FamilyID = netdb.GetFamilies ().GetFamilyID (family);
else else
{ {
@@ -522,12 +489,12 @@ namespace data
SetUnreachable (true); SetUnreachable (true);
} }
} }
if (!s) return;
} }
if (!m_SupportedTransports || !isNetId || !m_Version) if (!m_SupportedTransports || !isNetId || !m_Version)
SetUnreachable (true); SetUnreachable (true);
return true;
} }
bool RouterInfo::IsFamily (FamilyID famid) const bool RouterInfo::IsFamily (FamilyID famid) const
@@ -535,12 +502,11 @@ namespace data
return m_FamilyID == famid; return m_FamilyID == famid;
} }
void RouterInfo::ExtractCaps (const char * value) void RouterInfo::ExtractCaps (std::string_view value)
{ {
const char * cap = value; for (auto cap: value)
while (*cap)
{ {
switch (*cap) switch (cap)
{ {
case CAPS_FLAG_FLOODFILL: case CAPS_FLAG_FLOODFILL:
m_Caps |= Caps::eFloodfill; m_Caps |= Caps::eFloodfill;
@@ -549,16 +515,16 @@ namespace data
case CAPS_FLAG_LOW_BANDWIDTH2: case CAPS_FLAG_LOW_BANDWIDTH2:
case CAPS_FLAG_LOW_BANDWIDTH3: case CAPS_FLAG_LOW_BANDWIDTH3:
case CAPS_FLAG_LOW_BANDWIDTH4: case CAPS_FLAG_LOW_BANDWIDTH4:
m_BandwidthCap = *cap; m_BandwidthCap = cap;
break; break;
case CAPS_FLAG_HIGH_BANDWIDTH: case CAPS_FLAG_HIGH_BANDWIDTH:
m_Caps |= Caps::eHighBandwidth; m_Caps |= Caps::eHighBandwidth;
m_BandwidthCap = *cap; m_BandwidthCap = cap;
break; break;
case CAPS_FLAG_EXTRA_BANDWIDTH1: case CAPS_FLAG_EXTRA_BANDWIDTH1:
case CAPS_FLAG_EXTRA_BANDWIDTH2: case CAPS_FLAG_EXTRA_BANDWIDTH2:
m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth; m_Caps |= Caps::eExtraBandwidth | Caps::eHighBandwidth;
m_BandwidthCap = *cap; m_BandwidthCap = cap;
break; break;
case CAPS_FLAG_HIDDEN: case CAPS_FLAG_HIDDEN:
m_Caps |= Caps::eHidden; m_Caps |= Caps::eHidden;
@@ -580,17 +546,15 @@ namespace data
break; break;
default: ; default: ;
} }
cap++;
} }
} }
uint8_t RouterInfo::ExtractAddressCaps (const char * value) const uint8_t RouterInfo::ExtractAddressCaps (std::string_view value) const
{ {
uint8_t caps = 0; uint8_t caps = 0;
const char * cap = value; for (auto cap: value)
while (*cap)
{ {
switch (*cap) switch (cap)
{ {
case CAPS_FLAG_V4: case CAPS_FLAG_V4:
caps |= AddressCaps::eV4; caps |= AddressCaps::eV4;
@@ -606,7 +570,6 @@ namespace data
break; break;
default: ; default: ;
} }
cap++;
} }
return caps; return caps;
} }
@@ -670,23 +633,39 @@ namespace data
return SaveToFile (fullPath, m_Buffer); return SaveToFile (fullPath, m_Buffer);
} }
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const std::string_view RouterInfo::ExtractString (const uint8_t * buf, size_t len) const
{ {
uint8_t l; uint8_t l = buf[0];
s.read ((char *)&l, 1); if (l > len)
if (l < len)
{
s.read (str, l);
if (!s) l = 0; // failed, return empty string
str[l] = 0;
}
else
{ {
LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len); LogPrint (eLogWarning, "RouterInfo: String length ", (int)l, " exceeds buffer size ", len);
s.seekg (l, std::ios::cur); // skip l = len;
str[0] = 0;
} }
return l+1; return { (const char *)(buf + 1), l };
}
std::tuple<std::string_view, std::string_view, size_t> RouterInfo::ExtractParam (const uint8_t * buf, size_t len) const
{
auto key = ExtractString (buf, len);
size_t offset = key.length () + 1;
if (offset >= len) return { std::string_view(), std::string_view(), len };
if (buf[offset] != '=')
{
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead '=' after ", key);
key = std::string_view();
}
offset++;
if (offset >= len) return { key, std::string_view(), len };
auto value = ExtractString (buf + offset, len - offset);
offset += value.length () + 1;
if (offset >= len) return { key, std::string_view(), len };
if (buf[offset] != ';')
{
LogPrint (eLogWarning, "RouterInfo: Unexpected character ", buf[offset], " instead ';' after ", value);
value = std::string_view();
}
offset++;
return { key, value, offset };
} }
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps) void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps)
@@ -1402,9 +1381,9 @@ namespace data
if (!introducer.iTag) continue; if (!introducer.iTag) continue;
if (introducer.iExp) // expiration is specified if (introducer.iExp) // expiration is specified
{ {
WriteString ("iexp" + boost::lexical_cast<std::string>(i), properties); WriteString ("iexp" + std::to_string(i), properties);
properties << '='; properties << '=';
WriteString (boost::lexical_cast<std::string>(introducer.iExp), properties); WriteString (std::to_string(introducer.iExp), properties);
properties << ';'; properties << ';';
} }
i++; i++;
@@ -1413,7 +1392,7 @@ namespace data
for (const auto& introducer: address.ssu->introducers) for (const auto& introducer: address.ssu->introducers)
{ {
if (!introducer.iTag) continue; if (!introducer.iTag) continue;
WriteString ("ih" + boost::lexical_cast<std::string>(i), properties); WriteString ("ih" + std::to_string(i), properties);
properties << '='; properties << '=';
char value[64]; char value[64];
size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64); size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64);
@@ -1426,9 +1405,9 @@ namespace data
for (const auto& introducer: address.ssu->introducers) for (const auto& introducer: address.ssu->introducers)
{ {
if (!introducer.iTag) continue; if (!introducer.iTag) continue;
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties); WriteString ("itag" + std::to_string(i), properties);
properties << '='; properties << '=';
WriteString (boost::lexical_cast<std::string>(introducer.iTag), properties); WriteString (std::to_string(introducer.iTag), properties);
properties << ';'; properties << ';';
i++; i++;
} }
@@ -1442,7 +1421,7 @@ namespace data
{ {
WriteString ("mtu", properties); WriteString ("mtu", properties);
properties << '='; properties << '=';
WriteString (boost::lexical_cast<std::string>(address.ssu->mtu), properties); WriteString (std::to_string(address.ssu->mtu), properties);
properties << ';'; properties << ';';
} }
} }
@@ -1450,7 +1429,7 @@ namespace data
{ {
WriteString ("port", properties); WriteString ("port", properties);
properties << '='; properties << '=';
WriteString (boost::lexical_cast<std::string>(address.port), properties); WriteString (std::to_string(address.port), properties);
properties << ';'; properties << ';';
} }
if (address.IsNTCP2 () || address.IsSSU2 ()) if (address.IsNTCP2 () || address.IsSSU2 ())
@@ -1485,9 +1464,11 @@ namespace data
s.write (properties.str ().c_str (), properties.str ().size ()); s.write (properties.str ().c_str (), properties.str ().size ());
} }
void LocalRouterInfo::SetProperty (const std::string& key, const std::string& value) void LocalRouterInfo::SetProperty (std::string_view key, std::string_view value)
{ {
m_Properties[key] = value; auto [it, inserted] = m_Properties.emplace (key, value);
if (!inserted)
it->second = value;
} }
void LocalRouterInfo::DeleteProperty (const std::string& key) void LocalRouterInfo::DeleteProperty (const std::string& key)

View File

@@ -11,6 +11,8 @@
#include <inttypes.h> #include <inttypes.h>
#include <string> #include <string>
#include <string_view>
#include <tuple>
#include <map> #include <map>
#include <vector> #include <vector>
#include <array> #include <array>
@@ -219,7 +221,7 @@ namespace data
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
uint64_t GetTimestamp () const { return m_Timestamp; }; uint64_t GetTimestamp () const { return m_Timestamp; };
int GetVersion () const { return m_Version; }; int GetVersion () const { return m_Version; };
virtual void SetProperty (const std::string& key, const std::string& value) {}; virtual void SetProperty (std::string_view key, std::string_view value) {};
virtual void ClearProperties () {}; virtual void ClearProperties () {};
AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr AddressesPtr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr
std::shared_ptr<const Address> GetNTCP2V4Address () const; std::shared_ptr<const Address> GetNTCP2V4Address () const;
@@ -333,11 +335,12 @@ namespace data
bool LoadFile (const std::string& fullPath); bool LoadFile (const std::string& fullPath);
void ReadFromFile (const std::string& fullPath); void ReadFromFile (const std::string& fullPath);
void ReadFromStream (std::istream& s); bool ReadFromBuffer (const uint8_t * buf, size_t len); // return false if malformed
void ReadFromBuffer (bool verifySignature); void ReadFromBuffer (bool verifySignature);
size_t ReadString (char* str, size_t len, std::istream& s) const; std::string_view ExtractString (const uint8_t * buf, size_t len) const;
void ExtractCaps (const char * value); std::tuple<std::string_view, std::string_view, size_t> ExtractParam (const uint8_t * buf, size_t len) const;
uint8_t ExtractAddressCaps (const char * value) const; void ExtractCaps (std::string_view value);
uint8_t ExtractAddressCaps (std::string_view value) const;
void UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts); void UpdateIntroducers (std::shared_ptr<Address> address, uint64_t ts);
template<typename Filter> template<typename Filter>
std::shared_ptr<const Address> GetAddress (Filter filter) const; std::shared_ptr<const Address> GetAddress (Filter filter) const;
@@ -379,7 +382,7 @@ namespace data
void UpdateCaps (uint8_t caps); void UpdateCaps (uint8_t caps);
bool UpdateCongestion (Congestion c); // returns true if updated bool UpdateCongestion (Congestion c); // returns true if updated
void SetProperty (const std::string& key, const std::string& value) override; void SetProperty (std::string_view key, std::string_view value) override;
void DeleteProperty (const std::string& key); void DeleteProperty (const std::string& key);
std::string GetProperty (const std::string& key) const; std::string GetProperty (const std::string& key) const;
void ClearProperties () override { m_Properties.clear (); }; void ClearProperties () override { m_Properties.clear (); };

View File

@@ -191,12 +191,7 @@ namespace transport
void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed) void SSU2PeerTestSession::SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, bool delayed)
{ {
#if __cplusplus >= 202002L // C++20
m_SignedData.assign (signedData, signedData + signedDataLen); m_SignedData.assign (signedData, signedData + signedDataLen);
#else
m_SignedData.resize (signedDataLen);
memcpy (m_SignedData.data (), signedData, signedDataLen);
#endif
if (!delayed) if (!delayed)
SendPeerTest (msg); SendPeerTest (msg);
// schedule resend for msgs 5 or 6 // schedule resend for msgs 5 or 6
@@ -257,7 +252,7 @@ namespace transport
{ {
// we are Charlie // we are Charlie
uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id uint64_t destConnID = htobe64 (((uint64_t)nonce << 32) | nonce); // dest id
uint32_t sourceConnID = ~destConnID; uint64_t sourceConnID = ~destConnID;
SetSourceConnID (sourceConnID); SetSourceConnID (sourceConnID);
SetDestConnID (destConnID); SetDestConnID (destConnID);
SetState (eSSU2SessionStateHolePunch); SetState (eSSU2SessionStateHolePunch);
@@ -313,12 +308,7 @@ namespace transport
void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen) void SSU2HolePunchSession::SendHolePunch (const uint8_t * relayResponseBlock, size_t relayResponseBlockLen)
{ {
#if __cplusplus >= 202002L // C++20
m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen); m_RelayResponseBlock.assign (relayResponseBlock, relayResponseBlock + relayResponseBlockLen);
#else
m_RelayResponseBlock.resize (relayResponseBlockLen);
memcpy (m_RelayResponseBlock.data (), relayResponseBlock, relayResponseBlockLen);
#endif
SendHolePunch (); SendHolePunch ();
ScheduleResend (); ScheduleResend ();
} }

View File

@@ -189,7 +189,7 @@ namespace transport
if (!asz) return false; if (!asz) return false;
payload[17] = asz; payload[17] = asz;
packet->payloadSize = asz + 18; packet->payloadSize = asz + 18;
SignedData s; SignedData<128> s;
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash s.Insert (session->GetRemoteIdentity ()->GetIdentHash (), 32); // chash
@@ -1965,6 +1965,7 @@ namespace transport
void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len) void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len)
{ {
// we are Bob // we are Bob
if (len < 9) return;
auto mts = i2p::util::GetMillisecondsSinceEpoch (); auto mts = i2p::util::GetMillisecondsSinceEpoch ();
uint32_t nonce = bufbe32toh (buf + 1); // nonce uint32_t nonce = bufbe32toh (buf + 1); // nonce
uint32_t relayTag = bufbe32toh (buf + 5); // relay tag uint32_t relayTag = bufbe32toh (buf + 5); // relay tag
@@ -1998,7 +1999,7 @@ namespace transport
packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0; packet->payloadSize = r ? CreateRouterInfoBlock (packet->payload, m_MaxPayloadSize - len - 32, r) : 0;
if (!packet->payloadSize && r) if (!packet->payloadSize && r)
session->SendFragmentedMessage (CreateDatabaseStoreMsg (r)); session->SendFragmentedMessage (CreateDatabaseStoreMsg (r));
packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len -1); packet->payloadSize += CreateRelayIntroBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize, buf + 1, len - 1);
if (packet->payloadSize < m_MaxPayloadSize) if (packet->payloadSize < m_MaxPayloadSize)
packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); packet->payloadSize += CreatePaddingBlock (packet->payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize); uint32_t packetNum = session->SendData (packet->payload, packet->payloadSize);
@@ -2013,18 +2014,24 @@ namespace transport
void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts) void SSU2Session::HandleRelayIntro (const uint8_t * buf, size_t len, int attempts)
{ {
// we are Charlie // we are Charlie
if (len < 47) return;
SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept; SSU2RelayResponseCode code = eSSU2RelayResponseCodeAccept;
boost::asio::ip::udp::endpoint ep; boost::asio::ip::udp::endpoint ep;
std::shared_ptr<const i2p::data::RouterInfo::Address> addr; std::shared_ptr<const i2p::data::RouterInfo::Address> addr;
auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice auto r = i2p::data::netdb.FindRouter (buf + 1); // Alice
if (r) if (r)
{ {
SignedData s; SignedData<128> s;
s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue s.Insert ((const uint8_t *)"RelayRequestData", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (i2p::context.GetIdentHash (), 32); // chash s.Insert (i2p::context.GetIdentHash (), 32); // chash
s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz s.Insert (buf + 33, 14); // nonce, relay tag, timestamp, ver, asz
uint8_t asz = buf[46]; uint8_t asz = buf[46];
if (asz + 47 + r->GetIdentity ()->GetSignatureLen () > len)
{
LogPrint (eLogWarning, "SSU2: Malformed RelayIntro len=", len);
return;
}
s.Insert (buf + 47, asz); // Alice Port, Alice IP s.Insert (buf + 47, asz); // Alice Port, Alice IP
if (s.Verify (r->GetIdentity (), buf + 47 + asz)) if (s.Verify (r->GetIdentity (), buf + 47 + asz))
{ {
@@ -2113,6 +2120,7 @@ namespace transport
void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len) void SSU2Session::HandleRelayResponse (const uint8_t * buf, size_t len)
{ {
if (len < 6) return;
uint32_t nonce = bufbe32toh (buf + 2); uint32_t nonce = bufbe32toh (buf + 2);
if (m_State == eSSU2SessionStateIntroduced) if (m_State == eSSU2SessionStateIntroduced)
{ {
@@ -2133,7 +2141,9 @@ namespace transport
auto it = m_RelaySessions.find (nonce); auto it = m_RelaySessions.find (nonce);
if (it != m_RelaySessions.end ()) if (it != m_RelaySessions.end ())
{ {
if (it->second.first && it->second.first->IsEstablished ()) auto relaySession = it->second.first;
m_RelaySessions.erase (it);
if (relaySession && relaySession->IsEstablished ())
{ {
// we are Bob, message from Charlie // we are Bob, message from Charlie
auto packet = m_Server.GetSentPacketsPool ().AcquireShared (); auto packet = m_Server.GetSentPacketsPool ().AcquireShared ();
@@ -2143,12 +2153,12 @@ namespace transport
memcpy (payload + 3, buf, len); // forward to Alice as is memcpy (payload + 3, buf, len); // forward to Alice as is
packet->payloadSize = len + 3; packet->payloadSize = len + 3;
packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize); packet->payloadSize += CreatePaddingBlock (payload + packet->payloadSize, m_MaxPayloadSize - packet->payloadSize);
uint32_t packetNum = it->second.first->SendData (packet->payload, packet->payloadSize); uint32_t packetNum = relaySession->SendData (packet->payload, packet->payloadSize);
if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION) if (m_RemoteVersion >= SSU2_MIN_RELAY_RESPONSE_RESEND_VERSION)
{ {
// sometimes Alice doesn't ack this RelayResponse in older versions // sometimes Alice doesn't ack this RelayResponse in older versions
packet->sendTime = i2p::util::GetMillisecondsSinceEpoch (); packet->sendTime = i2p::util::GetMillisecondsSinceEpoch ();
it->second.first->m_SentPackets.emplace (packetNum, packet); relaySession->m_SentPackets.emplace (packetNum, packet);
} }
} }
else else
@@ -2157,25 +2167,31 @@ namespace transport
if (!buf[1]) // status code accepted? if (!buf[1]) // status code accepted?
{ {
// verify signature // verify signature
uint8_t csz = buf[11]; uint8_t csz = (len >= 12) ? buf[11] : 0;
SignedData s; if (csz + 12 + relaySession->GetRemoteIdentity ()->GetSignatureLen () > len)
{
LogPrint (eLogWarning, "SSU2: Malformed RelayResponse len=", len);
relaySession->Done ();
return;
}
SignedData<128> s;
s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint s.Insert (buf + 2, 10 + csz); // nonce, timestamp, ver, csz and Charlie's endpoint
if (s.Verify (it->second.first->GetRemoteIdentity (), buf + 12 + csz)) if (s.Verify (relaySession->GetRemoteIdentity (), buf + 12 + csz))
{ {
if (it->second.first->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet if (relaySession->m_State == eSSU2SessionStateIntroduced) // HolePunch not received yet
{ {
// update Charlie's endpoint // update Charlie's endpoint
if (ExtractEndpoint (buf + 12, csz, it->second.first->m_RemoteEndpoint)) if (ExtractEndpoint (buf + 12, csz, relaySession->m_RemoteEndpoint))
{ {
// update token // update token
uint64_t token; uint64_t token;
memcpy (&token, buf + len - 8, 8); memcpy (&token, buf + len - 8, 8);
m_Server.UpdateOutgoingToken (it->second.first->m_RemoteEndpoint, m_Server.UpdateOutgoingToken (relaySession->m_RemoteEndpoint,
token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT); token, i2p::util::GetSecondsSinceEpoch () + SSU2_TOKEN_EXPIRATION_TIMEOUT);
// connect to Charlie, HolePunch will be ignored // connect to Charlie, HolePunch will be ignored
it->second.first->ConnectAfterIntroduction (); relaySession->ConnectAfterIntroduction ();
} }
else else
LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint"); LogPrint (eLogWarning, "SSU2: RelayResponse can't extract endpoint");
@@ -2184,16 +2200,15 @@ namespace transport
else else
{ {
LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed"); LogPrint (eLogWarning, "SSU2: RelayResponse signature verification failed");
it->second.first->Done (); relaySession->Done ();
} }
} }
else else
{ {
LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1], " nonce=", bufbe32toh (buf + 2)); LogPrint (eLogInfo, "SSU2: RelayResponse status code=", (int)buf[1], " nonce=", bufbe32toh (buf + 2));
it->second.first->Done (); relaySession->Done ();
} }
} }
m_RelaySessions.erase (it);
} }
else else
LogPrint (eLogDebug, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2)); LogPrint (eLogDebug, "SSU2: RelayResponse unknown nonce ", bufbe32toh (buf + 2));
@@ -2262,10 +2277,13 @@ namespace transport
case 2: // Charlie from Bob case 2: // Charlie from Bob
{ {
// sign with Charlie's key // sign with Charlie's key
if (len < offset + 9) return;
uint8_t asz = buf[offset + 9]; uint8_t asz = buf[offset + 9];
std::vector<uint8_t> newSignedData (asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ()); size_t l = asz + 10 + i2p::context.GetIdentity ()->GetSignatureLen ();
if (len < offset + l) return;
std::vector<uint8_t> newSignedData (l);
memcpy (newSignedData.data (), buf + offset, asz + 10); memcpy (newSignedData.data (), buf + offset, asz + 10);
SignedData s; SignedData<128> s;
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (buf + 3, 32); // ahash s.Insert (buf + 3, 32); // ahash
@@ -2373,10 +2391,16 @@ namespace transport
if (GetRouterStatus () == eRouterStatusUnknown) if (GetRouterStatus () == eRouterStatusUnknown)
SetTestingState (true); SetTestingState (true);
auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie auto r = i2p::data::netdb.FindRouter (buf + 3); // find Charlie
if (r) if (r && len >= offset + 9)
{ {
uint8_t asz = buf[offset + 9]; uint8_t asz = buf[offset + 9];
SignedData s; if (len < offset + asz + 10 + r->GetIdentity ()->GetSignatureLen ())
{
LogPrint (eLogWarning, "Malformed PeerTest 4 len=", len);
session->Done ();
return;
}
SignedData<128> s;
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash s.Insert (i2p::context.GetIdentity ()->GetIdentHash (), 32); // ahash
@@ -2762,7 +2786,7 @@ namespace transport
size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize) size_t SSU2Session::CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize)
{ {
if (len < 3 || len < minSize) return 0; if (len < 3 || len < minSize) return 0;
size_t paddingSize = m_Server.GetRng ()() & 0x0F; // 0 - 15 size_t paddingSize = m_Server.GetRng ()() & 0x1F; // 0 - 31
if (paddingSize + 3 > len) paddingSize = len - 3; if (paddingSize + 3 > len) paddingSize = len - 3;
else if (paddingSize + 3 < minSize) paddingSize = minSize - 3; else if (paddingSize + 3 < minSize) paddingSize = minSize - 3;
buf[0] = eSSU2BlkPadding; buf[0] = eSSU2BlkPadding;
@@ -2864,7 +2888,7 @@ namespace transport
LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len); LogPrint (eLogError, "SSU2: Buffer for RelayResponse signature is too small ", len);
return 0; return 0;
} }
SignedData s; SignedData<128> s;
s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue s.Insert ((const uint8_t *)"RelayAgreementOK", 16); // prologue
if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie if (code == eSSU2RelayResponseCodeAccept || code >= 64) // Charlie
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
@@ -2926,7 +2950,7 @@ namespace transport
size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port)); size_t asz = CreateEndpoint (signedData + 10, 86, boost::asio::ip::udp::endpoint (localAddress->host, localAddress->port));
signedData[9] = asz; signedData[9] = asz;
// signature // signature
SignedData s; SignedData<128> s;
s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue s.Insert ((const uint8_t *)"PeerTestValidate", 16); // prologue
s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash s.Insert (GetRemoteIdentity ()->GetIdentHash (), 32); // bhash
s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint s.Insert (signedData, 10 + asz); // ver, nonce, ts, asz, Alice's endpoint

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -149,5 +149,56 @@ namespace crypto
LogPrint (eLogError, "EdDSA signing key is not set"); LogPrint (eLogError, "EdDSA signing key is not set");
} }
#endif #endif
#if (OPENSSL_VERSION_NUMBER >= 0x030000000)
static const OSSL_PARAM EDDSA25519phParams[] =
{
OSSL_PARAM_utf8_string ("instance", (char *)"Ed25519ph", 9),
OSSL_PARAM_END
};
bool EDDSA25519phVerifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
{
auto pkey = GetPkey ();
if (pkey)
{
uint8_t digest[64];
SHA512 (buf, len, digest);
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
EVP_DigestVerifyInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams);
auto ret = EVP_DigestVerify (ctx, signature, 64, digest, 64);
EVP_MD_CTX_destroy (ctx);
return ret;
}
else
LogPrint (eLogError, "EdDSA verification key is not set");
return false;
}
EDDSA25519phSigner::EDDSA25519phSigner (const uint8_t * signingPrivateKey):
EDDSA25519Signer (signingPrivateKey)
{
}
void EDDSA25519phSigner::Sign (const uint8_t * buf, int len, uint8_t * signature) const
{
auto pkey = GetPkey ();
if (pkey)
{
uint8_t digest[64];
SHA512 (buf, len, digest);
EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
size_t l = 64;
uint8_t sig[64];
EVP_DigestSignInit_ex (ctx, NULL, NULL, NULL, NULL, pkey, EDDSA25519phParams);
if (!EVP_DigestSign (ctx, sig, &l, digest, 64))
LogPrint (eLogError, "EdDSA signing failed");
memcpy (signature, sig, 64);
EVP_MD_CTX_destroy (ctx);
}
else
LogPrint (eLogError, "EdDSA signing key is not set");
}
#endif
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2023, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -304,13 +304,27 @@ namespace crypto
private: private:
#if OPENSSL_EDDSA #if OPENSSL_EDDSA
EVP_PKEY * m_Pkey; EVP_PKEY * m_Pkey;
protected:
EVP_PKEY * GetPkey () const { return m_Pkey; };
#else #else
EDDSAPoint m_PublicKey; EDDSAPoint m_PublicKey;
uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH]; uint8_t m_PublicKeyEncoded[EDDSA25519_PUBLIC_KEY_LENGTH];
#endif #endif
}; };
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
class EDDSA25519phVerifier: public EDDSA25519Verifier
{
public:
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
};
#endif
class EDDSA25519SignerCompat: public Signer class EDDSA25519SignerCompat: public Signer
{ {
public: public:
@@ -339,6 +353,10 @@ namespace crypto
void Sign (const uint8_t * buf, int len, uint8_t * signature) const; void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
protected:
EVP_PKEY * GetPkey () const { return m_Pkey; };
private: private:
EVP_PKEY * m_Pkey; EVP_PKEY * m_Pkey;
@@ -348,6 +366,18 @@ namespace crypto
typedef EDDSA25519SignerCompat EDDSA25519Signer; typedef EDDSA25519SignerCompat EDDSA25519Signer;
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x030000000) // since 3.0.0
class EDDSA25519phSigner: public EDDSA25519Signer
{
public:
EDDSA25519phSigner (const uint8_t * signingPrivateKey);
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
};
#endif #endif
inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey) inline void CreateEDDSA25519RandomKeys (uint8_t * signingPrivateKey, uint8_t * signingPublicKey)

View File

@@ -613,10 +613,8 @@ namespace stream
if (wasInitial) if (wasInitial)
ScheduleResend (); ScheduleResend ();
} }
if (m_IsClientChoked && ackThrough > m_DropWindowDelaySequenceNumber) if (m_IsClientChoked && ackThrough >= m_DropWindowDelaySequenceNumber)
{
m_IsClientChoked = false; m_IsClientChoked = false;
}
if (m_IsWinDropped && ackThrough > m_DropWindowDelaySequenceNumber) if (m_IsWinDropped && ackThrough > m_DropWindowDelaySequenceNumber)
{ {
m_IsFirstRttSample = true; m_IsFirstRttSample = true;
@@ -1297,7 +1295,7 @@ namespace stream
m_NumPacketsToSend = 1; m_PacingTimeRem = 0; m_NumPacketsToSend = 1; m_PacingTimeRem = 0;
} }
m_IsSendTime = true; m_IsSendTime = true;
if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime) if (m_WindowIncCounter && (m_WindowSize < MAX_WINDOW_SIZE || m_WindowDropTargetSize) && !m_SendBuffer.IsEmpty () && m_PacingTime > m_MinPacingTime && m_RTT <= m_SlowRTT)
{ {
for (int i = 0; i < m_NumPacketsToSend; i++) for (int i = 0; i < m_NumPacketsToSend; i++)
{ {
@@ -1307,7 +1305,7 @@ namespace stream
{ {
if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowDropTargetSize)) if (m_LastWindowDropSize && (m_LastWindowDropSize >= m_WindowDropTargetSize))
m_WindowDropTargetSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowDropTargetSize)); // some magic here m_WindowDropTargetSize += 1 - (1 / ((m_LastWindowDropSize + PREV_SPEED_KEEP_TIME_COEFF) / m_WindowDropTargetSize)); // some magic here
else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowSize)) else if (m_LastWindowDropSize && (m_LastWindowDropSize < m_WindowDropTargetSize))
m_WindowDropTargetSize += (m_WindowDropTargetSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; // some magic here m_WindowDropTargetSize += (m_WindowDropTargetSize - (m_LastWindowDropSize - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; // some magic here
else else
m_WindowDropTargetSize += (m_WindowDropTargetSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize; m_WindowDropTargetSize += (m_WindowDropTargetSize - (1 - PREV_SPEED_KEEP_TIME_COEFF)) / m_WindowDropTargetSize;
@@ -1646,14 +1644,22 @@ namespace stream
void Stream::ProcessWindowDrop () void Stream::ProcessWindowDrop ()
{ {
if (m_WindowSize > m_LastWindowDropSize) if (m_WindowDropTargetSize)
{ m_WindowDropTargetSize = (m_WindowDropTargetSize / 2) * 0.75; // congestion window size and -25% to drain queue
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2;
if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE;
}
else else
m_LastWindowDropSize = m_WindowSize; {
m_WindowDropTargetSize = m_LastWindowDropSize - (m_LastWindowDropSize / 4); // -25%; if (m_WindowSize < m_LastWindowDropSize)
{
m_LastWindowDropSize = m_WindowSize - (m_LastWindowDropSize - m_WindowSize);
if (m_LastWindowDropSize < MIN_WINDOW_SIZE) m_LastWindowDropSize = MIN_WINDOW_SIZE;
}
else
{
m_LastWindowDropSize = (m_LastWindowDropSize + m_WindowSize + m_WindowSizeTail) / 2;
if (m_LastWindowDropSize > MAX_WINDOW_SIZE) m_LastWindowDropSize = MAX_WINDOW_SIZE;
}
m_WindowDropTargetSize = m_LastWindowDropSize * 0.75; // -25% to drain queue
}
if (m_WindowDropTargetSize < MIN_WINDOW_SIZE) if (m_WindowDropTargetSize < MIN_WINDOW_SIZE)
m_WindowDropTargetSize = MIN_WINDOW_SIZE; m_WindowDropTargetSize = MIN_WINDOW_SIZE;
m_WindowIncCounter = 0; // disable window growth m_WindowIncCounter = 0; // disable window growth

View File

@@ -131,27 +131,35 @@ namespace tunnel
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());
std::lock_guard<std::mutex> l(m_HandleMutex); std::lock_guard<std::mutex> l(m_HandleMutex);
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg); if (!m_Endpoint) m_Endpoint = std::make_unique<TunnelEndpoint>(false); // transit endpoint is always outbound
m_Endpoint->HandleDecryptedTunnelDataMsg (newMsg);
} }
void TransitTunnelEndpoint::FlushTunnelDataMsgs () void TransitTunnelEndpoint::FlushTunnelDataMsgs ()
{ {
std::lock_guard<std::mutex> l(m_HandleMutex); if (m_Endpoint)
m_Endpoint.FlushI2NPMsgs (); {
std::lock_guard<std::mutex> l(m_HandleMutex);
m_Endpoint->FlushI2NPMsgs ();
}
} }
void TransitTunnelEndpoint::Cleanup () void TransitTunnelEndpoint::Cleanup ()
{ {
std::lock_guard<std::mutex> l(m_HandleMutex); if (m_Endpoint)
m_Endpoint.Cleanup (); {
std::lock_guard<std::mutex> l(m_HandleMutex);
m_Endpoint->Cleanup ();
}
} }
std::string TransitTunnelEndpoint::GetNextPeerName () const std::string TransitTunnelEndpoint::GetNextPeerName () const
{ {
auto hash = m_Endpoint.GetCurrentHash (); if (!m_Endpoint) return "";
auto hash = m_Endpoint->GetCurrentHash ();
if (hash) if (hash)
{ {
const auto& sender = m_Endpoint.GetSender (); const auto& sender = m_Endpoint->GetSender ();
if (sender) if (sender)
{ {
auto transport = sender->GetCurrentTransport (); auto transport = sender->GetCurrentTransport ();

View File

@@ -97,20 +97,19 @@ namespace tunnel
TransitTunnelEndpoint (uint32_t receiveTunnelID, TransitTunnelEndpoint (uint32_t receiveTunnelID,
const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID, const i2p::data::IdentHash& nextIdent, uint32_t nextTunnelID,
const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey): const i2p::crypto::AESKey& layerKey, const i2p::crypto::AESKey& ivKey):
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey) {};
m_Endpoint (false) {}; // transit endpoint is always outbound
void Cleanup () override; void Cleanup () override;
void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) override; void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg) override;
void FlushTunnelDataMsgs () override; void FlushTunnelDataMsgs () override;
size_t GetNumTransmittedBytes () const override { return m_Endpoint.GetNumReceivedBytes (); } size_t GetNumTransmittedBytes () const override { return m_Endpoint ? m_Endpoint->GetNumReceivedBytes () : 0; }
std::string GetNextPeerName () const override; std::string GetNextPeerName () const override;
private: private:
std::mutex m_HandleMutex; std::mutex m_HandleMutex;
TunnelEndpoint m_Endpoint; std::unique_ptr<TunnelEndpoint> m_Endpoint;
}; };
std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID, std::shared_ptr<TransitTunnel> CreateTransitTunnel (uint32_t receiveTunnelID,

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -10,7 +10,7 @@
#define TRANSPORT_SESSION_H__ #define TRANSPORT_SESSION_H__
#include <inttypes.h> #include <inttypes.h>
#include <iostream> #include <string.h>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <mutex> #include <mutex>
@@ -28,45 +28,51 @@ namespace transport
const size_t IPV6_HEADER_SIZE = 40; const size_t IPV6_HEADER_SIZE = 40;
const size_t UDP_HEADER_SIZE = 8; const size_t UDP_HEADER_SIZE = 8;
template<size_t sz>
class SignedData class SignedData
{ {
public: public:
SignedData () {} SignedData (): m_Size(0) {}
SignedData (const SignedData& other) SignedData (const SignedData& other)
{ {
m_Stream << other.m_Stream.rdbuf (); m_Size = other.m_Size;
memcpy (m_Buf, other.m_Buf, m_Size);
} }
void Reset () void Reset ()
{ {
m_Stream.str(""); m_Size = 0;
} }
void Insert (const uint8_t * buf, size_t len) size_t Insert (const uint8_t * buf, size_t len)
{ {
m_Stream.write ((char *)buf, len); if (m_Size + len > sz) len = sz - m_Size;
memcpy (m_Buf + m_Size, buf, len);
m_Size += len;
return len;
} }
template<typename T> template<typename T>
void Insert (T t) void Insert (T t)
{ {
m_Stream.write ((char *)&t, sizeof (T)); Insert ((const uint8_t *)&t, sizeof (T));
} }
bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const bool Verify (std::shared_ptr<const i2p::data::IdentityEx> ident, const uint8_t * signature) const
{ {
return ident->Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); return ident->Verify (m_Buf, m_Size, signature);
} }
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
{ {
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature); keys.Sign (m_Buf, m_Size, signature);
} }
private: private:
std::stringstream m_Stream; uint8_t m_Buf[sz];
size_t m_Size;
}; };
const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds const int64_t TRANSPORT_SESSION_SLOWNESS_THRESHOLD = 500; // in milliseconds

View File

@@ -680,8 +680,21 @@ namespace transport
auto directTransports = compatibleTransports & peer->router->GetPublishedTransports (); auto directTransports = compatibleTransports & peer->router->GetPublishedTransports ();
peer->numAttempts = 0; peer->numAttempts = 0;
peer->priority.clear (); peer->priority.clear ();
bool isReal = peer->router->GetProfile ()->IsReal ();
bool ssu2 = isReal ? (m_Rng () & 1) : false; // try NTCP2 if router is not confirmed real std::shared_ptr<RouterProfile> profile;
if (peer->router->HasProfile ()) profile = peer->router->GetProfile (); // only if in memory
bool ssu2 = false; // NTCP2 by default
bool isReal = profile ? profile->IsReal () : true;
if (isReal)
{
ssu2 = m_Rng () & 1; // 1/2
if (ssu2 && !profile)
{
profile = peer->router->GetProfile (); // load profile if necessary
isReal = profile->IsReal ();
if (!isReal) ssu2 = false; // try NTCP2 if router is not confirmed real
}
}
const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority; const auto& priority = ssu2 ? ssu2Priority : ntcp2Priority;
if (directTransports) if (directTransports)
{ {

View File

@@ -351,10 +351,13 @@ namespace tunnel
{ {
it.second.first->SetState (eTunnelStateFailed); it.second.first->SetState (eTunnelStateFailed);
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex); std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
if (m_OutboundTunnels.size () > 1 || m_NumOutboundTunnels <= 1) // don't fail last tunnel if (m_OutboundTunnels.size () > 1) // don't fail last tunnel
m_OutboundTunnels.erase (it.second.first); m_OutboundTunnels.erase (it.second.first);
else else
{
it.second.first->SetState (eTunnelStateTestFailed); it.second.first->SetState (eTunnelStateTestFailed);
CreateOutboundTunnel (); // create new tunnel immediately because last one failed
}
} }
else if (it.second.first->GetState () != eTunnelStateExpiring) else if (it.second.first->GetState () != eTunnelStateExpiring)
it.second.first->SetState (eTunnelStateTestFailed); it.second.first->SetState (eTunnelStateTestFailed);
@@ -368,13 +371,16 @@ namespace tunnel
bool failed = false; bool failed = false;
{ {
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex); std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
if (m_InboundTunnels.size () > 1 || m_NumInboundTunnels <= 1) // don't fail last tunnel if (m_InboundTunnels.size () > 1) // don't fail last tunnel
{ {
m_InboundTunnels.erase (it.second.second); m_InboundTunnels.erase (it.second.second);
failed = true; failed = true;
} }
else else
{
it.second.second->SetState (eTunnelStateTestFailed); it.second.second->SetState (eTunnelStateTestFailed);
CreateInboundTunnel (); // create new tunnel immediately because last one failed
}
} }
if (failed && m_LocalDestination) if (failed && m_LocalDestination)
m_LocalDestination->SetLeaseSetUpdated (true); m_LocalDestination->SetLeaseSetUpdated (true);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -16,6 +16,24 @@ namespace i2p
{ {
namespace client namespace client
{ {
void BOBI2PTunnelIncomingConnection::Established ()
{
if (m_IsQuiet)
StreamReceive ();
else
{
// send destination first like received from I2P
std::string dest = GetStream ()->GetRemoteIdentity ()->ToBase64 ();
dest += "\n";
if (dest.size() <= I2P_TUNNEL_CONNECTION_BUFFER_SIZE)
memcpy (GetStreamBuffer (), dest.c_str (), dest.size ());
else
memset (GetStreamBuffer (), 0, I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
HandleStreamReceive (boost::system::error_code (), dest.size ());
}
Receive ();
}
BOBI2PInboundTunnel::BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<ClientDestination> localDestination): BOBI2PInboundTunnel::BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<ClientDestination> localDestination):
BOBI2PTunnel (localDestination), m_Acceptor (localDestination->GetService (), ep) BOBI2PTunnel (localDestination), m_Acceptor (localDestination->GetService (), ep)
{ {
@@ -156,7 +174,7 @@ namespace client
{ {
if (stream) if (stream)
{ {
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, m_Endpoint, m_IsQuiet); auto conn = std::make_shared<BOBI2PTunnelIncomingConnection> (this, stream, m_Endpoint, m_IsQuiet);
AddHandler (conn); AddHandler (conn);
conn->Connect (); conn->Connect ();
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -71,6 +71,23 @@ namespace client
const char BOB_HELP_STATUS[] = "status <NICKNAME> - Display status of a nicknamed tunnel."; const char BOB_HELP_STATUS[] = "status <NICKNAME> - Display status of a nicknamed tunnel.";
const char BOB_HELP_HELP [] = "help <COMMAND> - Get help on a command."; const char BOB_HELP_HELP [] = "help <COMMAND> - Get help on a command.";
class BOBI2PTunnelIncomingConnection: public I2PTunnelConnection
{
public:
BOBI2PTunnelIncomingConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, bool quiet):
I2PTunnelConnection (owner, stream, target), m_IsQuiet (quiet) {};
protected:
void Established () override;
private:
bool m_IsQuiet; // don't send destination
};
class BOBI2PTunnel: public I2PService class BOBI2PTunnel: public I2PService
{ {
public: public:

View File

@@ -571,7 +571,7 @@ namespace client
m_IsSending = false; m_IsSending = false;
} }
std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len) std::string_view I2CPSession::ExtractString (const uint8_t * buf, size_t len) const
{ {
uint8_t l = buf[0]; uint8_t l = buf[0];
if (l > len) l = len; if (l > len) l = len;
@@ -588,7 +588,7 @@ namespace client
return l + 1; return l + 1;
} }
void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping) void I2CPSession::ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping) const
// TODO: move to Base.cpp // TODO: move to Base.cpp
{ {
size_t offset = 0; size_t offset = 0;

View File

@@ -194,9 +194,9 @@ namespace client
void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleI2CPMessageSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
std::string_view ExtractString (const uint8_t * buf, size_t len); std::string_view ExtractString (const uint8_t * buf, size_t len) const;
size_t PutString (uint8_t * buf, size_t len, std::string_view str); size_t PutString (uint8_t * buf, size_t len, std::string_view str);
void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping); void ExtractMapping (const uint8_t * buf, size_t len, std::map<std::string, std::string>& mapping) const;
void SendSessionStatusMessage (I2CPSessionStatus status); void SendSessionStatusMessage (I2CPSessionStatus status);
void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity); void SendHostReplyMessage (uint32_t requestID, std::shared_ptr<const i2p::data::IdentityEx> identity);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -32,8 +32,7 @@ namespace client
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket, I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint16_t port): std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint16_t port):
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()), I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ())
m_IsQuiet (true)
{ {
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port); m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port);
} }
@@ -41,14 +40,13 @@ namespace client
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream): std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream), I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true) m_RemoteEndpoint (socket->remote_endpoint ())
{ {
} }
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, bool quiet, const boost::asio::ip::tcp::endpoint& target,std::shared_ptr<boost::asio::ssl::context> sslCtx):
std::shared_ptr<boost::asio::ssl::context> sslCtx): I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target)
I2PServiceHandler(owner), m_Stream (stream), m_RemoteEndpoint (target), m_IsQuiet (quiet)
{ {
m_Socket = std::make_shared<boost::asio::ip::tcp::socket> (owner->GetService ()); m_Socket = std::make_shared<boost::asio::ip::tcp::socket> (owner->GetService ());
if (sslCtx) if (sslCtx)
@@ -292,18 +290,7 @@ namespace client
void I2PTunnelConnection::Established () void I2PTunnelConnection::Established ()
{ {
if (m_IsQuiet) StreamReceive ();
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 (); Receive ();
} }
@@ -377,7 +364,7 @@ namespace client
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P, const boost::asio::ip::tcp::endpoint& target, const std::string& host, const std::string& XI2P,
std::shared_ptr<boost::asio::ssl::context> sslCtx): std::shared_ptr<boost::asio::ssl::context> sslCtx):
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_Host (host), m_XI2P (XI2P), I2PTunnelConnection (owner, stream, target, sslCtx), m_Host (host), m_XI2P (XI2P),
m_HeaderSent (false), m_ResponseHeaderSent (false) m_HeaderSent (false), m_ResponseHeaderSent (false)
{ {
if (sslCtx) if (sslCtx)
@@ -528,7 +515,7 @@ namespace client
I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PTunnelConnectionIRC::I2PTunnelConnectionIRC (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass, const boost::asio::ip::tcp::endpoint& target, const std::string& webircpass,
std::shared_ptr<boost::asio::ssl::context> sslCtx): std::shared_ptr<boost::asio::ssl::context> sslCtx):
I2PTunnelConnection (owner, stream, target, true, sslCtx), m_From (stream->GetRemoteIdentity ()), I2PTunnelConnection (owner, stream, target, sslCtx), m_From (stream->GetRemoteIdentity ()),
m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass) m_NeedsWebIrc (webircpass.length() ? true : false), m_WebircPass (webircpass)
{ {
} }
@@ -857,7 +844,7 @@ namespace client
std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream) std::shared_ptr<I2PTunnelConnection> I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
{ {
return std::make_shared<I2PTunnelConnection> (this, stream, GetEndpoint (), true, m_SSLCtx); return std::make_shared<I2PTunnelConnection> (this, stream, GetEndpoint (), m_SSLCtx);
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2024, The PurpleI2P Project * Copyright (c) 2013-2025, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -27,7 +27,7 @@ namespace i2p
{ {
namespace client namespace client
{ {
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536; const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 16384;
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
// for HTTP tunnels // for HTTP tunnels
@@ -45,7 +45,7 @@ namespace client
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket, I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
const boost::asio::ip::tcp::endpoint& target, bool quiet = true, const boost::asio::ip::tcp::endpoint& target,
std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr); // from I2P std::shared_ptr<boost::asio::ssl::context> sslCtx = nullptr); // from I2P
~I2PTunnelConnection (); ~I2PTunnelConnection ();
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0); void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
@@ -54,24 +54,26 @@ namespace client
protected: protected:
virtual void Established ();
void Terminate (); void Terminate ();
void Receive (); void Receive ();
void StreamReceive (); void StreamReceive ();
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded virtual void WriteToStream (const uint8_t * buf, size_t len); // can be overloaded
std::shared_ptr<boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; }; std::shared_ptr<boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
std::shared_ptr<i2p::stream::Stream> GetStream () const { return m_Stream; };
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > GetSSL () const { return m_SSL; }; std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > GetSSL () const { return m_SSL; };
uint8_t * GetStreamBuffer () { return m_StreamBuffer; };
private: private:
void HandleConnect (const boost::system::error_code& ecode); void HandleConnect (const boost::system::error_code& ecode);
void HandleHandshake (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 HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
void HandleWrite (const boost::system::error_code& ecode); void HandleWrite (const boost::system::error_code& ecode);
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
private: private:
@@ -80,7 +82,6 @@ namespace client
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > m_SSL; std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > m_SSL;
std::shared_ptr<i2p::stream::Stream> m_Stream; std::shared_ptr<i2p::stream::Stream> m_Stream;
boost::asio::ip::tcp::endpoint m_RemoteEndpoint; boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
bool m_IsQuiet; // don't send destination
}; };
class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection
@@ -94,7 +95,7 @@ namespace client
protected: protected:
void Write (const uint8_t * buf, size_t len); void Write (const uint8_t * buf, size_t len) override;
private: private:
@@ -112,8 +113,8 @@ namespace client
protected: protected:
void Write (const uint8_t * buf, size_t len); void Write (const uint8_t * buf, size_t len) override;
void WriteToStream (const uint8_t * buf, size_t len); void WriteToStream (const uint8_t * buf, size_t len) override;
private: private:
@@ -132,7 +133,7 @@ namespace client
protected: protected:
void Write (const uint8_t * buf, size_t len); void Write (const uint8_t * buf, size_t len) override;
private: private: