diff --git a/Makefile b/Makefile index 460b16e4..6d56ec7d 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,12 @@ ifneq (, $(findstring darwin, $(SYS))) else include Makefile.osx endif +else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS))) + DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp + include Makefile.linux else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS))) DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp include Makefile.bsd -else ifneq (, $(findstring linux, $(SYS))) - DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp - include Makefile.linux else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp include Makefile.mingw diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 4f875b33..fc9ca417 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -94,13 +94,17 @@ endif() add_library(libi2pd ${LIBI2PD_SRC}) set_target_properties(libi2pd PROPERTIES PREFIX "") -install(TARGETS libi2pd - EXPORT libi2pd - ARCHIVE DESTINATION lib - COMPONENT Libraries) + +if (WITH_LIBRARY) + install(TARGETS libi2pd + EXPORT libi2pd + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT Libraries) # TODO Make libi2pd available to 3rd party projects via CMake as imported target # FIXME This pulls stdafx # install(EXPORT libi2pd DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() set (CLIENT_SRC "${LIBI2PD_CLIENT_SRC_DIR}/AddressBook.cpp" @@ -119,7 +123,17 @@ set (CLIENT_SRC if(WITH_WEBSOCKETS) list (APPEND CLIENT_SRC "${LIBI2PD_CLIENT_SRC_DIR}/Websocket.cpp") endif () -add_library(i2pdclient ${CLIENT_SRC}) + +add_library(libi2pdclient ${CLIENT_SRC}) +set_target_properties(libi2pdclient PROPERTIES PREFIX "") + +if (WITH_LIBRARY) + install(TARGETS libi2pdclient + EXPORT libi2pdclient + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + COMPONENT Libraries) +endif() set(DAEMON_SRC_DIR ../daemon) @@ -303,7 +317,7 @@ if (WITH_PCH) WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) target_compile_options(libi2pd PRIVATE /FIstdafx.h /Yustdafx.h /Zm155 "/Fp${CMAKE_BINARY_DIR}/stdafx.dir/$/stdafx.pch") - target_compile_options(i2pdclient PRIVATE /FIstdafx.h /Yustdafx.h /Zm155 "/Fp${CMAKE_BINARY_DIR}/stdafx.dir/$/stdafx.pch") + target_compile_options(libi2pdclient PRIVATE /FIstdafx.h /Yustdafx.h /Zm155 "/Fp${CMAKE_BINARY_DIR}/stdafx.dir/$/stdafx.pch") else() string(TOUPPER ${CMAKE_BUILD_TYPE} BTU) get_directory_property(DEFS DEFINITIONS) @@ -312,12 +326,12 @@ if (WITH_PCH) COMMAND ${CMAKE_CXX_COMPILER} ${FLAGS} -c ${CMAKE_CURRENT_SOURCE_DIR}/../libi2pd/stdafx.h -o ${CMAKE_BINARY_DIR}/stdafx.h.gch ) target_compile_options(libi2pd PRIVATE -include libi2pd/stdafx.h) - target_compile_options(i2pdclient PRIVATE -include libi2pd/stdafx.h) + target_compile_options(libi2pdclient PRIVATE -include libi2pd/stdafx.h) endif() target_link_libraries(libi2pd stdafx) endif() -target_link_libraries(i2pdclient libi2pd) +target_link_libraries(libi2pdclient libi2pd) find_package ( Boost COMPONENTS system filesystem program_options date_time REQUIRED ) if(NOT DEFINED Boost_INCLUDE_DIRS) @@ -450,7 +464,7 @@ if (WITH_BINARY) if (WITH_STATIC) set(DL_LIB ${CMAKE_DL_LIBS}) endif() - target_link_libraries( "${PROJECT_NAME}" libi2pd i2pdclient ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) + target_link_libraries( "${PROJECT_NAME}" libi2pd libi2pdclient ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES}) install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime) set (APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}") diff --git a/contrib/i2pd.service b/contrib/i2pd.service index 5ed31d41..9af96c37 100644 --- a/contrib/i2pd.service +++ b/contrib/i2pd.service @@ -8,6 +8,8 @@ User=i2pd Group=i2pd RuntimeDirectory=i2pd RuntimeDirectoryMode=0700 +LogsDirectory=i2pd +LogsDirectoryMode=0700 Type=simple ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --pidfile=/var/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service ExecReload=/bin/kill -HUP $MAINPID diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index 494ea026..fcff78cd 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -65,6 +65,7 @@ namespace client m_MethodHandlers["RouterInfo"] = &I2PControlService::RouterInfoHandler; m_MethodHandlers["RouterManager"] = &I2PControlService::RouterManagerHandler; m_MethodHandlers["NetworkSetting"] = &I2PControlService::NetworkSettingHandler; + m_MethodHandlers["ClientServicesInfo"] = &I2PControlService::ClientServicesInfoHandler; // I2PControl m_I2PControlHandlers["i2pcontrol.password"] = &I2PControlService::PasswordHandler; @@ -92,6 +93,14 @@ namespace client // NetworkSetting m_NetworkSettingHandlers["i2p.router.net.bw.in"] = &I2PControlService::InboundBandwidthLimit; m_NetworkSettingHandlers["i2p.router.net.bw.out"] = &I2PControlService::OutboundBandwidthLimit; + + // ClientServicesInfo + m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler; + m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler; + m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler; + m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler; + m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler; + m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler; } I2PControlService::~I2PControlService () @@ -289,6 +298,13 @@ namespace client ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; } + void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const + { + std::ostringstream buf; + boost::property_tree::write_json (buf, value, false); + ss << "\"" << name << "\":" << buf.str(); + } + void I2PControlService::SendResponse (std::shared_ptr socket, std::shared_ptr buf, std::ostringstream& response, bool isHtml) { @@ -457,6 +473,7 @@ namespace client InsertParam (results, "i2p.router.net.total.sent.bytes", (double)i2p::transport::transports.GetTotalSentBytes ()); } + // RouterManager void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results) @@ -586,5 +603,178 @@ namespace client } EVP_PKEY_free (pkey); } + +// ClientServicesInfo + + void I2PControlService::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) + { + for (auto it = params.begin (); it != params.end (); it++) + { + LogPrint (eLogDebug, "I2PControl: ClientServicesInfo request: ", it->first); + auto it1 = m_ClientServicesInfoHandlers.find (it->first); + if (it1 != m_ClientServicesInfoHandlers.end ()) + { + if (it != params.begin ()) results << ","; + (this->*(it1->second))(results); + } + else + LogPrint (eLogError, "I2PControl: ClientServicesInfo unknown request ", it->first); + } + } + + void I2PControlService::I2PTunnelInfoHandler (std::ostringstream& results) + { + boost::property_tree::ptree pt; + boost::property_tree::ptree client_tunnels, server_tunnels; + + for (auto& it: i2p::client::context.GetClientTunnels ()) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + boost::property_tree::ptree ct; + ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); + client_tunnels.add_child(it.second->GetName (), ct); + } + + auto& serverTunnels = i2p::client::context.GetServerTunnels (); + if (!serverTunnels.empty ()) { + for (auto& it: serverTunnels) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + boost::property_tree::ptree st; + st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); + st.put("port", it.second->GetLocalPort ()); + server_tunnels.add_child(it.second->GetName (), st); + } + } + + auto& clientForwards = i2p::client::context.GetClientForwards (); + if (!clientForwards.empty ()) + { + for (auto& it: clientForwards) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + boost::property_tree::ptree ct; + ct.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); + client_tunnels.add_child(it.second->GetName (), ct); + } + } + + auto& serverForwards = i2p::client::context.GetServerForwards (); + if (!serverForwards.empty ()) + { + for (auto& it: serverForwards) + { + auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); + boost::property_tree::ptree st; + st.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); + server_tunnels.add_child(it.second->GetName (), st); + } + } + + pt.add_child("client", client_tunnels); + pt.add_child("server", server_tunnels); + + InsertParam (results, "I2PTunnel", pt); + } + + void I2PControlService::HTTPProxyInfoHandler (std::ostringstream& results) + { + boost::property_tree::ptree pt; + + auto httpProxy = i2p::client::context.GetHttpProxy (); + if (httpProxy) + { + auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); + pt.put("enabled", true); + pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); + } + else + pt.put("enabled", false); + + InsertParam (results, "HTTPProxy", pt); + } + + void I2PControlService::SOCKSInfoHandler (std::ostringstream& results) + { + boost::property_tree::ptree pt; + + auto socksProxy = i2p::client::context.GetSocksProxy (); + if (socksProxy) + { + auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); + pt.put("enabled", true); + pt.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); + } + else + pt.put("enabled", false); + + InsertParam (results, "SOCKS", pt); + } + + void I2PControlService::SAMInfoHandler (std::ostringstream& results) + { + boost::property_tree::ptree pt; + auto sam = i2p::client::context.GetSAMBridge (); + if (sam) + { + pt.put("enabled", true); + boost::property_tree::ptree sam_sessions; + for (auto& it: sam->GetSessions ()) + { + boost::property_tree::ptree sam_session, sam_session_sockets; + auto& name = it.second->localDestination->GetNickname (); + auto& ident = it.second->localDestination->GetIdentHash(); + sam_session.put("name", name); + sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); + + for (const auto& socket: it.second->ListSockets()) + { + boost::property_tree::ptree stream; + stream.put("type", socket->GetSocketType ()); + stream.put("peer", socket->GetSocket ().remote_endpoint()); + + sam_session_sockets.push_back(std::make_pair("", stream)); + } + sam_session.add_child("sockets", sam_session_sockets); + sam_sessions.add_child(it.first, sam_session); + } + + pt.add_child("sessions", sam_sessions); + } + else + pt.put("enabled", false); + + InsertParam (results, "SAM", pt); + } + + void I2PControlService::BOBInfoHandler (std::ostringstream& results) + { + boost::property_tree::ptree pt; + auto bob = i2p::client::context.GetBOBCommandChannel (); + if (bob) + { + /* TODO more info */ + pt.put("enabled", true); + } + else + pt.put("enabled", false); + + InsertParam (results, "BOB", pt); + } + + void I2PControlService::I2CPInfoHandler (std::ostringstream& results) + { + boost::property_tree::ptree pt; + auto i2cp = i2p::client::context.GetI2CPServer (); + if (i2cp) + { + /* TODO more info */ + pt.put("enabled", true); + } + else + pt.put("enabled", false); + + InsertParam (results, "I2CP", pt); + } } } diff --git a/daemon/I2PControl.h b/daemon/I2PControl.h index a7ed1eab..3233ad12 100644 --- a/daemon/I2PControl.h +++ b/daemon/I2PControl.h @@ -57,6 +57,7 @@ namespace client void InsertParam (std::ostringstream& ss, const std::string& name, int value) const; void InsertParam (std::ostringstream& ss, const std::string& name, double value) const; void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const; + void InsertParam (std::ostringstream& ss, const std::string& name, const boost::property_tree::ptree& value) const; // methods typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results); @@ -67,6 +68,7 @@ namespace client void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results); void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results); + void ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results); // I2PControl typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value); @@ -98,6 +100,15 @@ namespace client void InboundBandwidthLimit (const std::string& value, std::ostringstream& results); void OutboundBandwidthLimit (const std::string& value, std::ostringstream& results); + // ClientServicesInfo + typedef void (I2PControlService::*ClientServicesInfoRequestHandler)(std::ostringstream& results); + void I2PTunnelInfoHandler (std::ostringstream& results); + void HTTPProxyInfoHandler (std::ostringstream& results); + void SOCKSInfoHandler (std::ostringstream& results); + void SAMInfoHandler (std::ostringstream& results); + void BOBInfoHandler (std::ostringstream& results); + void I2CPInfoHandler (std::ostringstream& results); + private: std::string m_Password; @@ -115,6 +126,7 @@ namespace client std::map m_RouterInfoHandlers; std::map m_RouterManagerHandlers; std::map m_NetworkSettingHandlers; + std::map m_ClientServicesInfoHandlers; }; } } diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index d7d5adf6..5ba3334d 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -373,7 +373,7 @@ namespace crypto } // ECIES - void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) + void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { BN_CTX_start (ctx); BIGNUM * q = BN_CTX_get (ctx); @@ -386,10 +386,19 @@ namespace crypto EC_POINT_mul (curve, p, k, nullptr, nullptr, ctx); BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); - encrypted[0] = 0; - bn2buf (x, encrypted + 1, len); - bn2buf (y, encrypted + 1 + len, len); - RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); + if (zeroPadding) + { + encrypted[0] = 0; + bn2buf (x, encrypted + 1, len); + bn2buf (y, encrypted + 1 + len, len); + RAND_bytes (encrypted + 1 + 2*len, 256 - 2*len); + } + else + { + bn2buf (x, encrypted, len); + bn2buf (y, encrypted + len, len); + RAND_bytes (encrypted + 2*len, 256 - 2*len); + } // ecryption key and iv EC_POINT_mul (curve, p, nullptr, key, k, ctx); EC_POINT_get_affine_coordinates_GFp (curve, p, x, y, nullptr); @@ -403,16 +412,21 @@ namespace crypto memcpy (m+33, data, 222); SHA256 (m+33, 222, m+1); // encrypt - encrypted[257] = 0; CBCEncryption encryption; encryption.SetKey (shared); encryption.SetIV (iv); - encryption.Encrypt (m, 256, encrypted + 258); + if (zeroPadding) + { + encrypted[257] = 0; + encryption.Encrypt (m, 256, encrypted + 258); + } + else + encryption.Encrypt (m, 256, encrypted + 256); EC_POINT_free (p); BN_CTX_end (ctx); } - bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) + bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { bool ret = true; BN_CTX_start (ctx); @@ -421,8 +435,16 @@ namespace crypto int len = BN_num_bytes (q); // point for shared secret BIGNUM * x = BN_CTX_get (ctx), * y = BN_CTX_get (ctx); - BN_bin2bn (encrypted + 1, len, x); - BN_bin2bn (encrypted + 1 + len, len, y); + if (zeroPadding) + { + BN_bin2bn (encrypted + 1, len, x); + BN_bin2bn (encrypted + 1 + len, len, y); + } + else + { + BN_bin2bn (encrypted, len, x); + BN_bin2bn (encrypted + len, len, y); + } auto p = EC_POINT_new (curve); if (EC_POINT_set_affine_coordinates_GFp (curve, p, x, y, nullptr)) { @@ -439,7 +461,10 @@ namespace crypto CBCDecryption decryption; decryption.SetKey (shared); decryption.SetIV (iv); - decryption.Decrypt (encrypted + 258, 256, m); + if (zeroPadding) + decryption.Decrypt (encrypted + 258, 256, m); + else + decryption.Decrypt (encrypted + 256, 256, m); // verify and copy uint8_t hash[32]; SHA256 (m + 33, 222, hash); diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index b833ff19..6e4ddb3d 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -54,8 +54,8 @@ namespace crypto void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub); // ECIES - void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx); // 222 bytes data, 514 bytes encrypted - bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); + void ECIESEncrypt (const EC_GROUP * curve, const EC_POINT * key, const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding = false); // 222 bytes data, 514 bytes encrypted with zeropadding, 512 without + bool ECIESDecrypt (const EC_GROUP * curve, const BIGNUM * key, const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding = false); void GenerateECIESKeyPair (const EC_GROUP * curve, BIGNUM *& priv, EC_POINT *& pub); // HMAC diff --git a/libi2pd/CryptoKey.cpp b/libi2pd/CryptoKey.cpp index 4be230f7..711d4ce6 100644 --- a/libi2pd/CryptoKey.cpp +++ b/libi2pd/CryptoKey.cpp @@ -12,9 +12,9 @@ namespace crypto memcpy (m_PublicKey, pub, 256); } - void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) + void ElGamalEncryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { - ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, true); + ElGamalEncrypt (m_PublicKey, data, encrypted, ctx, zeroPadding); } ElGamalDecryptor::ElGamalDecryptor (const uint8_t * priv) @@ -22,9 +22,9 @@ namespace crypto memcpy (m_PrivateKey, priv, 256); } - bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) + bool ElGamalDecryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { - return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, true); + return ElGamalDecrypt (m_PrivateKey, encrypted, data, ctx, zeroPadding); } ECIESP256Encryptor::ECIESP256Encryptor (const uint8_t * pub) @@ -44,10 +44,10 @@ namespace crypto if (m_PublicKey) EC_POINT_free (m_PublicKey); } - void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) + void ECIESP256Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { if (m_Curve && m_PublicKey) - ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted, ctx); + ECIESEncrypt (m_Curve, m_PublicKey, data, encrypted, ctx, zeroPadding); } ECIESP256Decryptor::ECIESP256Decryptor (const uint8_t * priv) @@ -62,10 +62,10 @@ namespace crypto if (m_PrivateKey) BN_free (m_PrivateKey); } - bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) + bool ECIESP256Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { if (m_Curve && m_PrivateKey) - return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data, ctx); + return ECIESDecrypt (m_Curve, m_PrivateKey, encrypted, data, ctx, zeroPadding); return false; } @@ -104,10 +104,10 @@ namespace crypto if (m_PublicKey) EC_POINT_free (m_PublicKey); } - void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) + void ECIESGOSTR3410Encryptor::Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) { if (m_PublicKey) - ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted, ctx); + ECIESEncrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PublicKey, data, encrypted, ctx, zeroPadding); } ECIESGOSTR3410Decryptor::ECIESGOSTR3410Decryptor (const uint8_t * priv) @@ -120,10 +120,10 @@ namespace crypto if (m_PrivateKey) BN_free (m_PrivateKey); } - bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) + bool ECIESGOSTR3410Decryptor::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) { if (m_PrivateKey) - return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data, ctx); + return ECIESDecrypt (GetGOSTR3410Curve (eGOSTR3410CryptoProA)->GetGroup (), m_PrivateKey, encrypted, data, ctx, zeroPadding); return false; } diff --git a/libi2pd/CryptoKey.h b/libi2pd/CryptoKey.h index ece86eb0..0dff7584 100644 --- a/libi2pd/CryptoKey.h +++ b/libi2pd/CryptoKey.h @@ -13,7 +13,7 @@ namespace crypto public: virtual ~CryptoKeyEncryptor () {}; - virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx) = 0; // 222 bytes data, 512 bytes encrypted + virtual void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding) = 0; // 222 bytes data, 512/514 bytes encrypted }; class CryptoKeyDecryptor @@ -21,7 +21,7 @@ namespace crypto public: virtual ~CryptoKeyDecryptor () {}; - virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) = 0; // 512 bytes encrypted, 222 bytes data + virtual bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding) = 0; // 512/514 bytes encrypted, 222 bytes data }; // ElGamal @@ -30,7 +30,7 @@ namespace crypto public: ElGamalEncryptor (const uint8_t * pub); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx); + void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); private: @@ -42,7 +42,7 @@ namespace crypto public: ElGamalDecryptor (const uint8_t * priv); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); private: @@ -57,7 +57,7 @@ namespace crypto ECIESP256Encryptor (const uint8_t * pub); ~ECIESP256Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx); + void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); private: @@ -72,7 +72,7 @@ namespace crypto ECIESP256Decryptor (const uint8_t * priv); ~ECIESP256Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); private: @@ -90,7 +90,7 @@ namespace crypto ECIESGOSTR3410Encryptor (const uint8_t * pub); ~ECIESGOSTR3410Encryptor (); - void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx); + void Encrypt (const uint8_t * data, uint8_t * encrypted, BN_CTX * ctx, bool zeroPadding); private: @@ -104,7 +104,7 @@ namespace crypto ECIESGOSTR3410Decryptor (const uint8_t * priv); ~ECIESGOSTR3410Decryptor (); - bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx); + bool Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx, bool zeroPadding); private: diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 33eff029..b7c2ee32 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -169,6 +169,46 @@ namespace client return false; } + bool LeaseSetDestination::Reconfigure(std::map params) + { + + auto itr = params.find("i2cp.dontPublishLeaseSet"); + if (itr != params.end()) + { + m_IsPublic = itr->second != "true"; + } + + int inLen, outLen, inQuant, outQuant, numTags, minLatency, maxLatency; + std::map intOpts = { + {I2CP_PARAM_INBOUND_TUNNEL_LENGTH, inLen}, + {I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, outLen}, + {I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, inQuant}, + {I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, outQuant}, + {I2CP_PARAM_TAGS_TO_SEND, numTags}, + {I2CP_PARAM_MIN_TUNNEL_LATENCY, minLatency}, + {I2CP_PARAM_MAX_TUNNEL_LATENCY, maxLatency} + }; + + auto pool = GetTunnelPool(); + inLen = pool->GetNumInboundHops(); + outLen = pool->GetNumOutboundHops(); + inQuant = pool->GetNumInboundTunnels(); + outQuant = pool->GetNumOutboundTunnels(); + minLatency = 0; + maxLatency = 0; + + for (auto & opt : intOpts) + { + itr = params.find(opt.first); + if(itr != params.end()) + { + opt.second = std::stoi(itr->second); + } + } + pool->RequireLatency(minLatency, maxLatency); + return pool->Reconfigure(inLen, outLen, inQuant, outQuant); + } + std::shared_ptr LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident) { std::shared_ptr remoteLS; @@ -241,8 +281,12 @@ namespace client i2p::garlic::GarlicDestination::SetLeaseSetUpdated (); if (m_IsPublic) { - m_PublishVerificationTimer.cancel (); - Publish (); + auto s = shared_from_this (); + m_Service.post ([s](void) + { + s->m_PublishVerificationTimer.cancel (); + s->Publish (); + }); } } @@ -984,7 +1028,7 @@ namespace client bool ClientDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const { if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data, ctx); + return m_Decryptor->Decrypt (encrypted, data, ctx, true); else LogPrint (eLogError, "Destinations: decryptor is not set"); return false; diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 6f37e768..3f261bc9 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -96,6 +96,10 @@ namespace client virtual bool Start (); virtual bool Stop (); + + /** i2cp reconfigure */ + virtual bool Reconfigure(std::map i2cpOpts); + bool IsRunning () const { return m_IsRunning; }; boost::asio::io_service& GetService () { return m_Service; }; std::shared_ptr GetTunnelPool () { return m_Pool; }; diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 9bb7dfd1..c91bfdb3 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -327,7 +327,7 @@ namespace i2p { LogPrint (eLogDebug, "I2NP: Build request record ", i, " is ours"); BN_CTX * ctx = BN_CTX_new (); - i2p::crypto::ElGamalDecrypt (i2p::context.GetPrivateKeys ().GetPrivateKey () , record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx); + i2p::context.DecryptTunnelBuildRecord (record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText, ctx); BN_CTX_free (ctx); // replace record to reply if (i2p::context.AcceptsTunnels () && diff --git a/libi2pd/LeaseSet.cpp b/libi2pd/LeaseSet.cpp index f2355930..8b6063fc 100644 --- a/libi2pd/LeaseSet.cpp +++ b/libi2pd/LeaseSet.cpp @@ -212,7 +212,7 @@ namespace data { auto encryptor = m_Identity->CreateEncryptor (m_EncryptionKey); if (encryptor) - encryptor->Encrypt (data, encrypted, ctx); + encryptor->Encrypt (data, encrypted, ctx, true); } LocalLeaseSet::LocalLeaseSet (std::shared_ptr identity, const uint8_t * encryptionPublicKey, std::vector > tunnels): diff --git a/libi2pd/NTCPSession.cpp b/libi2pd/NTCPSession.cpp index de4306e6..a297ad6a 100644 --- a/libi2pd/NTCPSession.cpp +++ b/libi2pd/NTCPSession.cpp @@ -184,7 +184,7 @@ namespace transport } // TODO: check for number of pending keys auto work = new NTCPWork{shared_from_this()}; - m_Server.Work(work->session, [work]() -> std::function { + m_Server.Work(work->session, [work, this]() -> std::function { if (!work->session->m_DHKeysPair) work->session->m_DHKeysPair = transports.GetNextDHKeysPair (); work->session->CreateAESKey (work->session->m_Establisher->phase1.pubKey); @@ -250,7 +250,7 @@ namespace transport else { auto work = new NTCPWork{shared_from_this()}; - m_Server.Work(work->session, [work]() -> std::function { + m_Server.Work(work->session, [work, this]() -> std::function { work->session->CreateAESKey (work->session->m_Establisher->phase2.pubKey); return std::bind(&NTCPSession::HandlePhase2, work->session, work); }); diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index 38fe3224..a82ace85 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -34,11 +34,7 @@ namespace i2p void RouterContext::CreateNewRouter () { -#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER) m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); -#else - m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1); -#endif SaveKeys (); NewRouterInfo (); } @@ -482,6 +478,11 @@ namespace i2p bool RouterContext::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const { - return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx) : false; + return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, true) : false; + } + + bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const + { + return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, false) : false; } } diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index ef23af25..4bd324f5 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -61,6 +61,7 @@ namespace i2p void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; }; int GetNetID () const { return m_NetID; }; void SetNetID (int netID) { m_NetID = netID; }; + bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const; void UpdatePort (int port); // called from Daemon void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index e9c2a384..e3f4d2d4 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -840,7 +840,7 @@ namespace data { auto encryptor = m_RouterIdentity->CreateEncryptor (nullptr); if (encryptor) - encryptor->Encrypt (data, encrypted, ctx); + encryptor->Encrypt (data, encrypted, ctx, true); } } } diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index e2c12b83..c7e1b1b4 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -670,10 +670,13 @@ namespace tunnel { if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) { - tunnel->SetIsRecreated (); auto pool = tunnel->GetTunnelPool (); - if (pool) + // let it die if the tunnel pool has been reconfigured and this is old + if (pool && tunnel->GetNumHops() == pool->GetNumOutboundHops()) + { + tunnel->SetIsRecreated (); pool->RecreateOutboundTunnel (tunnel); + } } if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) tunnel->SetState (eTunnelStateExpiring); @@ -721,10 +724,13 @@ namespace tunnel { if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) { - tunnel->SetIsRecreated (); auto pool = tunnel->GetTunnelPool (); - if (pool) + // let it die if the tunnel pool was reconfigured and has different number of hops + if (pool && tunnel->GetNumHops() == pool->GetNumInboundHops()) + { + tunnel->SetIsRecreated (); pool->RecreateInboundTunnel (tunnel); + } } if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 38beccaa..3244faad 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -105,6 +105,7 @@ namespace tunnel bool IsFailed () const { return m_State == eTunnelStateFailed; }; bool IsRecreated () const { return m_IsRecreated; }; void SetIsRecreated () { m_IsRecreated = true; }; + int GetNumHops () const { return m_Hops.size (); }; virtual bool IsInbound() const = 0; std::shared_ptr GetTunnelPool () const { return m_Pool; }; diff --git a/libi2pd/TunnelConfig.h b/libi2pd/TunnelConfig.h index 7267fc30..48e66f2e 100644 --- a/libi2pd/TunnelConfig.h +++ b/libi2pd/TunnelConfig.h @@ -5,7 +5,6 @@ #include #include #include -#include "Crypto.h" #include "Identity.h" #include "RouterContext.h" #include "Timestamp.h" @@ -35,6 +34,7 @@ namespace tunnel RAND_bytes (replyKey, 32); RAND_bytes (replyIV, 16); RAND_bytes ((uint8_t *)&tunnelID, 4); + if (!tunnelID) tunnelID = 1; // tunnelID can't be zero isGateway = true; isEndpoint = true; ident = r; @@ -50,6 +50,7 @@ namespace tunnel nextIdent = ident; isEndpoint = false; RAND_bytes ((uint8_t *)&nextTunnelID, 4); + if (!nextTunnelID) nextTunnelID = 1; // tunnelID can't be zero } void SetReplyHop (uint32_t replyTunnelID, const i2p::data::IdentHash& replyIdent) @@ -101,7 +102,9 @@ namespace tunnel htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET); - i2p::crypto::ElGamalEncrypt (ident->GetEncryptionPublicKey (), clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx); + auto encryptor = ident->CreateEncryptor (nullptr); + if (encryptor) + encryptor->Encrypt (clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, ctx, false); memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16); } }; diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index 52736fa0..4f740a09 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -69,6 +69,18 @@ namespace tunnel m_Tests.clear (); } + bool TunnelPool::Reconfigure(int inHops, int outHops, int inQuant, int outQuant) { + if( inHops >= 0 && outHops >= 0 && inQuant > 0 && outQuant > 0) + { + m_NumInboundHops = inHops; + m_NumOutboundHops = outHops; + m_NumInboundTunnels = inQuant; + m_NumOutboundTunnels = outQuant; + return true; + } + return false; + } + void TunnelPool::TunnelCreated (std::shared_ptr createdTunnel) { if (!m_IsActive) return; @@ -479,11 +491,17 @@ namespace tunnel outboundTunnel = tunnels.GetNextOutboundTunnel (); LogPrint (eLogDebug, "Tunnels: Re-creating destination inbound tunnel..."); std::shared_ptr config; - if (m_NumInboundHops > 0) config = std::make_shared(tunnel->GetPeers ()); - auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); - newTunnel->SetTunnelPool (shared_from_this()); - if (newTunnel->IsEstablished ()) // zero hops - TunnelCreated (newTunnel); + if (m_NumInboundHops > 0 && tunnel->GetPeers().size()) + { + config = std::make_shared(tunnel->GetPeers ()); + } + if (m_NumInboundHops == 0 || config) + { + auto newTunnel = tunnels.CreateInboundTunnel (config, outboundTunnel); + newTunnel->SetTunnelPool (shared_from_this()); + if (newTunnel->IsEstablished ()) // zero hops + TunnelCreated (newTunnel); + } } void TunnelPool::CreateOutboundTunnel () @@ -521,12 +539,17 @@ namespace tunnel { LogPrint (eLogDebug, "Tunnels: Re-creating destination outbound tunnel..."); std::shared_ptr config; - if (m_NumOutboundHops > 0) + if (m_NumOutboundHops > 0 && tunnel->GetPeers().size()) + { config = std::make_shared(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); - auto newTunnel = tunnels.CreateOutboundTunnel (config); - newTunnel->SetTunnelPool (shared_from_this ()); - if (newTunnel->IsEstablished ()) // zero hops - TunnelCreated (newTunnel); + } + if(m_NumOutboundHops == 0 || config) + { + auto newTunnel = tunnels.CreateOutboundTunnel (config); + newTunnel->SetTunnelPool (shared_from_this ()); + if (newTunnel->IsEstablished ()) // zero hops + TunnelCreated (newTunnel); + } } else LogPrint (eLogDebug, "Tunnels: Can't re-create outbound tunnel, no inbound tunnels found"); diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 07c3024e..fc46930c 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -78,7 +78,12 @@ namespace tunnel int GetNumInboundTunnels () const { return m_NumInboundTunnels; }; int GetNumOutboundTunnels () const { return m_NumOutboundTunnels; }; + int GetNumInboundHops() const { return m_NumInboundHops; }; + int GetNumOutboundHops() const { return m_NumOutboundHops; }; + /** i2cp reconfigure */ + bool Reconfigure(int inboundHops, int outboundHops, int inboundQuant, int outboundQuant); + void SetCustomPeerSelector(ITunnelPeerSelector * selector); void UnsetCustomPeerSelector(); bool HasCustomPeerSelector(); diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index 371456ee..b08fded1 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -37,7 +37,7 @@ namespace client bool I2CPDestination::Decrypt (const uint8_t * encrypted, uint8_t * data, BN_CTX * ctx) const { if (m_Decryptor) - return m_Decryptor->Decrypt (encrypted, data, ctx); + return m_Decryptor->Decrypt (encrypted, data, ctx, true); else LogPrint (eLogError, "I2CP: decryptor is not set"); return false; @@ -416,9 +416,60 @@ namespace client void I2CPSession::ReconfigureSessionMessageHandler (const uint8_t * buf, size_t len) { - // TODO: implement actual reconfiguration - SendSessionStatusMessage (2); // updated - } + uint8_t status = 3; // rejected + if(len > sizeof(uint16_t)) + { + uint16_t sessionID = bufbe16toh(buf); + if(sessionID == m_SessionID) + { + buf += sizeof(uint16_t); + const uint8_t * body = buf; + i2p::data::IdentityEx ident; + if(ident.FromBuffer(buf, len - sizeof(uint16_t))) + { + if (ident == *m_Destination->GetIdentity()) + { + size_t identsz = ident.GetFullLen(); + buf += identsz; + uint16_t optssize = bufbe16toh(buf); + if (optssize <= len - sizeof(uint16_t) - sizeof(uint64_t) - identsz - ident.GetSignatureLen() - sizeof(uint16_t)) + { + buf += sizeof(uint16_t); + std::map opts; + ExtractMapping(buf, optssize, opts); + buf += optssize; + //uint64_t date = bufbe64toh(buf); + buf += sizeof(uint64_t); + const uint8_t * sig = buf; + if(ident.Verify(body, len - sizeof(uint16_t) - ident.GetSignatureLen(), sig)) + { + if(m_Destination->Reconfigure(opts)) + { + LogPrint(eLogInfo, "I2CP: reconfigured destination"); + status = 2; // updated + } + else + LogPrint(eLogWarning, "I2CP: failed to reconfigure destination"); + } + else + LogPrint(eLogError, "I2CP: invalid reconfigure message signature"); + } + else + LogPrint(eLogError, "I2CP: mapping size missmatch"); + } + else + LogPrint(eLogError, "I2CP: destination missmatch"); + } + else + LogPrint(eLogError, "I2CP: malfromed destination"); + } + else + LogPrint(eLogError, "I2CP: session missmatch"); + } + else + LogPrint(eLogError, "I2CP: short message"); + SendSessionStatusMessage (status); + } void I2CPSession::SendSessionStatusMessage (uint8_t status) { diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 1f4d04db..05943981 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -107,6 +107,21 @@ namespace client std::placeholders::_1, std::placeholders::_2)); } + static bool SAMVersionAcceptable(const std::string & ver) + { + return ver == "3.0" || ver == "3.1"; + } + + static bool SAMVersionTooLow(const std::string & ver) + { + return ver.size() && ver[0] < '3'; + } + + static bool SAMVersionTooHigh(const std::string & ver) + { + return ver.size() && ver > "3.1"; + } + void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) @@ -132,19 +147,37 @@ namespace client if (!strcmp (m_Buffer, SAM_HANDSHAKE)) { - std::string version("3.0"); + std::string maxver("3.1"); + std::string minver("3.0"); // try to find MIN and MAX, 3.0 if not found if (separator) { separator++; std::map params; ExtractParams (separator, params); - //auto it = params.find (SAM_PARAM_MAX); - // TODO: check MIN as well - //if (it != params.end ()) - // version = it->second; + auto it = params.find (SAM_PARAM_MAX); + if (it != params.end ()) + maxver = it->second; + it = params.find(SAM_PARAM_MIN); + if (it != params.end ()) + minver = it->second; } - if (version[0] == '3') // we support v3 (3.0 and 3.1) only + // version negotiation + std::string version; + if (SAMVersionAcceptable(maxver)) + { + version = maxver; + } + else if (SAMVersionAcceptable(minver)) + { + version = minver; + } + else if (SAMVersionTooLow(minver) && SAMVersionTooHigh(maxver)) + { + version = "3.0"; + } + + if (SAMVersionAcceptable(version)) { #ifdef _MSC_VER size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); @@ -156,7 +189,7 @@ namespace client std::placeholders::_1, std::placeholders::_2)); } else - SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true); + SendMessageReply (SAM_HANDSHAKE_NOVERSION, strlen (SAM_HANDSHAKE_NOVERSION), true); } else { diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 5f0ee69b..6ecd14a4 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -23,6 +23,7 @@ namespace client const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; + const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\n"; const char SAM_SESSION_CREATE[] = "SESSION CREATE"; const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n";