From 34939f9381a24d8dae855bf25ee517b495627a2a Mon Sep 17 00:00:00 2001 From: orignal Date: Thu, 19 Jan 2017 22:00:02 -0500 Subject: [PATCH 01/30] calculate shared key in separate therad --- NTCPSession.cpp | 65 +++++++++++++++++++++++++++++++------------------ NTCPSession.h | 1 + 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 8f31e246..d50ccfe5 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "I2PEndian.h" #include "Base.h" @@ -38,7 +39,7 @@ namespace transport void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key) { uint8_t sharedKey[256]; - m_DHKeysPair->Agree (pubKey, sharedKey); + m_DHKeysPair->Agree (pubKey, sharedKey); // time consuming operation uint8_t * aesKey = key; if (sharedKey[0] & 0x80) @@ -229,32 +230,48 @@ namespace transport } else { - i2p::crypto::AESKey aesKey; - CreateAESKey (m_Establisher->phase2.pubKey, aesKey); - m_Decryption.SetKey (aesKey); - m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); - m_Encryption.SetKey (aesKey); - m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); - - m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); - // verify - uint8_t xy[512]; - memcpy (xy, m_DHKeysPair->GetPublicKey (), 256); - memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); - uint8_t digest[32]; - SHA256 (xy, 512, digest); - if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32)) - { - LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash"); - transports.ReuseDHKeysPair (m_DHKeysPair); - m_DHKeysPair = nullptr; - Terminate (); - return ; - } - SendPhase3 (); + auto s = shared_from_this (); + // create AES key in separate thread + auto createKey = std::async (std::launch::async, [s] ()->i2p::crypto::AESKey + { + i2p::crypto::AESKey aesKey; + s->CreateAESKey (s->m_Establisher->phase2.pubKey, aesKey); + return std::move (aesKey); + }).share (); // TODO: use move capture in C++ 14 instead shared_future + // let other operations execute while a key gets created + m_Server.GetService ().post ([s, createKey]() + { + auto aesKey = createKey.get (); // we might wait if no more pending operations + s->HandlePhase2 (aesKey); + }); } } + void NTCPSession::HandlePhase2 (const i2p::crypto::AESKey& aesKey) + { + m_Decryption.SetKey (aesKey); + m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); + m_Encryption.SetKey (aesKey); + m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); + + m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); + // verify + uint8_t xy[512]; + memcpy (xy, m_DHKeysPair->GetPublicKey (), 256); + memcpy (xy + 256, m_Establisher->phase2.pubKey, 256); + uint8_t digest[32]; + SHA256 (xy, 512, digest); + if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32)) + { + LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash"); + transports.ReuseDHKeysPair (m_DHKeysPair); + m_DHKeysPair = nullptr; + Terminate (); + return ; + } + SendPhase3 (); + } + void NTCPSession::SendPhase3 () { auto& keys = i2p::context.GetPrivateKeys (); diff --git a/NTCPSession.h b/NTCPSession.h index a5d6f99f..60a7b6c1 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -73,6 +73,7 @@ namespace transport void SendPhase3 (); void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void HandlePhase2 (const i2p::crypto::AESKey& aesKey); void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); From 65ccc5bfcec213b16fa048714d69bfc8b9e3b516 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 20 Jan 2017 10:02:16 -0500 Subject: [PATCH 02/30] send actual local address to webirc --- I2PTunnel.cpp | 2 +- I2PTunnel.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index 843d129f..6a9b3ffd 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -302,7 +302,7 @@ namespace client if (m_NeedsWebIrc) { m_NeedsWebIrc = false; - m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " 127.0.0.1\n"; + m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl; } m_InPacket.clear (); diff --git a/I2PTunnel.h b/I2PTunnel.h index 4b9b2c9b..68f46e43 100644 --- a/I2PTunnel.h +++ b/I2PTunnel.h @@ -53,6 +53,8 @@ namespace client void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleConnect (const boost::system::error_code& ecode); + std::shared_ptr GetSocket () const { return m_Socket; }; + private: uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE]; From a746f5657fcf418d37e7efaa4fc15ca853ad0598 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 21 Jan 2017 18:59:50 -0500 Subject: [PATCH 03/30] calculate shared key in separate thread for incoming connection --- NTCPSession.cpp | 45 +++++++++++++++++++++++++-------------------- NTCPSession.h | 4 ++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/NTCPSession.cpp b/NTCPSession.cpp index d50ccfe5..883f0a11 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -36,12 +36,12 @@ namespace transport delete m_Establisher; } - void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key) + void NTCPSession::CreateAESKey (uint8_t * pubKey) { uint8_t sharedKey[256]; m_DHKeysPair->Agree (pubKey, sharedKey); // time consuming operation - uint8_t * aesKey = key; + i2p::crypto::AESKey aesKey; if (sharedKey[0] & 0x80) { aesKey[0] = 0; @@ -64,6 +64,9 @@ namespace transport } memcpy (aesKey, nonZero, 32); } + + m_Decryption.SetKey (aesKey); + m_Encryption.SetKey (aesKey); } void NTCPSession::Done () @@ -164,15 +167,25 @@ namespace transport return; } } - - SendPhase2 (); + + // TODO: check for number of pending keys + auto s = shared_from_this (); + auto keyCreated = std::async (std::launch::async, [s] () + { + if (!s->m_DHKeysPair) + s->m_DHKeysPair = transports.GetNextDHKeysPair (); + s->CreateAESKey (s->m_Establisher->phase1.pubKey); + }).share (); + m_Server.GetService ().post ([s, keyCreated]() + { + keyCreated.get (); + s->SendPhase2 (); + }); } } void NTCPSession::SendPhase2 () { - if (!m_DHKeysPair) - m_DHKeysPair = transports.GetNextDHKeysPair (); const uint8_t * y = m_DHKeysPair->GetPublicKey (); memcpy (m_Establisher->phase2.pubKey, y, 256); uint8_t xy[512]; @@ -183,11 +196,7 @@ namespace transport memcpy (m_Establisher->phase2.encrypted.timestamp, &tsB, 4); RAND_bytes (m_Establisher->phase2.encrypted.filler, 12); - i2p::crypto::AESKey aesKey; - CreateAESKey (m_Establisher->phase1.pubKey, aesKey); - m_Encryption.SetKey (aesKey); m_Encryption.SetIV (y + 240); - m_Decryption.SetKey (aesKey); m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16); m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); @@ -232,26 +241,22 @@ namespace transport { auto s = shared_from_this (); // create AES key in separate thread - auto createKey = std::async (std::launch::async, [s] ()->i2p::crypto::AESKey + auto keyCreated = std::async (std::launch::async, [s] () { - i2p::crypto::AESKey aesKey; - s->CreateAESKey (s->m_Establisher->phase2.pubKey, aesKey); - return std::move (aesKey); + s->CreateAESKey (s->m_Establisher->phase2.pubKey); }).share (); // TODO: use move capture in C++ 14 instead shared_future // let other operations execute while a key gets created - m_Server.GetService ().post ([s, createKey]() + m_Server.GetService ().post ([s, keyCreated]() { - auto aesKey = createKey.get (); // we might wait if no more pending operations - s->HandlePhase2 (aesKey); + keyCreated.get (); // we might wait if no more pending operations + s->HandlePhase2 (); }); } } - void NTCPSession::HandlePhase2 (const i2p::crypto::AESKey& aesKey) + void NTCPSession::HandlePhase2 () { - m_Decryption.SetKey (aesKey); m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240); - m_Encryption.SetKey (aesKey); m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16); m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted); diff --git a/NTCPSession.h b/NTCPSession.h index 60a7b6c1..d9acd5ce 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -67,13 +67,13 @@ namespace transport void SendTimeSyncMessage (); void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; } - void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key); + void CreateAESKey (uint8_t * pubKey); // client void SendPhase3 (); void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void HandlePhase2 (const i2p::crypto::AESKey& aesKey); + void HandlePhase2 (); void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA); From 162b60a05bea84a10d51e054df3a03a527b851f4 Mon Sep 17 00:00:00 2001 From: r4sas Date: Sun, 22 Jan 2017 10:53:53 +0300 Subject: [PATCH 04/30] Added script - builder for mingw. --- contrib/build_mingw.sh | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 contrib/build_mingw.sh diff --git a/contrib/build_mingw.sh b/contrib/build_mingw.sh new file mode 100644 index 00000000..564c8a79 --- /dev/null +++ b/contrib/build_mingw.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +# Определяем архитектуру. +if [ $MSYSTEM == MINGW64 ]; then + export arch="win64" +elif [ $MSYSTEM == MINGW32 ]; then + export arch="win32" +else + echo "Не могу понять, какая у вас архитектура, используемая для сборки."; + echo "Вы точно запустили скрипт в оболочке MSYS2 MinGW [64/32]-bit ?"; + echo "Обычно его можно запустить выполнив c:\msys64\mingw64.exe или c:\msys64\mingw32.exe"; + exit 1; +fi; + +# Задаём переменной contrib текущий путь и переходим на уровень выше. +export contrib=$PWD +cd .. + +# Очистка от предыдущей сборки (на всякий случай =) ). +make clean >> /dev/null + +# Обновляем репозиторий, и получаем хеш последнего коммита. +echo "Получаем обновления из репозитория."; +git pull +if [ "$?" != 0 ]; then + echo "Не удалось обновить локальный репозиторий."; + echo "Вы точно запустили скрипт в папке репозитория?"; + exit 1; +fi; + +export commit=$(git rev-parse --verify HEAD | cut -c -7) +if [ -z commit ]; then + echo "Не удалось получить хеш последнего коммита."; + echo "Вы точно запустили скрипт в папке репозитория?"; + exit 1; +fi; + +# Получаем версию приложения +export version=$(grep -E "I2PD_VERSION_(MAJOR|MINOR|MICRO)\ " version.h | grep -oE '[^ ]+$' | tr '\n' '.'|head -c -1) + +echo "Собираем i2pd ${version} (коммит ${commit}) для ${arch}."; + +# Собираем приложение с разными параметрами, и архивируем в zip архивы. +make USE_UPNP=yes USE_AVX=1 USE_AESNI=1 -j $NUMBER_OF_PROCESSORS > ${contrib}/build_avx_aesni.log 2>&1 +if [ "$?" != 0 ]; then + echo "Сборка не удалась. Смотрите в build_avx_aesni.log"; + exit 1; +fi; +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_avx_aesni.zip i2pd.exe +make clean >> /dev/null + +make USE_UPNP=yes USE_AVX=1 -j $NUMBER_OF_PROCESSORS > ${contrib}/build_avx.log 2>&1 +if [ "$?" != 0 ]; then + echo "Сборка не удалась. Смотрите в build_avx.log."; + exit 1; +fi; +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_avx.zip i2pd.exe +make clean >> /dev/null + +make USE_UPNP=yes USE_AESNI=1 -j $NUMBER_OF_PROCESSORS > ${contrib}/build_aesni.log 2>&1 +if [ "$?" != 0 ]; then + echo "Сборка не удалась. Смотрите в build_aesni.log"; + exit 1; +fi; +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_aesni.zip i2pd.exe +make clean >> /dev/null + +make USE_UPNP=yes -j $NUMBER_OF_PROCESSORS > ${contrib}/build.log 2>&1 +if [ "$?" != 0 ]; then + echo "Сборка не удалась. Смотрите в build.log"; + exit 1; +fi; +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw.zip i2pd.exe +make clean >> /dev/null + +echo "Сборка i2pd ${version} для ${arch} завершена."; +exit 0; From 29944f6bf29106373e7b701bb7cd7119077f16ca Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 22 Jan 2017 21:22:12 -0500 Subject: [PATCH 05/30] cleanup stream upon termination --- Streaming.cpp | 36 +++++++++++++++++++++--------------- Streaming.h | 2 ++ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Streaming.cpp b/Streaming.cpp index 343b9b38..86bfcd14 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -36,21 +36,7 @@ namespace stream Stream::~Stream () { - while (!m_ReceiveQueue.empty ()) - { - auto packet = m_ReceiveQueue.front (); - m_ReceiveQueue.pop (); - m_LocalDestination.DeletePacket (packet); - } - - for (auto it: m_SentPackets) - m_LocalDestination.DeletePacket (it); - m_SentPackets.clear (); - - for (auto it: m_SavedPackets) - m_LocalDestination.DeletePacket (it); - m_SavedPackets.clear (); - + CleanUp (); LogPrint (eLogDebug, "Streaming: Stream deleted"); } @@ -65,8 +51,28 @@ namespace stream m_SendHandler = nullptr; handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); } + CleanUp (); m_LocalDestination.DeleteStream (shared_from_this ()); } + + void Stream::CleanUp () + { + m_SendBuffer.str (""); + while (!m_ReceiveQueue.empty ()) + { + auto packet = m_ReceiveQueue.front (); + m_ReceiveQueue.pop (); + m_LocalDestination.DeletePacket (packet); + } + + for (auto it: m_SentPackets) + m_LocalDestination.DeletePacket (it); + m_SentPackets.clear (); + + for (auto it: m_SavedPackets) + m_LocalDestination.DeletePacket (it); + m_SavedPackets.clear (); + } void Stream::HandleNextPacket (Packet * packet) { diff --git a/Streaming.h b/Streaming.h index 828fccc7..bfea74ed 100644 --- a/Streaming.h +++ b/Streaming.h @@ -158,6 +158,8 @@ namespace stream private: + void CleanUp (); + void SendBuffer (); void SendQuickAck (); void SendClose (); From 8e4bd7fe4a59e2ccaafbea16ed3069a6cabaa27d Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 23 Jan 2017 13:14:08 -0500 Subject: [PATCH 06/30] build with openssl 1.1 --- Makefile.osx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.osx b/Makefile.osx index c8a7de2a..786ab2f1 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -1,11 +1,11 @@ CXX = clang++ -CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX +CXXFLAGS = -Os -Wall -std=c++11 -DMAC_OSX #CXXFLAGS = -g -O2 -Wall -std=c++11 -INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include -LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib +INCFLAGS = -I/usr/local/include +LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib ifeq ($(USE_STATIC),yes) -LDLIBS = -lz -lcrypto -lssl /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread +LDLIBS = -lz -l/usr/local/lib/libcrypto.a -l/usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread else LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread endif From bcd6bd6b048c8ed11484b91c551bb1254cc8fc3d Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 23 Jan 2017 13:22:03 -0500 Subject: [PATCH 07/30] correct handle of AESNI/AVX --- Makefile.osx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile.osx b/Makefile.osx index 786ab2f1..f9372c8f 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -5,7 +5,7 @@ INCFLAGS = -I/usr/local/include LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib ifeq ($(USE_STATIC),yes) -LDLIBS = -lz -l/usr/local/lib/libcrypto.a -l/usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread +LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread else LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread endif @@ -15,11 +15,13 @@ ifeq ($(USE_UPNP),yes) CXXFLAGS += -DUSE_UPNP endif -ifeq ($(USE_AESNI),yes) +ifeq ($(USE_AESNI),1) CXXFLAGS += -maes -DAESNI +else + CXXFLAGS += -msse endif -ifeq ($(USE_AVX),yes) +ifeq ($(USE_AVX),1) CXXFLAGS += -mavx endif From 14d74d32305a26cd2df9e7dd15865cbc773e0005 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 23 Jan 2017 15:36:45 -0500 Subject: [PATCH 08/30] use openssl 1.1 for androidn build --- android/jni/Android.mk | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 5eaa983e..5b77c66e 100755 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -4,10 +4,10 @@ LOCAL_MODULE := i2pd LOCAL_CPP_FEATURES := rtti exceptions LOCAL_C_INCLUDES += $(IFADDRS_PATH) ../.. LOCAL_STATIC_LIBRARIES := \ - boost_system-gcc-mt-1_53 \ - boost_date_time-gcc-mt-1_53 \ - boost_filesystem-gcc-mt-1_53 \ - boost_program_options-gcc-mt-1_53 \ + boost_system \ + boost_date_time \ + boost_filesystem \ + boost_program_options \ crypto ssl \ miniupnpc LOCAL_LDLIBS := -lz @@ -68,44 +68,44 @@ include $(BUILD_SHARED_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := boost_system-gcc-mt-1_53 -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_system-gcc-mt-1_53.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +LOCAL_MODULE := boost_system +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_62_0/$(TARGET_ARCH_ABI)/lib/libboost_system.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := boost_date_time-gcc-mt-1_53 -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time-gcc-mt-1_53.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +LOCAL_MODULE := boost_date_time +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_62_0/$(TARGET_ARCH_ABI)/lib/libboost_date_time.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := boost_filesystem-gcc-mt-1_53 -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem-gcc-mt-1_53.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +LOCAL_MODULE := boost_filesystem +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_62_0/$(TARGET_ARCH_ABI)/lib/libboost_filesystem.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := boost_program_options-gcc-mt-1_53 -LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_53_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options-gcc-mt-1_53.a -LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_53_0/include +LOCAL_MODULE := boost_program_options +LOCAL_SRC_FILES := $(BOOST_PATH)/boost_1_62_0/$(TARGET_ARCH_ABI)/lib/libboost_program_options.a +LOCAL_EXPORT_C_INCLUDES := $(BOOST_PATH)/boost_1_62_0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := crypto -LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.0.2/$(TARGET_ARCH_ABI)/lib/libcrypto.a -LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.0.2/include +LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.0/$(TARGET_ARCH_ABI)/lib/libcrypto.a +LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.0/include include $(PREBUILT_STATIC_LIBRARY) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ssl -LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.0.2/$(TARGET_ARCH_ABI)/lib/libssl.a -LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.0.2/include +LOCAL_SRC_FILES := $(OPENSSL_PATH)/openssl-1.1.0/$(TARGET_ARCH_ABI)/lib/libssl.a +LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_PATH)/openssl-1.1.0/include LOCAL_STATIC_LIBRARIES := crypto include $(PREBUILT_STATIC_LIBRARY) From 188987a8ff13374504a2a1c5df4e280473aa26e0 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 23 Jan 2017 16:22:48 -0500 Subject: [PATCH 09/30] eliminated deprecated function --- Crypto.h | 4 ++++ Family.cpp | 4 ++-- Reseed.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Crypto.h b/Crypto.h index 00255a37..3f99f2b6 100644 --- a/Crypto.h +++ b/Crypto.h @@ -325,6 +325,10 @@ inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **pri inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) { return pkey->pkey.rsa; } + +// ssl +#define TLS_method TLSv1_method + #endif #endif diff --git a/Family.cpp b/Family.cpp index ce995a4a..fb3b19a6 100644 --- a/Family.cpp +++ b/Family.cpp @@ -20,7 +20,7 @@ namespace data void Families::LoadCertificate (const std::string& filename) { - SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ()); + SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) { @@ -135,7 +135,7 @@ namespace data { auto filename = i2p::fs::DataDirPath("family", (family + ".key")); std::string sig; - SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ()); + SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) { diff --git a/Reseed.cpp b/Reseed.cpp index b4eaa76c..52079565 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -362,7 +362,7 @@ namespace data void Reseeder::LoadCertificate (const std::string& filename) { - SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ()); + SSL_CTX * ctx = SSL_CTX_new (TLS_method ()); int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) { From 4d2f26b1cd74f5bc269b03733ee578341099ab49 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 25 Jan 2017 11:20:15 -0500 Subject: [PATCH 10/30] limit number of precalculated DH pairs --- Transports.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Transports.cpp b/Transports.cpp index 1659eeed..5b188a7d 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -101,8 +101,9 @@ namespace transport void DHKeysPairSupplier::Return (std::shared_ptr pair) { - std::unique_lock l(m_AcquiredMutex); - m_Queue.push (pair); + std::unique_lockl(m_AcquiredMutex); + if ((int)m_Queue.size () < 2*m_QueueSize) + m_Queue.push (pair); } Transports transports; From a6785e9143bcb509eb12e435a08b8a970139a713 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 25 Jan 2017 16:14:01 -0500 Subject: [PATCH 11/30] support of 'X' in RouterInfo --- RouterContext.cpp | 7 ++++--- RouterInfo.cpp | 13 ++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/RouterContext.cpp b/RouterContext.cpp index 966da6ac..8bfbdc1d 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -205,7 +205,7 @@ namespace i2p void RouterContext::SetBandwidth (char L) { uint16_t limit = 0; - enum { low, high, extra } type = high; + enum { low, high, extra, unlim } type = high; /* detect parameters */ switch (L) { @@ -215,7 +215,7 @@ namespace i2p case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break; case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break; case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break; - case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = extra; break; + case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = unlim; break; default: limit = 48; type = low; } @@ -226,7 +226,8 @@ namespace i2p switch (type) { case low : /* not set */; break; - case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here + case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P' + case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here, extra + high means 'X' case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break; } m_RouterInfo.SetCaps (caps); diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 08d46ecb..205262e5 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -376,14 +376,21 @@ namespace data std::string caps; if (m_Caps & eFloodfill) { - if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' + if (m_Caps & eExtraBandwidth) caps += (m_Caps & eHighBandwidth) ? + CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X' + CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O' caps += CAPS_FLAG_FLOODFILL; // floodfill } else { - if (m_Caps & eExtraBandwidth) caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P' - caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth + if (m_Caps & eExtraBandwidth) + { + caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */ + caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O' + } + else + caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth } if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable From 7196bfd1570d070df49b71a72f465bcbc212f7cc Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 25 Jan 2017 16:37:21 -0500 Subject: [PATCH 12/30] keep bandwidth caps if unreachable --- RouterContext.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/RouterContext.cpp b/RouterContext.cpp index 8bfbdc1d..b558e050 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -254,7 +254,12 @@ namespace i2p void RouterContext::SetUnreachable () { // set caps - m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B + uint8_t caps = m_RouterInfo.GetCaps (); + caps &= ~i2p::data::RouterInfo::eReachable; + caps |= i2p::data::RouterInfo::eUnreachable; + caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill + caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer + m_RouterInfo.SetCaps (caps); // remove NTCP address auto& addresses = m_RouterInfo.GetAddresses (); for (auto it = addresses.begin (); it != addresses.end (); ++it) From a840ed06b7abd8cec373dac209c6f935136941bb Mon Sep 17 00:00:00 2001 From: r4sas Date: Thu, 26 Jan 2017 11:08:25 +0300 Subject: [PATCH 13/30] update android notification icon update android qt app icon add mingw build batch and .gitignore --- .../drawable/itoopie_notification_icon.png | Bin 37239 -> 1940 bytes contrib/.gitignore | 2 ++ contrib/build_mingw.cmd | 22 +++++++++++++++ contrib/build_mingw.sh | 25 +++++++++++------- contrib/debian/README | 2 +- qt/i2pd_qt/android/res/drawable-hdpi/icon.png | Bin 8712 -> 37177 bytes .../drawable/itoopie_notification_icon.png | Bin 33199 -> 1940 bytes 7 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 contrib/.gitignore create mode 100644 contrib/build_mingw.cmd diff --git a/android/res/drawable/itoopie_notification_icon.png b/android/res/drawable/itoopie_notification_icon.png index d055bc8f897bdceee70c5a3e78ebedd926765a88..fa99e7fc66b68ea9550dd70cd00b36611ecd9fa9 100644 GIT binary patch literal 1940 zcmXX{2{c>j9#2o7mnjvAqb(hkNjjY=MXRAiD;~LOEY(E1JW^EElCi{AQL*$?aR)Ox z$NDTS*Fi>zsSO`oZ$FSRBSLSCeb=3H1Qi->toh7B-@?L6ch@L^MG8=B?qpE#Ywde zSe<`wYVJ-Sm|?sPAz}@me_Kd9D_yzZHINb0;ib0!$MTBzbW-_B$3a0b^?Gqh_Ih*? z=g3?z-|FyGir_pBALfmlktWw-tDX)Jd!3Xg2>vt~uczjcumjCDM2Hk2OthYBl2Zte zifZ?T=jC>KMN-L-o7!$NxVWSvVRG@{Pjznsg#?SaNHQ0;x<{fweFaU2poX?vhk;o7 z(2+SEUiLaGzp<+X2od;3aj>fqqKdx&KWaNs6m;d>%a+}A+|k2X_kqz0uG{KtKGy2+ zwaDQjj13WLkmuzlmwqB*=n0ChcsRCKZ;FUXf ze9!Jc_DQThZpvh?bDz@qf>;M>e7 zJ+bWswJilNo=x~<{5WMXbICL;$vL^Gkx+gmQe}-$l|i%P*s4B}!hQIm6GV;LC0?8| zgG%F~;Gy8KQ%#Ih1MsYgj5;cMxLeFKJ%A*XnE+oo5&Cr8>X3LZf;}HbPHf`DWhqxL zD?axBc;rivu&Zs_T~5N!%GF>+je*o(=r+k~hYPn%%#$LRyoE!jvw$N~+F(7fNXIpS z>hy8w)4b1L4xTF=pY3@OdW2ak@V}VbzCl{MDVcEi4w4@uK>_}G4iI7g6))JL74$&R z(_gjO?#paH3hK{nvm;qj&j~Bq8YRb{NwHP^lOSh2*xos?3*>oSK7*Ish{$bZL|M&g zD`3tC`RA~LXu9GL0=v`Rtxk}flo5Ah>CoxuA>Tln#ayW(L`XP&YbkXxXNhO9EgB&3 zE0YXDw4EX+IYT2+s}jd&QWpQQs1sW?tj1&xFIR-jp5hfUPyW5iny-$QzyIVL0#KR`C~6h}xo8Xj#{ zr#7rDZ?&x0*c;7WxR32AZFL=`QAA}g(H+|0pEr+HEek+*$B!%XcdG~2*WDFyyj2xC z^%d}ntwDGCzz=3c_0;IM5`Uoz|KVRG>c_pmbZTx4SDGLlvBD0`PRpi+_d#3ePEYv3 z!h5_1?a)GIm?Dwnlh8fVHdC{sWGo>a_6b*iMn}cvWULz;`yFsD?~)PHG>aiP7K`mB z)?fMYF|E-X-gLM(`{XT<-wJMVcR_Q1Np?K7i(41*&Wt^8onsxc?4<=~on+nNxpj)Q z6N;AVQJx*(ks_q>$+&2vm0P#;k!!k0(eu<_AO`YyTgFYtkKY+H-wu8A2ObIi9skPKXxcg+^SI=uS4iIy(+t{T8L@LcCbOH^@)me;H<*}JO}wI;8yv0Y3C3PTAzz1Q-S|3^+@_j{ zl>=@3>QNeot{uOB!OXZ-0842K$)k5&cNTZZ_HpsP>ak8Pn28Snfhs7ft0S1dRIKYW z3}TCH2)bhHmA5LAulnL?*&`cF-C&;x@2q-KfoWbyVk+gs8vK-A Vg*^@~1UD$k!_5bJ;ClJy{{UE{z48D6 literal 37239 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 ${contrib}/build_avx_aesni.log 2>&1 +make USE_UPNP=yes USE_AVX=1 USE_AESNI=1 -j ${threads} > ${contrib}/build_avx_aesni.log 2>&1 if [ "$?" != 0 ]; then echo "Сборка не удалась. Смотрите в build_avx_aesni.log"; exit 1; fi; -zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_avx_aesni.zip i2pd.exe +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_avx_aesni.zip i2pd.exe >> /dev/null make clean >> /dev/null -make USE_UPNP=yes USE_AVX=1 -j $NUMBER_OF_PROCESSORS > ${contrib}/build_avx.log 2>&1 +make USE_UPNP=yes USE_AVX=1 -j ${threads} > ${contrib}/build_avx.log 2>&1 if [ "$?" != 0 ]; then echo "Сборка не удалась. Смотрите в build_avx.log."; exit 1; fi; -zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_avx.zip i2pd.exe +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_avx.zip i2pd.exe >> /dev/null make clean >> /dev/null -make USE_UPNP=yes USE_AESNI=1 -j $NUMBER_OF_PROCESSORS > ${contrib}/build_aesni.log 2>&1 +make USE_UPNP=yes USE_AESNI=1 -j ${threads} > ${contrib}/build_aesni.log 2>&1 if [ "$?" != 0 ]; then echo "Сборка не удалась. Смотрите в build_aesni.log"; exit 1; fi; -zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_aesni.zip i2pd.exe +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw_aesni.zip i2pd.exe >> /dev/null make clean >> /dev/null -make USE_UPNP=yes -j $NUMBER_OF_PROCESSORS > ${contrib}/build.log 2>&1 +make USE_UPNP=yes -j ${threads} > ${contrib}/build.log 2>&1 if [ "$?" != 0 ]; then echo "Сборка не удалась. Смотрите в build.log"; exit 1; fi; -zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw.zip i2pd.exe +zip -9 ${contrib}/i2pd_${version}_${commit}_${arch}_mingw.zip i2pd.exe >> /dev/null make clean >> /dev/null echo "Сборка i2pd ${version} для ${arch} завершена."; diff --git a/contrib/debian/README b/contrib/debian/README index 077d7b96..cccbc4de 100644 --- a/contrib/debian/README +++ b/contrib/debian/README @@ -1,2 +1,2 @@ This forder contain systemd unit files. -To use systemd daemon control, place files from this directory to debian folder. +To use systemd daemon control, place files from this directory to debian folder before building package. diff --git a/qt/i2pd_qt/android/res/drawable-hdpi/icon.png b/qt/i2pd_qt/android/res/drawable-hdpi/icon.png index a5dc7b680ba9dee30161ffb1c9fcd17bbd6ddd84..9a2f7404a20eef22adc6da0fb1538b6c507c97f4 100644 GIT binary patch literal 37177 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 RE#UwF002ovPDHLkV1gi%A?yGE literal 8712 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 diff --git a/qt/i2pd_qt/android/res/drawable/itoopie_notification_icon.png b/qt/i2pd_qt/android/res/drawable/itoopie_notification_icon.png index 8fbe24689b0af36100a6afa238829d7ce96a6529..fa99e7fc66b68ea9550dd70cd00b36611ecd9fa9 100644 GIT binary patch literal 1940 zcmXX{2{c>j9#2o7mnjvAqb(hkNjjY=MXRAiD;~LOEY(E1JW^EElCi{AQL*$?aR)Ox z$NDTS*Fi>zsSO`oZ$FSRBSLSCeb=3H1Qi->toh7B-@?L6ch@L^MG8=B?qpE#Ywde zSe<`wYVJ-Sm|?sPAz}@me_Kd9D_yzZHINb0;ib0!$MTBzbW-_B$3a0b^?Gqh_Ih*? z=g3?z-|FyGir_pBALfmlktWw-tDX)Jd!3Xg2>vt~uczjcumjCDM2Hk2OthYBl2Zte zifZ?T=jC>KMN-L-o7!$NxVWSvVRG@{Pjznsg#?SaNHQ0;x<{fweFaU2poX?vhk;o7 z(2+SEUiLaGzp<+X2od;3aj>fqqKdx&KWaNs6m;d>%a+}A+|k2X_kqz0uG{KtKGy2+ zwaDQjj13WLkmuzlmwqB*=n0ChcsRCKZ;FUXf ze9!Jc_DQThZpvh?bDz@qf>;M>e7 zJ+bWswJilNo=x~<{5WMXbICL;$vL^Gkx+gmQe}-$l|i%P*s4B}!hQIm6GV;LC0?8| zgG%F~;Gy8KQ%#Ih1MsYgj5;cMxLeFKJ%A*XnE+oo5&Cr8>X3LZf;}HbPHf`DWhqxL zD?axBc;rivu&Zs_T~5N!%GF>+je*o(=r+k~hYPn%%#$LRyoE!jvw$N~+F(7fNXIpS z>hy8w)4b1L4xTF=pY3@OdW2ak@V}VbzCl{MDVcEi4w4@uK>_}G4iI7g6))JL74$&R z(_gjO?#paH3hK{nvm;qj&j~Bq8YRb{NwHP^lOSh2*xos?3*>oSK7*Ish{$bZL|M&g zD`3tC`RA~LXu9GL0=v`Rtxk}flo5Ah>CoxuA>Tln#ayW(L`XP&YbkXxXNhO9EgB&3 zE0YXDw4EX+IYT2+s}jd&QWpQQs1sW?tj1&xFIR-jp5hfUPyW5iny-$QzyIVL0#KR`C~6h}xo8Xj#{ zr#7rDZ?&x0*c;7WxR32AZFL=`QAA}g(H+|0pEr+HEek+*$B!%XcdG~2*WDFyyj2xC z^%d}ntwDGCzz=3c_0;IM5`Uoz|KVRG>c_pmbZTx4SDGLlvBD0`PRpi+_d#3ePEYv3 z!h5_1?a)GIm?Dwnlh8fVHdC{sWGo>a_6b*iMn}cvWULz;`yFsD?~)PHG>aiP7K`mB z)?fMYF|E-X-gLM(`{XT<-wJMVcR_Q1Np?K7i(41*&Wt^8onsxc?4<=~on+nNxpj)Q z6N;AVQJx*(ks_q>$+&2vm0P#;k!!k0(eu<_AO`YyTgFYtkKY+H-wu8A2ObIi9skPKXxcg+^SI=uS4iIy(+t{T8L@LcCbOH^@)me;H<*}JO}wI;8yv0Y3C3PTAzz1Q-S|3^+@_j{ zl>=@3>QNeot{uOB!OXZ-0842K$)k5&cNTZZ_HpsP>ak8Pn28Snfhs7ft0S1dRIKYW z3}TCH2)bhHmA5LAulnL?*&`cF-C&;x@2q-KfoWbyVk+gs8vK-A Vg*^@~1UD$k!_5bJ;ClJy{{UE{z48D6 literal 33199 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 Date: Thu, 26 Jan 2017 15:59:33 -0500 Subject: [PATCH 14/30] show if router is connected in i2p.router.status --- I2PControl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I2PControl.cpp b/I2PControl.cpp index 523f10e1..6ffe8936 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -391,7 +391,7 @@ namespace client void I2PControlService::StatusHandler (std::ostringstream& results) { - InsertParam (results, "i2p.router.status", "???"); // TODO: + InsertParam (results, "i2p.router.status", i2p::transport::transports.IsOnline () ? "1" : "0"); } void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results) From aa865937026355e7e9e82b8889e40cdf5d559c41 Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 27 Jan 2017 14:51:06 -0500 Subject: [PATCH 15/30] send correct response if JSON parse error --- I2PControl.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/I2PControl.cpp b/I2PControl.cpp index 6ffe8936..23503906 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -187,13 +187,16 @@ namespace client size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf) { - if (ecode) { + if (ecode) + { LogPrint (eLogError, "I2PControl: read error: ", ecode.message ()); return; - } else { + } + else + { + bool isHtml = !memcmp (buf->data (), "POST", 4); try { - bool isHtml = !memcmp (buf->data (), "POST", 4); std::stringstream ss; ss.write (buf->data (), bytes_transferred); if (isHtml) @@ -237,7 +240,9 @@ namespace client response << "{\"id\":" << id << ",\"result\":{"; (this->*(it->second))(pt.get_child ("params"), response); response << "},\"jsonrpc\":\"2.0\"}"; - } else { + } + else + { LogPrint (eLogWarning, "I2PControl: unknown method ", method); response << "{\"id\":null,\"error\":"; response << "{\"code\":-32601,\"message\":\"Method not found\"},"; @@ -249,6 +254,11 @@ namespace client catch (std::exception& ex) { LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ()); + std::ostringstream response; + response << "{\"id\":null,\"error\":"; + response << "{\"code\":-32700,\"message\":\"" << ex.what () << "\"},"; + response << "\"jsonrpc\":\"2.0\"}"; + SendResponse (socket, buf, response, isHtml); } catch (...) { From 4aa48fb4b6819df552e02478796e61795147ee2a Mon Sep 17 00:00:00 2001 From: orignal Date: Fri, 27 Jan 2017 20:41:15 -0500 Subject: [PATCH 16/30] websocks added --- qt/i2pd_qt/i2pd_qt.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index e51eb963..36883399 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -36,7 +36,7 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \ ../../SSUData.cpp ../../SSUSession.cpp ../../Streaming.cpp ../../TransitTunnel.cpp \ ../../Transports.cpp ../../Tunnel.cpp ../../TunnelEndpoint.cpp ../../TunnelGateway.cpp \ ../../TunnelPool.cpp ../../UPnP.cpp ../../Gzip.cpp ../../Timestamp.cpp ../../util.cpp \ - ../../Event.cpp ../../BloomFiler.cpp ../../i2pd.cpp + ../../Event.cpp ../../BloomFilter.cpp ../../WebSocks.cpp ../../i2pd.cpp HEADERS += DaemonQT.h mainwindow.h \ ../../HTTPServer.h ../../I2PControl.h ../../UPnP.h ../../Daemon.h ../../Config.h \ @@ -51,7 +51,7 @@ HEADERS += DaemonQT.h mainwindow.h \ ../../TransportSession.h ../../Tunnel.h ../../TunnelBase.h ../../TunnelConfig.h \ ../../TunnelEndpoint.h ../../TunnelGateway.h ../../TunnelPool.h ../../UPnP.h \ ../../util.h ../../version.h ../../Gzip.h ../../Tag.h \ - ../../BloomFiler.h ../../Event.h + ../../BloomFilter.h ../../Event.h ../../WebSocks.h FORMS += mainwindow.ui From 28cf450bfa9c29b163deb8963ac2af5483d0a043 Mon Sep 17 00:00:00 2001 From: orignal Date: Sat, 28 Jan 2017 09:18:30 -0500 Subject: [PATCH 17/30] show status of shared local destination --- I2PControl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/I2PControl.cpp b/I2PControl.cpp index 23503906..4adc6e07 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -25,6 +25,7 @@ #include "Transports.h" #include "version.h" #include "util.h" +#include "ClientContext.h" #include "I2PControl.h" namespace i2p @@ -401,7 +402,8 @@ namespace client void I2PControlService::StatusHandler (std::ostringstream& results) { - InsertParam (results, "i2p.router.status", i2p::transport::transports.IsOnline () ? "1" : "0"); + auto dest = i2p::client::context.GetSharedLocalDestination (); + InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0"); } void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results) From 2d46cb072e65d70437db0867bb9c528b29e3d831 Mon Sep 17 00:00:00 2001 From: r4sas Date: Sat, 28 Jan 2017 19:23:14 +0300 Subject: [PATCH 18/30] disabled cleanup() before deleting stream on termination (line 54) --- Streaming.cpp | 442 +++++++++++++++++++++++++------------------------- 1 file changed, 221 insertions(+), 221 deletions(-) diff --git a/Streaming.cpp b/Streaming.cpp index 86bfcd14..f8e1e99d 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -11,49 +11,49 @@ namespace i2p { namespace stream { - Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, + Stream::Stream (boost::asio::io_service& service, StreamingDestination& local, std::shared_ptr remote, int port): m_Service (service), - m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), - m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), - m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), - m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), + m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), + m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), + m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), + m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); m_RemoteIdentity = remote->GetIdentity (); - } + } Stream::Stream (boost::asio::io_service& service, StreamingDestination& local): - m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), + m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local), - m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), - m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), + m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service), + m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0) { RAND_bytes ((uint8_t *)&m_RecvStreamID, 4); } Stream::~Stream () - { - CleanUp (); + { + CleanUp (); LogPrint (eLogDebug, "Streaming: Stream deleted"); - } + } void Stream::Terminate () { m_AckSendTimer.cancel (); m_ReceiveTimer.cancel (); m_ResendTimer.cancel (); - if (m_SendHandler) + if (m_SendHandler) { auto handler = m_SendHandler; m_SendHandler = nullptr; handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted)); } - CleanUp (); - m_LocalDestination.DeleteStream (shared_from_this ()); - } + //CleanUp (); /* Need to recheck - broke working on windows */ + m_LocalDestination.DeleteStream (shared_from_this ()); + } void Stream::CleanUp () { @@ -64,25 +64,25 @@ namespace stream m_ReceiveQueue.pop (); m_LocalDestination.DeletePacket (packet); } - + for (auto it: m_SentPackets) m_LocalDestination.DeletePacket (it); m_SentPackets.clear (); - + for (auto it: m_SavedPackets) m_LocalDestination.DeletePacket (it); m_SavedPackets.clear (); - } - + } + void Stream::HandleNextPacket (Packet * packet) { m_NumReceivedBytes += packet->GetLength (); - if (!m_SendStreamID) - m_SendStreamID = packet->GetReceiveStreamID (); + if (!m_SendStreamID) + m_SendStreamID = packet->GetReceiveStreamID (); if (!packet->IsNoAck ()) // ack received ProcessAck (packet); - + int32_t receivedSeqn = packet->GetSeqn (); bool isSyn = packet->IsSYN (); if (!receivedSeqn && !isSyn) @@ -95,13 +95,13 @@ namespace stream LogPrint (eLogDebug, "Streaming: Received seqn=", receivedSeqn, " on sSID=", m_SendStreamID); if (receivedSeqn == m_LastReceivedSequenceNumber + 1) - { + { // we have received next in sequence message ProcessPacket (packet); - + // we should also try stored messages if any for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();) - { + { if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1)) { Packet * savedPacket = *it; @@ -125,35 +125,35 @@ namespace stream m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, shared_from_this (), std::placeholders::_1)); } - } + } else if (isSyn) // we have to send SYN back to incoming connection - SendBuffer (); // also sets m_IsOpen - } - else - { + SendBuffer (); // also sets m_IsOpen + } + else + { if (receivedSeqn <= m_LastReceivedSequenceNumber) { // we have received duplicate LogPrint (eLogWarning, "Streaming: Duplicate message ", receivedSeqn, " on sSID=", m_SendStreamID); SendQuickAck (); // resend ack for previous message again m_LocalDestination.DeletePacket (packet); // packet dropped - } + } else { LogPrint (eLogWarning, "Streaming: Missing messages on sSID=", m_SendStreamID, ": from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1); // save message and wait for missing message again SavePacket (packet); if (m_LastReceivedSequenceNumber >= 0) - { + { // send NACKs for missing messages ASAP if (m_IsAckSendScheduled) { - m_IsAckSendScheduled = false; + m_IsAckSendScheduled = false; m_AckSendTimer.cancel (); } SendQuickAck (); - } + } else { // wait for SYN @@ -161,16 +161,16 @@ namespace stream m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT)); m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer, shared_from_this (), std::placeholders::_1)); - } - } - } - } + } + } + } + } void Stream::SavePacket (Packet * packet) { if (!m_SavedPackets.insert (packet).second) m_LocalDestination.DeletePacket (packet); - } + } void Stream::ProcessPacket (Packet * packet) { @@ -178,52 +178,52 @@ namespace stream uint32_t receivedSeqn = packet->GetSeqn (); uint16_t flags = packet->GetFlags (); LogPrint (eLogDebug, "Streaming: Process seqn=", receivedSeqn, ", flags=", flags); - + const uint8_t * optionData = packet->GetOptionData (); if (flags & PACKET_FLAG_DELAY_REQUESTED) optionData += 2; - + if (flags & PACKET_FLAG_FROM_INCLUDED) { m_RemoteIdentity = std::make_shared(optionData, packet->GetOptionSize ()); optionData += m_RemoteIdentity->GetFullLen (); if (!m_RemoteLeaseSet) LogPrint (eLogDebug, "Streaming: Incoming stream from ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), ", sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); - } + } if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED) { uint16_t maxPacketSize = bufbe16toh (optionData); LogPrint (eLogDebug, "Streaming: Max packet size ", maxPacketSize); optionData += 2; - } - + } + if (flags & PACKET_FLAG_SIGNATURE_INCLUDED) { - uint8_t signature[256]; + uint8_t signature[256]; auto signatureLen = m_RemoteIdentity->GetSignatureLen (); memcpy (signature, optionData, signatureLen); memset (const_cast(optionData), 0, signatureLen); if (!m_RemoteIdentity->Verify (packet->GetBuffer (), packet->GetLength (), signature)) - { + { LogPrint (eLogError, "Streaming: Signature verification failed, sSID=", m_SendStreamID, ", rSID=", m_RecvStreamID); Close (); flags |= PACKET_FLAG_CLOSE; - } + } memcpy (const_cast(optionData), signature, signatureLen); optionData += signatureLen; - } + } packet->offset = packet->GetPayload () - packet->buf; if (packet->GetLength () > 0) - { + { m_ReceiveQueue.push (packet); m_ReceiveTimer.cancel (); - } + } else m_LocalDestination.DeletePacket (packet); - + m_LastReceivedSequenceNumber = receivedSeqn; if (flags & PACKET_FLAG_RESET) @@ -237,9 +237,9 @@ namespace stream if (m_Status != eStreamStatusClosed) SendClose (); m_Status = eStreamStatusClosed; - Terminate (); + Terminate (); } - } + } void Stream::ProcessAck (Packet * packet) { @@ -250,7 +250,7 @@ namespace stream { LogPrint (eLogError, "Streaming: Unexpected ackThrough=", ackThrough, " > seqn=", m_SequenceNumber); return; - } + } int nackCount = packet->GetNACKCount (); for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();) { @@ -271,7 +271,7 @@ namespace stream LogPrint (eLogDebug, "Streaming: Packet ", seqn, " NACK"); ++it; continue; - } + } } auto sentPacket = *it; uint64_t rtt = ts - sentPacket->sendTime; @@ -284,7 +284,7 @@ namespace stream m_RTO = m_RTT*1.5; // TODO: implement it better LogPrint (eLogDebug, "Streaming: Packet ", seqn, " acknowledged rtt=", rtt, " sentTime=", sentPacket->sendTime); m_SentPackets.erase (it++); - m_LocalDestination.DeletePacket (sentPacket); + m_LocalDestination.DeletePacket (sentPacket); acknowledged = true; if (m_WindowSize < WINDOW_SIZE) m_WindowSize++; // slow start @@ -312,13 +312,13 @@ namespace stream { m_NumResendAttempts = 0; SendBuffer (); - } + } if (m_Status == eStreamStatusClosed) Terminate (); else if (m_Status == eStreamStatusClosing) Close (); // check is all outgoing messages have been sent and we can send close - } - + } + size_t Stream::Send (const uint8_t * buf, size_t len) { if (len > 0 && buf) @@ -326,14 +326,14 @@ namespace stream std::unique_lock l(m_SendBufferMutex); m_SendBuffer.clear (); m_SendBuffer.write ((const char *)buf, len); - } + } m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ())); return len; - } + } void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler) { - if (m_SendHandler) + if (m_SendHandler) handler (boost::asio::error::make_error_code (boost::asio::error::in_progress)); else m_SendHandler = handler; @@ -341,10 +341,10 @@ namespace stream } void Stream::SendBuffer () - { + { int numMsgs = m_WindowSize - m_SentPackets.size (); - if (numMsgs <= 0) return; // window is full - + if (numMsgs <= 0) return; // window is full + bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet std::vector packets; { @@ -361,20 +361,20 @@ namespace stream size += 4; // receiveStreamID htobe32buf (packet + size, m_SequenceNumber++); size += 4; // sequenceNum - if (isNoAck) + if (isNoAck) htobuf32 (packet + size, 0); else htobe32buf (packet + size, m_LastReceivedSequenceNumber); size += 4; // ack Through - packet[size] = 0; + packet[size] = 0; size++; // NACK count packet[size] = m_RTO/1000; size++; // resend delay if (m_Status == eStreamStatusNew) - { + { // initial packet m_Status = eStreamStatusOpen; - uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | + uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED | PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED; if (isNoAck) flags |= PACKET_FLAG_NO_ACK; htobe16buf (packet + size, flags); @@ -383,7 +383,7 @@ namespace stream size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen (); htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size size += 2; // options size - m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); + m_LocalDestination.GetOwner ()->GetIdentity ()->ToBuffer (packet + size, identityLen); size += identityLen; // from htobe16buf (packet + size, STREAMING_MTU); size += 2; // max packet size @@ -393,7 +393,7 @@ namespace stream m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size); size += m_SendBuffer.gcount (); // payload m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - } + } else { // follow on packet @@ -401,9 +401,9 @@ namespace stream size += 2; // flags htobuf16 (packet + size, 0); // no options size += 2; // options size - m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size); + m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size); size += m_SendBuffer.gcount (); // payload - } + } p->len = size; packets.push_back (p); numMsgs--; @@ -413,14 +413,14 @@ namespace stream m_SendHandler (boost::system::error_code ()); m_SendHandler = nullptr; } - } + } if (packets.size () > 0) { if (m_SavedPackets.empty ()) // no NACKS - { - m_IsAckSendScheduled = false; + { + m_IsAckSendScheduled = false; m_AckSendTimer.cancel (); - } + } bool isEmpty = m_SentPackets.empty (); auto ts = i2p::util::GetMillisecondsSinceEpoch (); for (auto& it: packets) @@ -433,9 +433,9 @@ namespace stream SendClose (); if (isEmpty) ScheduleResend (); - } + } } - + void Stream::SendQuickAck () { int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber; @@ -443,15 +443,15 @@ namespace stream { int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn (); if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn; - } - if (lastReceivedSeqn < 0) - { + } + if (lastReceivedSeqn < 0) + { LogPrint (eLogError, "Streaming: No packets have been received yet"); return; } - + Packet p; - uint8_t * packet = p.GetBuffer (); + uint8_t * packet = p.GetBuffer (); size_t size = 0; htobe32buf (packet + size, m_SendStreamID); size += 4; // sendStreamID @@ -462,8 +462,8 @@ namespace stream htobe32buf (packet + size, lastReceivedSeqn); size += 4; // ack Through uint8_t numNacks = 0; - if (lastReceivedSeqn > m_LastReceivedSequenceNumber) - { + if (lastReceivedSeqn > m_LastReceivedSequenceNumber) + { // fill NACKs uint8_t * nacks = packet + size + 1; auto nextSeqn = m_LastReceivedSequenceNumber + 1; @@ -475,35 +475,35 @@ namespace stream LogPrint (eLogError, "Streaming: Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn); htobe32buf (packet + 12, nextSeqn); // change ack Through break; - } + } for (uint32_t i = nextSeqn; i < seqn; i++) { htobe32buf (nacks, i); nacks += 4; numNacks++; - } + } nextSeqn = seqn + 1; } - packet[size] = numNacks; - size++; // NACK count + packet[size] = numNacks; + size++; // NACK count size += numNacks*4; // NACKs - } + } else { // No NACKs - packet[size] = 0; - size++; // NACK count - } + packet[size] = 0; + size++; // NACK count + } size++; // resend delay htobuf16 (packet + size, 0); // nof flags set size += 2; // flags htobuf16 (packet + size, 0); // no options size += 2; // options size - p.len = size; + p.len = size; SendPackets (std::vector { &p }); LogPrint (eLogDebug, "Streaming: Quick Ack sent. ", (int)numNacks, " NACKs"); - } + } void Stream::Close () { @@ -518,7 +518,7 @@ namespace stream break; case eStreamStatusReset: // TODO: send reset - Terminate (); + Terminate (); break; case eStreamStatusClosing: if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send @@ -530,10 +530,10 @@ namespace stream case eStreamStatusClosed: // already closed Terminate (); - break; + break; default: LogPrint (eLogWarning, "Streaming: Unexpected stream status ", (int)m_Status, "sSID=", m_SendStreamID); - }; + }; } void Stream::SendClose () @@ -549,7 +549,7 @@ namespace stream size += 4; // sequenceNum htobe32buf (packet + size, m_LastReceivedSequenceNumber >= 0 ? m_LastReceivedSequenceNumber : 0); size += 4; // ack Through - packet[size] = 0; + packet[size] = 0; size++; // NACK count size++; // resend delay htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); @@ -561,12 +561,12 @@ namespace stream memset (packet + size, 0, signatureLen); size += signatureLen; // signature m_LocalDestination.GetOwner ()->Sign (packet, size, signature); - + p->len = size; m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p)); LogPrint (eLogDebug, "Streaming: FIN sent, sSID=", m_SendStreamID); - } - + } + size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len) { size_t pos = 0; @@ -581,18 +581,18 @@ namespace stream { m_ReceiveQueue.pop (); m_LocalDestination.DeletePacket (packet); - } - } - return pos; + } + } + return pos; } bool Stream::SendPacket (Packet * packet) { if (packet) - { + { if (m_IsAckSendScheduled) { - m_IsAckSendScheduled = false; + m_IsAckSendScheduled = false; m_AckSendTimer.cancel (); } SendPackets (std::vector { packet }); @@ -600,17 +600,17 @@ namespace stream m_SentPackets.insert (packet); if (isEmpty) ScheduleResend (); - return true; - } + return true; + } else return false; - } - + } + void Stream::SendPackets (const std::vector& packets) { if (!m_RemoteLeaseSet) { - UpdateCurrentRemoteLease (); + UpdateCurrentRemoteLease (); if (!m_RemoteLeaseSet) { LogPrint (eLogError, "Streaming: Can't send packets, missing remote LeaseSet, sSID=", m_SendStreamID); @@ -618,7 +618,7 @@ namespace stream } } if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) // expired and detached - m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); + m_RoutingSession = m_LocalDestination.GetOwner ()->GetRoutingSession (m_RemoteLeaseSet, true); if (!m_CurrentOutboundTunnel && m_RoutingSession) // first message to send { // try to get shared path first @@ -630,7 +630,7 @@ namespace stream m_RTT = routingPath->rtt; m_RTO = m_RTT*1.5; // TODO: implement it better } - } + } if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ()) m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel); if (!m_CurrentOutboundTunnel) @@ -639,30 +639,30 @@ namespace stream return; } - auto ts = i2p::util::GetMillisecondsSinceEpoch (); + auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (!m_CurrentRemoteLease || !m_CurrentRemoteLease->endDate || // excluded from LeaseSet ts >= m_CurrentRemoteLease->endDate - i2p::data::LEASE_ENDDATE_THRESHOLD) UpdateCurrentRemoteLease (true); if (m_CurrentRemoteLease && ts < m_CurrentRemoteLease->endDate + i2p::data::LEASE_ENDDATE_THRESHOLD) - { + { std::vector msgs; for (auto it: packets) - { + { auto msg = m_RoutingSession->WrapSingleMessage (m_LocalDestination.CreateDataMessage (it->GetBuffer (), it->GetLength (), m_Port)); - msgs.push_back (i2p::tunnel::TunnelMessageBlock - { + msgs.push_back (i2p::tunnel::TunnelMessageBlock + { i2p::tunnel::eDeliveryTypeTunnel, m_CurrentRemoteLease->tunnelGateway, m_CurrentRemoteLease->tunnelID, msg - }); + }); m_NumSentBytes += it->GetLength (); } m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs); - } + } else - { + { LogPrint (eLogWarning, "Streaming: Remote lease is not available, sSID=", m_SendStreamID); - if (m_RoutingSession) + if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); // invalidate routing path } } @@ -670,8 +670,8 @@ namespace stream void Stream::SendUpdatedLeaseSet () { if (m_RoutingSession) - { - if (m_RoutingSession->IsLeaseSetNonConfirmed ()) + { + if (m_RoutingSession->IsLeaseSetNonConfirmed ()) { auto ts = i2p::util::GetMillisecondsSinceEpoch (); if (ts > m_RoutingSession->GetLeaseSetSubmissionTime () + i2p::garlic::LEASET_CONFIRMATION_TIMEOUT) @@ -679,19 +679,19 @@ namespace stream // LeaseSet was not confirmed, should try other tunnels LogPrint (eLogWarning, "Streaming: LeaseSet was not confrimed in ", i2p::garlic::LEASET_CONFIRMATION_TIMEOUT, " milliseconds. Trying to resubmit"); m_RoutingSession->SetSharedRoutingPath (nullptr); - m_CurrentOutboundTunnel = nullptr; + m_CurrentOutboundTunnel = nullptr; m_CurrentRemoteLease = nullptr; SendQuickAck (); - } - } + } + } else if (m_RoutingSession->IsLeaseSetUpdated ()) - { + { LogPrint (eLogDebug, "Streaming: sending updated LeaseSet"); SendQuickAck (); - } - } - } - + } + } + } + void Stream::ScheduleResend () { m_ResendTimer.cancel (); @@ -701,11 +701,11 @@ namespace stream m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, shared_from_this (), std::placeholders::_1)); } - + void Stream::HandleResendTimer (const boost::system::error_code& ecode) { - if (ecode != boost::asio::error::operation_aborted) - { + if (ecode != boost::asio::error::operation_aborted) + { // check for resend attempts if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS) { @@ -713,7 +713,7 @@ namespace stream m_Status = eStreamStatusReset; Close (); return; - } + } // collect packets to resend auto ts = i2p::util::GetMillisecondsSinceEpoch (); @@ -724,8 +724,8 @@ namespace stream { it->sendTime = ts; packets.push_back (it); - } - } + } + } // select tunnels if necessary and send if (packets.size () > 0) @@ -733,7 +733,7 @@ namespace stream m_NumResendAttempts++; m_RTO *= 2; switch (m_NumResendAttempts) - { + { case 1: // congesion avoidance m_WindowSize /= 2; if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE; @@ -745,21 +745,21 @@ namespace stream if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); UpdateCurrentRemoteLease (); // pick another lease LogPrint (eLogWarning, "Streaming: Another remote lease has been selected for stream with rSID=", m_RecvStreamID, ", sSID=", m_SendStreamID); - break; + break; case 3: - // pick another outbound tunnel + // pick another outbound tunnel if (m_RoutingSession) m_RoutingSession->SetSharedRoutingPath (nullptr); - m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); + m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ()->GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel); LogPrint (eLogWarning, "Streaming: Another outbound tunnel has been selected for stream with sSID=", m_SendStreamID); break; - default: ; - } + default: ; + } SendPackets (packets); - } + } ScheduleResend (); - } - } - + } + } + void Stream::HandleAckSendTimer (const boost::system::error_code& ecode) { if (m_IsAckSendScheduled) @@ -770,10 +770,10 @@ namespace stream m_Status = eStreamStatusReset; Close (); return; - } + } if (m_Status == eStreamStatusOpen) { - if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ()) + if (m_RoutingSession && m_RoutingSession->IsLeaseSetNonConfirmed ()) { // seems something went wrong and we should re-select tunnels m_CurrentOutboundTunnel = nullptr; @@ -782,19 +782,19 @@ namespace stream SendQuickAck (); } m_IsAckSendScheduled = false; - } + } } void Stream::UpdateCurrentRemoteLease (bool expired) { - if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) + if (!m_RemoteLeaseSet || m_RemoteLeaseSet->IsExpired ()) { m_RemoteLeaseSet = m_LocalDestination.GetOwner ()->FindLeaseSet (m_RemoteIdentity->GetIdentHash ()); - if (!m_RemoteLeaseSet) - { + if (!m_RemoteLeaseSet) + { LogPrint (eLogWarning, "Streaming: LeaseSet ", m_RemoteIdentity->GetIdentHash ().ToBase64 (), " not found"); m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // try to request for a next attempt - } + } } if (m_RemoteLeaseSet) { @@ -805,11 +805,11 @@ namespace stream { expired = false; m_LocalDestination.GetOwner ()->RequestDestination (m_RemoteIdentity->GetIdentHash ()); // time to request - leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold + leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold } if (!leases.empty ()) - { - bool updated = false; + { + bool updated = false; if (expired && m_CurrentRemoteLease) { for (const auto& it: leases) @@ -818,34 +818,34 @@ namespace stream m_CurrentRemoteLease = it; updated = true; break; - } + } } if (!updated) { uint32_t i = rand () % leases.size (); if (m_CurrentRemoteLease && leases[i]->tunnelID == m_CurrentRemoteLease->tunnelID) - // make sure we don't select previous + // make sure we don't select previous i = (i + 1) % leases.size (); // if so, pick next - m_CurrentRemoteLease = leases[i]; + m_CurrentRemoteLease = leases[i]; } - } + } else - { + { LogPrint (eLogWarning, "Streaming: All remote leases are expired"); m_RemoteLeaseSet = nullptr; m_CurrentRemoteLease = nullptr; // we have requested expired before, no need to do it twice - } + } } else { LogPrint (eLogWarning, "Streaming: Remote LeaseSet not found"); m_CurrentRemoteLease = nullptr; - } - } + } + } - StreamingDestination::StreamingDestination (std::shared_ptr owner, uint16_t localPort, bool gzip): - m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), + StreamingDestination::StreamingDestination (std::shared_ptr owner, uint16_t localPort, bool gzip): + m_Owner (owner), m_LocalPort (localPort), m_Gzip (gzip), m_LastIncomingReceiveStreamID (0), m_PendingIncomingTimer (m_Owner->GetService ()), m_ConnTrackTimer(m_Owner->GetService()), @@ -853,13 +853,13 @@ namespace stream m_LastBanClear(i2p::util::GetMillisecondsSinceEpoch()) { } - + StreamingDestination::~StreamingDestination () { for (auto& it: m_SavedPackets) { for (auto it1: it.second) DeletePacket (it1); - it.second.clear (); + it.second.clear (); } m_SavedPackets.clear (); } @@ -868,9 +868,9 @@ namespace stream { ScheduleConnTrack(); } - + void StreamingDestination::Stop () - { + { ResetAcceptor (); m_PendingIncomingTimer.cancel (); m_PendingIncomingStreams.clear (); @@ -883,34 +883,34 @@ namespace stream std::unique_lock l(m_ConnsMutex); m_Conns.clear (); } - } - + } + void StreamingDestination::HandleNextPacket (Packet * packet) { uint32_t sendStreamID = packet->GetSendStreamID (); if (sendStreamID) - { + { auto it = m_Streams.find (sendStreamID); if (it != m_Streams.end ()) it->second->HandleNextPacket (packet); else - { + { LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID); DeletePacket (packet); } - } - else + } + else { if (packet->IsSYN () && !packet->GetSeqn ()) // new incoming stream - { + { uint32_t receiveStreamID = packet->GetReceiveStreamID (); - if (receiveStreamID == m_LastIncomingReceiveStreamID) + if (receiveStreamID == m_LastIncomingReceiveStreamID) { // already pending LogPrint(eLogWarning, "Streaming: Incoming streaming with rSID=", receiveStreamID, " already exists"); DeletePacket (packet); // drop it, because previous should be connected return; - } + } auto incomingStream = CreateNewIncomingStream (); incomingStream->HandleNextPacket (packet); // SYN auto ident = incomingStream->GetRemoteIdentity(); @@ -926,7 +926,7 @@ namespace stream } } m_LastIncomingReceiveStreamID = receiveStreamID; - + // handle saved packets if any { auto it = m_SavedPackets.find (receiveStreamID); @@ -936,7 +936,7 @@ namespace stream for (auto it1: it->second) incomingStream->HandleNextPacket (it1); m_SavedPackets.erase (it); - } + } } // accept if (m_Acceptor != nullptr) @@ -949,17 +949,17 @@ namespace stream m_PendingIncomingStreams.push_back (incomingStream); m_PendingIncomingTimer.cancel (); m_PendingIncomingTimer.expires_from_now (boost::posix_time::seconds(PENDING_INCOMING_TIMEOUT)); - m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer, + m_PendingIncomingTimer.async_wait (std::bind (&StreamingDestination::HandlePendingIncomingTimer, shared_from_this (), std::placeholders::_1)); LogPrint (eLogDebug, "Streaming: Pending incoming stream added, rSID=", receiveStreamID); } else - { + { LogPrint (eLogWarning, "Streaming: Pending incoming streams backlog exceeds ", MAX_PENDING_INCOMING_BACKLOG); incomingStream->Close (); - } - } - } + } + } + } else // follow on packet without SYN { uint32_t receiveStreamID = packet->GetReceiveStreamID (); @@ -994,17 +994,17 @@ namespace stream } }); } - } - } - } - + } + } + } + std::shared_ptr StreamingDestination::CreateNewOutgoingStream (std::shared_ptr remote, int port) { auto s = std::make_shared (m_Owner->GetService (), *this, remote, port); std::unique_lock l(m_StreamsMutex); m_Streams[s->GetRecvStreamID ()] = s; return s; - } + } std::shared_ptr StreamingDestination::CreateNewIncomingStream () { @@ -1017,39 +1017,39 @@ namespace stream void StreamingDestination::DeleteStream (std::shared_ptr stream) { if (stream) - { + { std::unique_lock l(m_StreamsMutex); auto it = m_Streams.find (stream->GetRecvStreamID ()); if (it != m_Streams.end ()) m_Streams.erase (it); - } - } + } + } - void StreamingDestination::SetAcceptor (const Acceptor& acceptor) - { + void StreamingDestination::SetAcceptor (const Acceptor& acceptor) + { m_Acceptor = acceptor; // we must set it immediately for IsAcceptorSet auto s = shared_from_this (); m_Owner->GetService ().post([s](void) - { + { // take care about incoming queue for (auto& it: s->m_PendingIncomingStreams) if (it->GetStatus () == eStreamStatusOpen) // still open? s->m_Acceptor (it); s->m_PendingIncomingStreams.clear (); s->m_PendingIncomingTimer.cancel (); - }); + }); } - void StreamingDestination::ResetAcceptor () - { - if (m_Acceptor) m_Acceptor (nullptr); - m_Acceptor = nullptr; + void StreamingDestination::ResetAcceptor () + { + if (m_Acceptor) m_Acceptor (nullptr); + m_Acceptor = nullptr; } void StreamingDestination::AcceptOnce (const Acceptor& acceptor) { m_Owner->GetService ().post([acceptor, this](void) - { + { if (!m_PendingIncomingStreams.empty ()) { acceptor (m_PendingIncomingStreams.front ()); @@ -1059,14 +1059,14 @@ namespace stream } else // we must save old acceptor and set it back { - auto oldAcceptor = m_Acceptor; + auto oldAcceptor = m_Acceptor; m_Acceptor = [acceptor, oldAcceptor, this](std::shared_ptr stream) { acceptor (stream); m_Acceptor = oldAcceptor; }; } - }); + }); } void StreamingDestination::HandlePendingIncomingTimer (const boost::system::error_code& ecode) @@ -1077,9 +1077,9 @@ namespace stream for (auto& it: m_PendingIncomingStreams) it->Close (); m_PendingIncomingStreams.clear (); - } - } - + } + } + void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len) { // unzip it @@ -1087,7 +1087,7 @@ namespace stream uncompressed->offset = 0; uncompressed->len = m_Inflator.Inflate (buf, len, uncompressed->buf, MAX_PACKET_SIZE); if (uncompressed->len) - HandleNextPacket (uncompressed); + HandleNextPacket (uncompressed); else DeletePacket (uncompressed); } @@ -1107,11 +1107,11 @@ namespace stream { htobe32buf (msg->GetPayload (), size); // length htobe16buf (buf + 4, m_LocalPort); // source port - htobe16buf (buf + 6, toPort); // destination port + htobe16buf (buf + 6, toPort); // destination port buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol - msg->len += size; + msg->len += size; msg->FillI2NPMessageHeader (eI2NPData); - } + } else msg = nullptr; return msg; @@ -1164,15 +1164,15 @@ namespace stream } // reschedule timer ScheduleConnTrack(); - } + } } void StreamingDestination::ScheduleConnTrack() { m_ConnTrackTimer.expires_from_now (boost::posix_time::seconds(60)); m_ConnTrackTimer.async_wait ( - std::bind (&StreamingDestination::HandleConnTrack, + std::bind (&StreamingDestination::HandleConnTrack, shared_from_this (), std::placeholders::_1)); } -} -} +} +} From f722b3e9cb62aff79e92bc41432516e20a76b5c0 Mon Sep 17 00:00:00 2001 From: r4sas Date: Sun, 29 Jan 2017 17:08:36 +0300 Subject: [PATCH 19/30] Moved reopening of log to SIGUSR1 (16) Added --pidfile option to init.d script --- DaemonLinux.cpp | 32 ++++++++++++++++++-------------- debian/i2pd.init | 2 +- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index 07e27e9d..416b6d2c 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -21,20 +21,23 @@ void handle_signal(int sig) switch (sig) { case SIGHUP: - LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening logs and tunnel configuration..."); - i2p::log::Logger().Reopen (); + LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening tunnel configuration..."); i2p::client::context.ReloadConfig(); break; + case SIGUSR1: + LogPrint(eLogInfo, "Daemon: Got SIGUSR1, reopening logs..."); + i2p::log::Logger().Reopen (); + break; case SIGINT: if (i2p::context.AcceptsTunnels () && !Daemon.gracefulShutdownInterval) - { + { i2p::context.SetAcceptsTunnels (false); Daemon.gracefulShutdownInterval = 10*60; // 10 minutes LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefulShutdownInterval, " seconds"); - } + } else - Daemon.running = 0; - break; + Daemon.running = 0; + break; case SIGABRT: case SIGTERM: Daemon.running = 0; // Exit loop @@ -77,7 +80,7 @@ namespace i2p } // point std{in,out,err} descriptors to /dev/null - freopen("/dev/null", "r", stdin); + freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); } @@ -101,8 +104,8 @@ namespace i2p } uint32_t cfsize; i2p::config::GetOption("limits.coresize", cfsize); if (cfsize) // core file size set - { - cfsize *= 1024; + { + cfsize *= 1024; getrlimit(RLIMIT_CORE, &limit); if (cfsize <= limit.rlim_max) { limit.rlim_cur = cfsize; @@ -116,7 +119,7 @@ namespace i2p } else { LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max); } - } + } // Pidfile // this code is c-styled and a bit ugly, but we need fd for locking pidfile @@ -153,6 +156,7 @@ namespace i2p sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGHUP, &sa, 0); + sigaction(SIGUSR1, &sa, 0); sigaction(SIGABRT, &sa, 0); sigaction(SIGTERM, &sa, 0); sigaction(SIGINT, &sa, 0); @@ -164,7 +168,7 @@ namespace i2p { i2p::fs::Remove(pidfile); - return Daemon_Singleton::stop(); + return Daemon_Singleton::stop(); } void DaemonLinux::run () @@ -175,12 +179,12 @@ namespace i2p if (gracefulShutdownInterval) { gracefulShutdownInterval--; // - 1 second - if (gracefulShutdownInterval <= 0) - { + if (gracefulShutdownInterval <= 0) + { LogPrint(eLogInfo, "Graceful shutdown"); return; } - } + } } } } diff --git a/debian/i2pd.init b/debian/i2pd.init index ca403598..e4ed01e1 100644 --- a/debian/i2pd.init +++ b/debian/i2pd.init @@ -53,7 +53,7 @@ do_start() || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid "$USER" -- \ --service --daemon --log=file --logfile=$LOGFILE --conf=$I2PCONF --tunconf=$TUNCONF \ - $DAEMON_OPTS > /dev/null 2>&1 \ + --pidfile=$PIDFILE $DAEMON_OPTS > /dev/null 2>&1 \ || return 2 return $? } From bdcbaa031d4247ddc67afb52ddcead3d037d004a Mon Sep 17 00:00:00 2001 From: orignal Date: Sun, 29 Jan 2017 19:16:34 -0500 Subject: [PATCH 20/30] clean transit tunnels endpoints --- TransitTunnel.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TransitTunnel.h b/TransitTunnel.h index 2a1908df..eec244ce 100644 --- a/TransitTunnel.h +++ b/TransitTunnel.h @@ -85,6 +85,8 @@ namespace tunnel TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey), m_Endpoint (false) {}; // transit endpoint is always outbound + void Cleanup () { m_Endpoint.Cleanup (); } + void HandleTunnelDataMsg (std::shared_ptr tunnelMsg); size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); } From 35230472437dfd6617b137efc91f5ea508d4c7c3 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 30 Jan 2017 19:31:01 -0500 Subject: [PATCH 21/30] #788 ReseedFromZIPFile added --- Reseed.cpp | 28 +++++++++++++++++++++++++--- Reseed.h | 4 +++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Reseed.cpp b/Reseed.cpp index 52079565..9387d2ae 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -83,10 +83,24 @@ namespace data } } + int Reseeder::ProcessZIPFile (const char * filename) + { + std::ifstream s(filename, std::ifstream::binary); + if (s.is_open ()) + { + s.seekg (0, std::ios::end); + auto len = s.tellg (); + s.seekg (0, std::ios::beg); + return ProcessZIPStream (s, len); + } + else + { + LogPrint (eLogError, "Reseed: Can't open file ", filename); + return 0; + } + } + const char SU3_MAGIC_NUMBER[]="I2Psu3"; - const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; - const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; - const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; int Reseeder::ProcessSU3Stream (std::istream& s) { char magicNumber[7]; @@ -194,6 +208,14 @@ namespace data } // handle content + return ProcessZIPStream (s, contentLength); + } + + const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50; + const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50; + const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008; + int Reseeder::ProcessZIPStream (std::istream& s, uint64_t contentLength) + { int numFiles = 0; size_t contentPos = s.tellg (); while (!s.eof ()) diff --git a/Reseed.h b/Reseed.h index de47c0b1..3ef8767c 100644 --- a/Reseed.h +++ b/Reseed.h @@ -31,8 +31,10 @@ namespace data int ReseedFromSU3 (const std::string& url); int ProcessSU3File (const char * filename); + int ProcessZIPFile (const char * filename); int ProcessSU3Stream (std::istream& s); - + int ProcessZIPStream (std::istream& s, uint64_t contentLength); + bool FindZipDataDescriptor (std::istream& s); std::string HttpsRequest (const std::string& address); From 7094588c5338f21a94c1f2244a2bf76d31914158 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 30 Jan 2017 19:56:06 -0500 Subject: [PATCH 22/30] print zlib error codes --- Gzip.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Gzip.cpp b/Gzip.cpp index da9f06b1..db991283 100644 --- a/Gzip.cpp +++ b/Gzip.cpp @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2017, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * @@ -9,11 +9,13 @@ #include #include /* memset */ #include - +#include "Log.h" #include "Gzip.h" -namespace i2p { -namespace data { +namespace i2p +{ +namespace data +{ const size_t GZIP_CHUNK_SIZE = 16384; GzipInflator::GzipInflator (): m_IsDirty (false) @@ -36,9 +38,10 @@ namespace data { m_Inflator.next_out = out; m_Inflator.avail_out = outLen; int err; - if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) { + if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) return outLen - m_Inflator.avail_out; - } + // else + LogPrint (eLogError, "Gzip: Inflate error ", err); return 0; } @@ -49,17 +52,20 @@ namespace data { m_Inflator.next_in = const_cast(in); m_Inflator.avail_in = inLen; int ret; - do { + do + { m_Inflator.next_out = out; m_Inflator.avail_out = GZIP_CHUNK_SIZE; ret = inflate (&m_Inflator, Z_NO_FLUSH); - if (ret < 0) { + if (ret < 0) + { inflateEnd (&m_Inflator); os.setstate(std::ios_base::failbit); break; } os.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out); - } while (!m_Inflator.avail_out); // more data to read + } + while (!m_Inflator.avail_out); // more data to read delete[] out; } @@ -99,9 +105,10 @@ namespace data { m_Deflator.next_out = out; m_Deflator.avail_out = outLen; int err; - if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) { + if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) return outLen - m_Deflator.avail_out; - } /* else */ + // else + LogPrint (eLogError, "Gzip: Deflate error ", err); return 0; } } // data From 3e2605490f1b2f299b17eb85513e17197d767435 Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 30 Jan 2017 20:36:35 -0500 Subject: [PATCH 23/30] cleanup error messages --- NetDb.cpp | 8 ++++---- Streaming.cpp | 2 +- Transports.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/NetDb.cpp b/NetDb.cpp index 88d5483f..8b4ec88a 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -583,7 +583,7 @@ namespace data IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET); if (ident.IsZero ()) { - LogPrint (eLogError, "NetDb: database store with zero ident, dropped"); + LogPrint (eLogDebug, "NetDb: database store with zero ident, dropped"); return; } uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET); @@ -602,14 +602,14 @@ namespace data if (outbound) outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus); else - LogPrint (eLogError, "NetDb: no outbound tunnels for DatabaseStore reply found"); + LogPrint (eLogWarning, "NetDb: no outbound tunnels for DatabaseStore reply found"); } offset += 32; } // we must send reply back before this check if (ident == i2p::context.GetIdentHash ()) { - LogPrint (eLogError, "NetDb: database store with own RouterInfo received, dropped"); + LogPrint (eLogDebug, "NetDb: database store with own RouterInfo received, dropped"); return; } size_t payloadOffset = offset; @@ -636,7 +636,7 @@ namespace data updated = AddRouterInfo (ident, uncompressed, uncompressedSize); else { - LogPrint (eLogError, "NetDb: decompression failed ", uncompressedSize); + LogPrint (eLogInfo, "NetDb: decompression failed ", uncompressedSize); return; } } diff --git a/Streaming.cpp b/Streaming.cpp index f8e1e99d..551b1ff6 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -895,7 +895,7 @@ namespace stream it->second->HandleNextPacket (packet); else { - LogPrint (eLogError, "Streaming: Unknown stream sSID=", sendStreamID); + LogPrint (eLogInfo, "Streaming: Unknown stream sSID=", sendStreamID); DeletePacket (packet); } } diff --git a/Transports.cpp b/Transports.cpp index 5b188a7d..b0a19379 100644 --- a/Transports.cpp +++ b/Transports.cpp @@ -418,7 +418,7 @@ namespace transport } else { - LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages"); + LogPrint (eLogWarning, "Transports: RouterInfo not found, Failed to send messages"); std::unique_lock l(m_PeersMutex); m_Peers.erase (it); } From 76fd1c5c580e887d262d740e309d00614aae26dc Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 29 Jan 2017 20:38:18 -0500 Subject: [PATCH 24/30] udp sockets for sam --- Makefile.linux | 2 +- SAM.cpp | 103 ++++++++++++++++++++++++++++++++++++++++--------- SAM.h | 10 ++++- 3 files changed, 95 insertions(+), 20 deletions(-) diff --git a/Makefile.linux b/Makefile.linux index 02f099d2..af23f955 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,5 +1,5 @@ # set defaults instead redefine -CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic +CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation INCFLAGS ?= ## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time diff --git a/SAM.cpp b/SAM.cpp index 929b144d..d9bc7bb2 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -252,6 +252,7 @@ namespace client Terminate (); } } + else { LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred); @@ -278,13 +279,37 @@ namespace client return; } + std::shared_ptr forward = nullptr; + if (style == SAM_VALUE_DATAGRAM && params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end()) + { + // udp forward selected + boost::system::error_code e; + // TODO: support hostnames in udp forward + auto addr = boost::asio::ip::address::from_string(params[SAM_VALUE_HOST], e); + if (e) + { + // not an ip address + SendI2PError("Invalid IP Address in HOST"); + return; + } + + auto port = std::stoi(params[SAM_VALUE_PORT]); + if (port == -1) + { + SendI2PError("Invalid port"); + return; + } + forward = std::make_shared(addr, port); + } + // create destination - m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); + m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); if (m_Session) { m_SocketType = eSAMSocketTypeSession; if (style == SAM_VALUE_DATAGRAM) { + m_Session->UDPEndpoint = forward; auto dest = m_Session->localDestination->CreateDatagramDestination (); dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); @@ -480,18 +505,29 @@ namespace client shared_from_this (), std::placeholders::_1, ident)); } else - { - LogPrint (eLogError, "SAM: naming failed, unknown address ", name); + { + LogPrint (eLogError, "SAM: naming failed, unknown address ", name); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif - SendMessageReply (m_Buffer, len, false); + SendMessageReply (m_Buffer, len, false); } - } + } - void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) + void SAMSocket::SendI2PError(const std::string & msg) + { + LogPrint (eLogError, "SAM: i2p error ", msg); +#ifdef _MSC_VER + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); +#endif + SendMessageReply (m_Buffer, len, true); + } + + void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) { if (leaseSet) { @@ -692,23 +728,46 @@ namespace client { LogPrint (eLogDebug, "SAM: datagram received ", len); auto base64 = from.ToBase64 (); + auto ep = m_Session->UDPEndpoint; + if (ep) + { + // udp forward enabled + size_t bsz = base64.size(); + size_t sz = 4 + bsz + 1 + len; + // build datagram body + uint8_t * data = new uint8_t[sz]; + data[0] = '3'; + data[1] = '.'; + data[2] = '0'; + data[3] = ' '; + memcpy(data+4, base64.c_str(), bsz); + data[4+bsz] = '\n'; + memcpy(data+4+bsz+1, buf, sz); + // send to remote endpoint + m_Owner.SendTo(data, sz, ep); + delete [] buf; + } + else + { #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); #else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); + size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); #endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { - memcpy (m_StreamBuffer + l, buf, len); - boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), - std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + if (len < SAM_SOCKET_BUFFER_SIZE - l) + { + memcpy (m_StreamBuffer + l, buf, len); + boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), + std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); + } + else + LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer"); } - else - LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer"); } SAMSession::SAMSession (std::shared_ptr dest): - localDestination (dest) + localDestination (dest), + UDPEndpoint(nullptr) { } @@ -873,6 +932,14 @@ namespace client return nullptr; } + void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote) + { + if(remote) + { + m_DatagramSocket.send_to(boost::asio::buffer(buf, len), *remote); + } + } + void SAMBridge::ReceiveDatagram () { m_DatagramSocket.async_receive_from ( diff --git a/SAM.h b/SAM.h index db08c5a0..c5aef611 100644 --- a/SAM.h +++ b/SAM.h @@ -29,6 +29,7 @@ namespace client const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; + const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n"; const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; @@ -58,7 +59,9 @@ namespace client const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; const char SAM_VALUE_RAW[] = "RAW"; const char SAM_VALUE_TRUE[] = "true"; - const char SAM_VALUE_FALSE[] = "false"; + const char SAM_VALUE_FALSE[] = "false"; + const char SAM_VALUE_HOST[] = "HOST"; + const char SAM_VALUE_PORT[] = "PORT"; enum SAMSocketType { @@ -106,6 +109,7 @@ namespace client void ProcessStreamAccept (char * buf, size_t len); void ProcessDestGenerate (); void ProcessNamingLookup (char * buf, size_t len); + void SendI2PError(const std::string & msg); size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 void ExtractParams (char * buf, std::map& params); @@ -135,6 +139,7 @@ namespace client { std::shared_ptr localDestination; std::list > m_Sockets; + std::shared_ptr UDPEndpoint; std::mutex m_SocketsMutex; /** safely add a socket to this session */ @@ -181,6 +186,9 @@ namespace client void CloseSession (const std::string& id); std::shared_ptr FindSession (const std::string& id) const; + /** send raw data to remote endpoint from our UDP Socket */ + void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); + private: void Run (); From 775b9f30f037e7db45105f218aa9643e3ae5441d Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 31 Jan 2017 11:16:55 -0500 Subject: [PATCH 25/30] indentation fixes and SAM datagrams --- SAM.cpp | 232 ++++++++++++++++++++++++++++---------------------------- SAM.h | 67 ++++++++-------- 2 files changed, 149 insertions(+), 150 deletions(-) diff --git a/SAM.cpp b/SAM.cpp index d9bc7bb2..3d51041a 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -15,9 +15,9 @@ namespace i2p { namespace client { - SAMSocket::SAMSocket (SAMBridge& owner): + SAMSocket::SAMSocket (SAMBridge& owner): m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()), - m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), + m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_Stream (nullptr), m_Session (nullptr) { } @@ -25,21 +25,21 @@ namespace client SAMSocket::~SAMSocket () { Terminate (); - } + } void SAMSocket::CloseStream () { if (m_Stream) - { + { m_Stream->Close (); m_Stream.reset (); - } - } - + } + } + void SAMSocket::Terminate () { CloseStream (); - + switch (m_SocketType) { case eSAMSocketTypeSession: @@ -47,14 +47,14 @@ namespace client break; case eSAMSocketTypeStream: { - if (m_Session) + if (m_Session) m_Session->DelSocket (shared_from_this ()); break; } case eSAMSocketTypeAcceptor: { if (m_Session) - { + { m_Session->DelSocket (shared_from_this ()); if (m_Session->localDestination) m_Session->localDestination->StopAcceptingStreams (); @@ -71,8 +71,8 @@ namespace client void SAMSocket::ReceiveHandshake () { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -85,7 +85,7 @@ namespace client Terminate (); } else - { + { m_Buffer[bytes_transferred] = 0; char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred); if (eol) @@ -94,8 +94,8 @@ namespace client char * separator = strchr (m_Buffer, ' '); if (separator) { - separator = strchr (separator + 1, ' '); - if (separator) + separator = strchr (separator + 1, ' '); + if (separator) *separator = 0; } @@ -117,13 +117,13 @@ namespace client { #ifdef _MSC_VER size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); -#else +#else size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ()); #endif boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), + std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); - } + } else SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true); } @@ -145,25 +145,25 @@ namespace client } else { - m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), - std::bind(&SAMSocket::HandleMessage, shared_from_this (), - std::placeholders::_1, std::placeholders::_2)); - } + m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + std::bind(&SAMSocket::HandleMessage, shared_from_this (), + std::placeholders::_1, std::placeholders::_2)); + } } void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close) { - if (!m_IsSilent) + if (!m_IsSilent) boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), - std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), + std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, close)); else { if (close) Terminate (); else - Receive (); - } + Receive (); + } } void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) @@ -179,8 +179,8 @@ namespace client if (close) Terminate (); else - Receive (); - } + Receive (); + } } void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -205,8 +205,8 @@ namespace client char * separator = strchr (m_Buffer, ' '); if (separator) { - separator = strchr (separator + 1, ' '); - if (separator) + separator = strchr (separator + 1, ' '); + if (separator) *separator = 0; else separator = eol; @@ -236,12 +236,12 @@ namespace client *separator = ' '; *eol = '\n'; } - } + } // since it's SAM v1 reply is not expected Receive (); } - else - { + else + { LogPrint (eLogError, "SAM: unexpected message ", m_Buffer); Terminate (); } @@ -254,7 +254,7 @@ namespace client } else - { + { LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred); m_BufferOffset = bytes_transferred; // try to receive remaining message @@ -268,10 +268,10 @@ namespace client LogPrint (eLogDebug, "SAM: session create: ", buf); std::map params; ExtractParams (buf, params); - std::string& style = params[SAM_PARAM_STYLE]; + std::string& style = params[SAM_PARAM_STYLE]; std::string& id = params[SAM_PARAM_ID]; std::string& destination = params[SAM_PARAM_DESTINATION]; - m_ID = id; + m_ID = id; if (m_Owner.FindSession (id)) { // session exists @@ -302,7 +302,7 @@ namespace client forward = std::make_shared(addr, port); } - // create destination + // create destination m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms); if (m_Session) { @@ -311,7 +311,7 @@ namespace client { m_Session->UDPEndpoint = forward; auto dest = m_Session->localDestination->CreateDatagramDestination (); - dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), + dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); } @@ -321,7 +321,7 @@ namespace client { m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, - shared_from_this (), std::placeholders::_1)); + shared_from_this (), std::placeholders::_1)); } } else @@ -339,7 +339,7 @@ namespace client m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, shared_from_this (), std::placeholders::_1)); - } + } } } @@ -352,7 +352,7 @@ namespace client priv[l1] = 0; #ifdef _MSC_VER size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); -#else +#else size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv); #endif SendMessageReply (m_Buffer, l2, false); @@ -366,7 +366,7 @@ namespace client std::string& id = params[SAM_PARAM_ID]; std::string& destination = params[SAM_PARAM_DESTINATION]; std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; m_ID = id; m_Session = m_Owner.FindSession (id); if (m_Session) @@ -390,7 +390,7 @@ namespace client SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true); } else - SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); + SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); } void SAMSocket::Connect (std::shared_ptr remote) @@ -399,7 +399,7 @@ namespace client m_Session->AddSocket (shared_from_this ()); m_Stream = m_Session->localDestination->CreateStream (remote); m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect - I2PReceive (); + I2PReceive (); SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); } @@ -421,17 +421,17 @@ namespace client ExtractParams (buf, params); std::string& id = params[SAM_PARAM_ID]; std::string& silent = params[SAM_PARAM_SILENT]; - if (silent == SAM_VALUE_TRUE) m_IsSilent = true; + if (silent == SAM_VALUE_TRUE) m_IsSilent = true; m_ID = id; m_Session = m_Owner.FindSession (id); if (m_Session) - { + { m_SocketType = eSAMSocketTypeAcceptor; m_Session->AddSocket (shared_from_this ()); if (!m_Session->localDestination->IsAcceptingStreams ()) m_Session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1)); SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false); - } + } else SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true); } @@ -443,9 +443,9 @@ namespace client ExtractParams (buf, params); size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf; if (offset + size <= len) - { + { if (m_Session) - { + { auto d = m_Session->localDestination->GetDatagramDestination (); if (d) { @@ -458,24 +458,24 @@ namespace client } else LogPrint (eLogError, "SAM: session is not created from DATAGRAM SEND"); - } + } else { LogPrint (eLogWarning, "SAM: sent datagram size ", size, " exceeds buffer ", len - offset); return 0; // try to receive more - } + } return offset + size; - } - + } + void SAMSocket::ProcessDestGenerate () { LogPrint (eLogDebug, "SAM: dest generate"); auto keys = i2p::data::PrivateKeys::CreateRandomKeys (); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); -#else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, +#else + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); #endif SendMessageReply (m_Buffer, len, false); @@ -504,12 +504,12 @@ namespace client std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, shared_from_this (), std::placeholders::_1, ident)); } - else + else { LogPrint (eLogError, "SAM: naming failed, unknown address ", name); #ifdef _MSC_VER size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); -#else +#else size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif SendMessageReply (m_Buffer, len, false); @@ -521,7 +521,7 @@ namespace client LogPrint (eLogError, "SAM: i2p error ", msg); #ifdef _MSC_VER size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); -#else +#else size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str()); #endif SendMessageReply (m_Buffer, len, true); @@ -530,30 +530,30 @@ namespace client void SAMSocket::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr leaseSet, i2p::data::IdentHash ident) { if (leaseSet) - { + { context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ()); SendNamingLookupReply (leaseSet->GetIdentity ()); - } + } else { LogPrint (eLogError, "SAM: naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found"); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, context.GetAddressBook ().ToAddress (ident).c_str()); -#else +#else size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, context.GetAddressBook ().ToAddress (ident).c_str()); #endif SendMessageReply (m_Buffer, len, false); } - } - + } + void SAMSocket::SendNamingLookupReply (std::shared_ptr identity) { auto base64 = identity->ToBase64 (); #ifdef _MSC_VER - size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); -#else + size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); +#else size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); #endif SendMessageReply (m_Buffer, l, false); @@ -561,7 +561,7 @@ namespace client void SAMSocket::ExtractParams (char * buf, std::map& params) { - char * separator; + char * separator; do { separator = strchr (buf, ' '); @@ -572,11 +572,11 @@ namespace client *value = 0; value++; params[buf] = value; - } + } buf = separator + 1; } while (separator); - } + } void SAMSocket::Receive () { @@ -586,7 +586,7 @@ namespace client Terminate (); return; } - m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), + m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset), std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -602,17 +602,17 @@ namespace client else { if (m_Stream) - { + { auto s = shared_from_this (); m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred, [s](const boost::system::error_code& ecode) { if (!ecode) s->Receive (); - else + else s->m_Owner.GetService ().post ([s] { s->Terminate (); }); }); - } + } } } @@ -622,7 +622,7 @@ namespace client { if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew || m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular - { + { m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE), std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (), std::placeholders::_1, std::placeholders::_2), @@ -638,10 +638,10 @@ namespace client std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); } else // no more data - Terminate (); - } + Terminate (); + } } - } + } void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) { @@ -653,17 +653,17 @@ namespace client if (bytes_transferred > 0) boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred), std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); // postpone termination - else - { + else + { auto s = shared_from_this (); m_Owner.GetService ().post ([s] { s->Terminate (); }); } } - else - { + else + { auto s = shared_from_this (); m_Owner.GetService ().post ([s] { s->Terminate (); }); - } + } } else { @@ -694,7 +694,7 @@ namespace client context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ()); auto session = m_Owner.FindSession (m_ID); if (session) - { + { // find more pending acceptors for (auto it: session->ListSockets ()) if (it->m_SocketType == eSAMSocketTypeAcceptor) @@ -710,19 +710,19 @@ namespace client const size_t ident_len = ident_ptr->GetFullLen(); uint8_t* ident = new uint8_t[ident_len]; - // send remote peer address as base64 + // send remote peer address as base64 const size_t l = ident_ptr->ToBuffer (ident, ident_len); const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE); delete[] ident; m_StreamBuffer[l1] = '\n'; HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream - } + } else I2PReceive (); } else LogPrint (eLogWarning, "SAM: I2P acceptor has been reset"); - } + } void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len) { @@ -750,12 +750,12 @@ namespace client else { #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); -#else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); +#else + size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); #endif - if (len < SAM_SOCKET_BUFFER_SIZE - l) - { + if (len < SAM_SOCKET_BUFFER_SIZE - l) + { memcpy (m_StreamBuffer + l, buf, len); boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l), std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); @@ -770,7 +770,7 @@ namespace client UDPEndpoint(nullptr) { } - + SAMSession::~SAMSession () { CloseStreams(); @@ -800,7 +800,7 @@ namespace client { if (m_IsRunning) Stop (); - } + } void SAMBridge::Start () { @@ -819,26 +819,26 @@ namespace client m_Sessions.clear (); m_Service.stop (); if (m_Thread) - { - m_Thread->join (); + { + m_Thread->join (); delete m_Thread; m_Thread = nullptr; - } + } } - void SAMBridge::Run () - { + void SAMBridge::Run () + { while (m_IsRunning) { try - { + { m_Service.run (); } catch (std::exception& ex) { LogPrint (eLogError, "SAM: runtime exception: ", ex.what ()); - } - } + } + } } void SAMBridge::Accept () @@ -855,7 +855,7 @@ namespace client boost::system::error_code ec; auto ep = socket->GetSocket ().remote_endpoint (ec); if (!ec) - { + { LogPrint (eLogDebug, "SAM: new connection from ", ep); socket->ReceiveHandshake (); } @@ -869,10 +869,10 @@ namespace client Accept (); } - std::shared_ptr SAMBridge::CreateSession (const std::string& id, const std::string& destination, + std::shared_ptr SAMBridge::CreateSession (const std::string& id, const std::string& destination, const std::map * params) { - std::shared_ptr localDestination = nullptr; + std::shared_ptr localDestination = nullptr; if (destination != "") { i2p::data::PrivateKeys keys; @@ -887,10 +887,10 @@ namespace client { auto it = params->find (SAM_PARAM_SIGNATURE_TYPE); if (it != params->end ()) - // TODO: extract string values + // TODO: extract string values signatureType = std::stoi(it->second); } - localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params); + localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params); } if (localDestination) { @@ -911,13 +911,13 @@ namespace client std::unique_lock l(m_SessionsMutex); auto it = m_Sessions.find (id); if (it != m_Sessions.end ()) - { + { session = it->second; m_Sessions.erase (it); - } - } + } + } if (session) - { + { session->localDestination->StopAcceptingStreams (); session->CloseStreams (); } @@ -943,9 +943,9 @@ namespace client void SAMBridge::ReceiveDatagram () { m_DatagramSocket.async_receive_from ( - boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), + boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE), m_SenderEndpoint, - std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); + std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2)); } void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -955,7 +955,7 @@ namespace client m_DatagramReceiveBuffer[bytes_transferred] = 0; char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n'); *eol = 0; eol++; - size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); + size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer); LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); if (sessionID) @@ -967,12 +967,12 @@ namespace client *destination = 0; destination++; auto session = FindSession (sessionID); if (session) - { + { i2p::data::IdentityEx dest; dest.FromBase64 (destination); session->localDestination->GetDatagramDestination ()-> SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); - } + } else LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); } diff --git a/SAM.h b/SAM.h index c5aef611..db6991f5 100644 --- a/SAM.h +++ b/SAM.h @@ -20,48 +20,48 @@ namespace client { const size_t SAM_SOCKET_BUFFER_SIZE = 8192; const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds - const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds + 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_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\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"; const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n"; - const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; + const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n"; const char SAM_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\n"; - const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n"; + const char SAM_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\n"; const char SAM_STREAM_CONNECT[] = "STREAM CONNECT"; const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n"; const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n"; const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n"; const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n"; - const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; + const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT"; const char SAM_DATAGRAM_SEND[] = "DATAGRAM SEND"; const char SAM_DEST_GENERATE[] = "DEST GENERATE"; - const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; + const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n"; const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n"; - const char SAM_PARAM_MIN[] = "MIN"; - const char SAM_PARAM_MAX[] = "MAX"; - const char SAM_PARAM_STYLE[] = "STYLE"; - const char SAM_PARAM_ID[] = "ID"; + const char SAM_PARAM_MIN[] = "MIN"; + const char SAM_PARAM_MAX[] = "MAX"; + const char SAM_PARAM_STYLE[] = "STYLE"; + const char SAM_PARAM_ID[] = "ID"; const char SAM_PARAM_SILENT[] = "SILENT"; - const char SAM_PARAM_DESTINATION[] = "DESTINATION"; + const char SAM_PARAM_DESTINATION[] = "DESTINATION"; const char SAM_PARAM_NAME[] = "NAME"; - const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; + const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE"; const char SAM_PARAM_SIZE[] = "SIZE"; - const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; + const char SAM_VALUE_TRANSIENT[] = "TRANSIENT"; const char SAM_VALUE_STREAM[] = "STREAM"; const char SAM_VALUE_DATAGRAM[] = "DATAGRAM"; - const char SAM_VALUE_RAW[] = "RAW"; - const char SAM_VALUE_TRUE[] = "true"; + const char SAM_VALUE_RAW[] = "RAW"; + const char SAM_VALUE_TRUE[] = "true"; const char SAM_VALUE_FALSE[] = "false"; - const char SAM_VALUE_HOST[] = "HOST"; - const char SAM_VALUE_PORT[] = "PORT"; + const char SAM_VALUE_HOST[] = "HOST"; + const char SAM_VALUE_PORT[] = "PORT"; enum SAMSocketType { @@ -79,8 +79,8 @@ namespace client public: SAMSocket (SAMBridge& owner); - ~SAMSocket (); - void CloseStream (); // TODO: implement it better + ~SAMSocket (); + void CloseStream (); // TODO: implement it better boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; }; void ReceiveHandshake (); @@ -89,16 +89,16 @@ namespace client private: - void Terminate (); + void Terminate (); void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void SendMessageReply (const char * msg, size_t len, bool close); + void SendMessageReply (const char * msg, size_t len, bool close); void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close); void Receive (); void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); - void I2PReceive (); + void I2PReceive (); void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred); void HandleI2PAccept (std::shared_ptr stream); void HandleWriteI2PData (const boost::system::error_code& ecode); @@ -109,8 +109,8 @@ namespace client void ProcessStreamAccept (char * buf, size_t len); void ProcessDestGenerate (); void ProcessNamingLookup (char * buf, size_t len); - void SendI2PError(const std::string & msg); - size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 + void SendI2PError(const std::string & msg); + size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0 void ExtractParams (char * buf, std::map& params); void Connect (std::shared_ptr remote); @@ -133,13 +133,13 @@ namespace client bool m_IsSilent; std::shared_ptr m_Stream; std::shared_ptr m_Session; - }; + }; struct SAMSession { std::shared_ptr localDestination; std::list > m_Sockets; - std::shared_ptr UDPEndpoint; + std::shared_ptr UDPEndpoint; std::mutex m_SocketsMutex; /** safely add a socket to this session */ @@ -163,8 +163,8 @@ namespace client } return l; } - - SAMSession (std::shared_ptr dest); + + SAMSession (std::shared_ptr dest); ~SAMSession (); void CloseStreams (); @@ -179,15 +179,15 @@ namespace client void Start (); void Stop (); - + boost::asio::io_service& GetService () { return m_Service; }; std::shared_ptr CreateSession (const std::string& id, const std::string& destination, // empty string means transient const std::map * params); void CloseSession (const std::string& id); std::shared_ptr FindSession (const std::string& id) const; - /** send raw data to remote endpoint from our UDP Socket */ - void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); + /** send raw data to remote endpoint from our UDP Socket */ + void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); private: @@ -202,7 +202,7 @@ namespace client private: bool m_IsRunning; - std::thread * m_Thread; + std::thread * m_Thread; boost::asio::io_service m_Service; boost::asio::ip::tcp::acceptor m_Acceptor; boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint; @@ -215,9 +215,8 @@ namespace client // for HTTP const decltype(m_Sessions)& GetSessions () const { return m_Sessions; }; - }; + }; } } #endif - From cb6a1bfb1d866017f5bf764523a52d818e5ab7f5 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 31 Jan 2017 11:20:16 -0500 Subject: [PATCH 26/30] unindent --- SAM.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SAM.cpp b/SAM.cpp index 3d51041a..a85c766d 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -505,14 +505,14 @@ namespace client shared_from_this (), std::placeholders::_1, ident)); } else - { - LogPrint (eLogError, "SAM: naming failed, unknown address ", name); + { + LogPrint (eLogError, "SAM: naming failed, unknown address ", name); #ifdef _MSC_VER - size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #else - size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); + size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str()); #endif - SendMessageReply (m_Buffer, len, false); + SendMessageReply (m_Buffer, len, false); } } From 764b8ab7a56d2c2adbb0d2adec5e6964034d5503 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 31 Jan 2017 11:22:28 -0500 Subject: [PATCH 27/30] wrong param --- SAM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SAM.cpp b/SAM.cpp index a85c766d..8fcd905b 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -742,7 +742,7 @@ namespace client data[3] = ' '; memcpy(data+4, base64.c_str(), bsz); data[4+bsz] = '\n'; - memcpy(data+4+bsz+1, buf, sz); + memcpy(data+4+bsz+1, buf, len); // send to remote endpoint m_Owner.SendTo(data, sz, ep); delete [] buf; From 925c51420db6c2284ddff6992467e6e9ed5a46e2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 31 Jan 2017 11:32:50 -0500 Subject: [PATCH 28/30] use correct format --- SAM.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/SAM.cpp b/SAM.cpp index 8fcd905b..19138d55 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -733,16 +733,15 @@ namespace client { // udp forward enabled size_t bsz = base64.size(); - size_t sz = 4 + bsz + 1 + len; + size_t sz = bsz + 1 + len; // build datagram body uint8_t * data = new uint8_t[sz]; - data[0] = '3'; - data[1] = '.'; - data[2] = '0'; - data[3] = ' '; - memcpy(data+4, base64.c_str(), bsz); - data[4+bsz] = '\n'; - memcpy(data+4+bsz+1, buf, len); + // Destination + memcpy(data, base64.c_str(), bsz); + // linefeed + data[bsz] = '\n'; + // Payload + memcpy(data+bsz+1, buf, len); // send to remote endpoint m_Owner.SendTo(data, sz, ep); delete [] buf; From ace16d473f9c7a2eda1b0e6691f11ad2cfc9ba3b Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 31 Jan 2017 11:55:57 -0500 Subject: [PATCH 29/30] fix --- SAM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SAM.cpp b/SAM.cpp index 19138d55..d89750f2 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -744,7 +744,7 @@ namespace client memcpy(data+bsz+1, buf, len); // send to remote endpoint m_Owner.SendTo(data, sz, ep); - delete [] buf; + delete [] data; } else { From 0b46495afd0c97e5218ad833e8492b27b02340be Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 31 Jan 2017 12:12:39 -0500 Subject: [PATCH 30/30] i2p.router.net.tunnels.successrate --- I2PControl.cpp | 8 ++++++++ I2PControl.h | 1 + 2 files changed, 9 insertions(+) diff --git a/I2PControl.cpp b/I2PControl.cpp index 4adc6e07..3a65a11c 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -79,6 +79,8 @@ namespace client m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S; m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler; m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler; + m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] = +&I2PControlService::TunnelsSuccessRateHandler; m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes; m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes; @@ -427,6 +429,12 @@ namespace client InsertParam (results, "i2p.router.net.tunnels.participating", transit); } + void I2PControlService::TunnelsSuccessRateHandler (std::ostringstream& results) + { + int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate (); + InsertParam (results, "i2p.router.net.tunnels.successrate", rate); + } + void I2PControlService::InboundBandwidth1S (std::ostringstream& results) { double bw = i2p::transport::transports.GetInBandwidth (); diff --git a/I2PControl.h b/I2PControl.h index 047c2fe2..5d81c8f6 100644 --- a/I2PControl.h +++ b/I2PControl.h @@ -81,6 +81,7 @@ namespace client void NetDbActivePeersHandler (std::ostringstream& results); void NetStatusHandler (std::ostringstream& results); void TunnelsParticipatingHandler (std::ostringstream& results); + void TunnelsSuccessRateHandler (std::ostringstream& results); void InboundBandwidth1S (std::ostringstream& results); void OutboundBandwidth1S (std::ostringstream& results); void NetTotalReceivedBytes (std::ostringstream& results);