From aac1141ca6884bd4693127f800cc911c81c5d359 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 11 Feb 2018 06:05:41 -0500 Subject: [PATCH 01/17] fix issue #1107 --- libi2pd/HTTP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index 24e55457..9bca11d7 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -84,7 +84,7 @@ namespace http { pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */ if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) { std::size_t delim = url.find(':', pos_p); - if (delim != std::string::npos && delim < pos_c) { + if (delim && delim != std::string::npos && delim < pos_c) { user = url.substr(pos_p, delim - pos_p); delim += 1; pass = url.substr(delim, pos_c - delim); From c8de7aa23c5a17c35520e7606459da4e9e770111 Mon Sep 17 00:00:00 2001 From: "mewmew@i2p" Date: Tue, 24 Apr 2018 01:40:12 +0800 Subject: [PATCH 02/17] qt now statically compiles for win32 --- qt/i2pd_qt/ServerTunnelPane.cpp | 18 ------------------ qt/i2pd_qt/ServerTunnelPane.h | 13 ------------- qt/i2pd_qt/TunnelConfig.cpp | 3 +-- qt/i2pd_qt/TunnelConfig.h | 6 ------ qt/i2pd_qt/i2pd_qt.pro | 23 ++++++++++++++++++++--- qt/i2pd_qt/mainwindow.h | 4 ---- 6 files changed, 21 insertions(+), 46 deletions(-) diff --git a/qt/i2pd_qt/ServerTunnelPane.cpp b/qt/i2pd_qt/ServerTunnelPane.cpp index 029a3ea2..bc6389a9 100644 --- a/qt/i2pd_qt/ServerTunnelPane.cpp +++ b/qt/i2pd_qt/ServerTunnelPane.cpp @@ -204,24 +204,6 @@ int ServerTunnelPane::appendServerTunnelForm( horizontalLayout_2->addItem(horizontalSpacer); tunnelGridLayout->addLayout(horizontalLayout_2); } - { - uint32_t maxConns = tunnelConfig->getmaxConns(); - QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); - horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2")); - ui.maxConnsLabel = new QLabel(gridLayoutWidget_2); - maxConnsLabel->setObjectName(QStringLiteral("maxConnsLabel")); - horizontalLayout_2->addWidget(maxConnsLabel); - ui.maxConnsLineEdit = new QLineEdit(gridLayoutWidget_2); - maxConnsLineEdit->setObjectName(QStringLiteral("maxConnsLineEdit")); - maxConnsLineEdit->setText(QString::number(maxConns)); - maxConnsLineEdit->setMaximumWidth(80); - QObject::connect(maxConnsLineEdit, SIGNAL(textChanged(const QString &)), - this, SLOT(updated())); - horizontalLayout_2->addWidget(maxConnsLineEdit); - QSpacerItem * horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - horizontalLayout_2->addItem(horizontalSpacer); - tunnelGridLayout->addLayout(horizontalLayout_2); - } { std::string address = tunnelConfig->getaddress(); QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); diff --git a/qt/i2pd_qt/ServerTunnelPane.h b/qt/i2pd_qt/ServerTunnelPane.h index 556c8473..0a07267b 100644 --- a/qt/i2pd_qt/ServerTunnelPane.h +++ b/qt/i2pd_qt/ServerTunnelPane.h @@ -81,10 +81,6 @@ private: QLabel * addressLabel; QLineEdit * addressLineEdit; - //maxConns - QLabel * maxConnsLabel; - QLineEdit * maxConnsLineEdit; - //gzip QCheckBox * gzipCheckBox; @@ -109,7 +105,6 @@ private: hostOverrideLabel->setText(QApplication::translate("srvTunForm", "Host override:", 0)); webIRCPassLabel->setText(QApplication::translate("srvTunForm", "WebIRC password:", 0)); addressLabel->setText(QApplication::translate("srvTunForm", "Address:", 0)); - maxConnsLabel->setText(QApplication::translate("srvTunForm", "Max connections:", 0)); gzipCheckBox->setText(QApplication::translate("srvTunForm", "GZip", 0)); isUniqueLocalCheckBox->setText(QApplication::translate("srvTunForm", "Is unique local", 0)); @@ -152,14 +147,6 @@ protected: stc->setaddress(addressLineEdit->text().toStdString()); - auto mcStr=maxConnsLineEdit->text(); - uint32_t mcInt=(uint32_t)mcStr.toInt(&ok); - if(!ok){ - highlightWrongInput(QApplication::tr("Bad maxConns, must be int.")+" "+cannotSaveSettings,maxConnsLineEdit); - return false; - } - stc->setmaxConns(mcInt); - stc->setgzip(gzipCheckBox->isChecked()); stc->setisUniqueLocal(isUniqueLocalCheckBox->isChecked()); diff --git a/qt/i2pd_qt/TunnelConfig.cpp b/qt/i2pd_qt/TunnelConfig.cpp index 81216c0b..e4354b62 100644 --- a/qt/i2pd_qt/TunnelConfig.cpp +++ b/qt/i2pd_qt/TunnelConfig.cpp @@ -48,7 +48,6 @@ void ServerTunnelConfig::saveToStringStream(std::stringstream& out) { << "enableuniquelocal=" << (isUniqueLocal?"true":"false") << "\n" << "address=" << address << "\n" << "hostoverride=" << hostOverride << "\n" - << "webircpassword=" << webircpass << "\n" - << "maxconns=" << maxConns << "\n"; + << "webircpassword=" << webircpass << "\n"; } diff --git a/qt/i2pd_qt/TunnelConfig.h b/qt/i2pd_qt/TunnelConfig.h index c714a4f5..58a1fa0b 100644 --- a/qt/i2pd_qt/TunnelConfig.h +++ b/qt/i2pd_qt/TunnelConfig.h @@ -148,7 +148,6 @@ public: std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); - uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN); std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1"); bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); # * inport -- optional, i2p service port, if unset - the same as 'port' @@ -170,7 +169,6 @@ class ServerTunnelConfig : public TunnelConfig { std::string webircpass; bool gzip; i2p::data::SigningKeyType sigType; - uint32_t maxConns; std::string address; bool isUniqueLocal; public: @@ -184,7 +182,6 @@ public: std::string webircpass_, bool gzip_, i2p::data::SigningKeyType sigType_, - uint32_t maxConns_, std::string address_, bool isUniqueLocal_): TunnelConfig(name_, type_, i2cpParameters_), host(host_), @@ -196,7 +193,6 @@ public: webircpass(webircpass_), gzip(gzip_), sigType(sigType_), - maxConns(maxConns_), address(address_), isUniqueLocal(isUniqueLocal_) {} std::string& gethost(){return host;} @@ -208,7 +204,6 @@ public: std::string& getwebircpass(){return webircpass;} bool getgzip(){return gzip;} i2p::data::SigningKeyType getsigType(){return sigType;} - uint32_t getmaxConns(){return maxConns;} std::string& getaddress(){return address;} bool getisUniqueLocal(){return isUniqueLocal;} void sethost(const std::string& host_){host=host_;} @@ -220,7 +215,6 @@ public: void setwebircpass(const std::string& webircpass_){webircpass=webircpass_;} void setgzip(bool gzip_){gzip=gzip_;} void setsigType(i2p::data::SigningKeyType sigType_){sigType=sigType_;} - void setmaxConns(uint32_t maxConns_){maxConns=maxConns_;} void setaddress(const std::string& address_){address=address_;} void setisUniqueLocal(bool isUniqueLocal_){isUniqueLocal=isUniqueLocal_;} virtual void saveToStringStream(std::stringstream& out); diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 941dfff3..b46cfa38 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -272,10 +272,27 @@ linux:!android { LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc } -windows:!android { +windows { message("Using Windows settings") - DEFINES += BOOST_USE_WINDOWS_H WINDOWS - LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc + DEFINES += BOOST_USE_WINDOWS_H WINDOWS _WINDOWS WIN32_LEAN_AND_MEAN MINIUPNP_STATICLIB + DEFINES -= UNICODE _UNICODE + BOOST_SUFFIX = -mt + QMAKE_LDFLAGS = -s -Wl,-rpath,/usr/local/lib -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows + + LIBS = -lminiupnpc \ + -lboost_system$$BOOST_SUFFIX \ + -lboost_date_time$$BOOST_SUFFIX \ + -lboost_filesystem$$BOOST_SUFFIX \ + -lboost_program_options$$BOOST_SUFFIX \ + -lssl \ + -lcrypto \ + -lz \ + -lwsock32 \ + -lws2_32 \ + -lgdi32 \ + -liphlpapi \ + -lstdc++ \ + -lpthread } !android:!symbian:!maemo5:!simulator { diff --git a/qt/i2pd_qt/mainwindow.h b/qt/i2pd_qt/mainwindow.h index cac97a1f..c5f0c902 100644 --- a/qt/i2pd_qt/mainwindow.h +++ b/qt/i2pd_qt/mainwindow.h @@ -628,7 +628,6 @@ private: std::string webircpass = ""; bool gzip = true; i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256; - uint32_t maxConns = i2p::stream::DEFAULT_MAX_CONNS_PER_MIN; std::string address = "127.0.0.1"; bool isUniqueLocal = true; @@ -646,7 +645,6 @@ private: webircpass, gzip, sigType, - maxConns, address, isUniqueLocal); @@ -734,7 +732,6 @@ private: std::string webircpass = section.second.get (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, ""); bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true); i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); - uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN); std::string address = section.second.get (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1"); bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true); @@ -769,7 +766,6 @@ private: webircpass, gzip, sigType, - maxConns, address, isUniqueLocal); } From a2b3ee53e085bf40b35d49629efe38ab1e349daa Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 23 Apr 2018 14:39:46 -0400 Subject: [PATCH 03/17] fixed build error --- daemon/Daemon.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/daemon/Daemon.h b/daemon/Daemon.h index f3e72904..4491d303 100644 --- a/daemon/Daemon.h +++ b/daemon/Daemon.h @@ -64,7 +64,18 @@ namespace util DaemonWin32 ():isGraceful(false) {} }; - +#elif defined(ANDROID) +#define Daemon i2p::util::DaemonAndroid::Instance() + // dummy, invoked from android/jni/DaemonAndroid.* + class DaemonAndroid: public i2p::util::Daemon_Singleton + { + public: + static DaemonAndroid& Instance() + { + static DaemonAndroid instance; + return instance; + } + }; #else #define Daemon i2p::util::DaemonLinux::Instance() class DaemonLinux : public Daemon_Singleton From 396cba7339b9dda8f9f13c4743bdfc216c0b4658 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Tue, 24 Apr 2018 03:19:02 +0300 Subject: [PATCH 04/17] fix static building on windows, add resource files (closes #1163) --- qt/i2pd_qt/i2pd.qrc | 7 +++-- qt/i2pd_qt/i2pd.rc | 32 +++++++++++++++++++++ qt/i2pd_qt/i2pd_qt.pro | 4 ++- qt/i2pd_qt/mainwindow.cpp | 2 +- qt/i2pd_qt/resources/icons/mask.ico | Bin 0 -> 156564 bytes qt/i2pd_qt/{ => resources}/images/icon.png | Bin 6 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 qt/i2pd_qt/i2pd.rc create mode 100644 qt/i2pd_qt/resources/icons/mask.ico rename qt/i2pd_qt/{ => resources}/images/icon.png (100%) diff --git a/qt/i2pd_qt/i2pd.qrc b/qt/i2pd_qt/i2pd.qrc index 2abdeb05..4e5523e9 100644 --- a/qt/i2pd_qt/i2pd.qrc +++ b/qt/i2pd_qt/i2pd.qrc @@ -1,5 +1,6 @@ - - images/icon.png - + + resources/icons/mask.ico + resources/images/icon.png + diff --git a/qt/i2pd_qt/i2pd.rc b/qt/i2pd_qt/i2pd.rc new file mode 100644 index 00000000..bebdf1d6 --- /dev/null +++ b/qt/i2pd_qt/i2pd.rc @@ -0,0 +1,32 @@ +IDI_ICON1 ICON DISCARDABLE "resources/icons/mask.ico" + +#include // needed for VERSIONINFO +#include "../../libi2pd/version.h" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION I2PD_VERSION_MAJOR,I2PD_VERSION_MINOR,I2PD_VERSION_MICRO,I2PD_VERSION_PATCH +PRODUCTVERSION I2P_VERSION_MAJOR,I2P_VERSION_MINOR,I2P_VERSION_MICRO,I2P_VERSION_PATCH +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "PurpleI2P" + VALUE "FileDescription", "I2Pd Qt" + VALUE "FileVersion", I2PD_VERSION + VALUE "InternalName", "i2pd-qt" + VALUE "LegalCopyright", "Copyright (C) 2013-2018, The PurpleI2P Project" + VALUE "LegalTrademarks1", "Distributed under the BSD 3-Clause software license, see the accompanying file COPYING or https://opensource.org/licenses/BSD-3-Clause." + VALUE "OriginalFilename", "i2pd_qt.exe" + VALUE "ProductName", "i2pd-qt" + VALUE "ProductVersion", I2P_VERSION + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index b46cfa38..21ef6358 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -274,10 +274,12 @@ linux:!android { windows { message("Using Windows settings") + RC_FILE = i2pd.rc DEFINES += BOOST_USE_WINDOWS_H WINDOWS _WINDOWS WIN32_LEAN_AND_MEAN MINIUPNP_STATICLIB DEFINES -= UNICODE _UNICODE BOOST_SUFFIX = -mt - QMAKE_LDFLAGS = -s -Wl,-rpath,/usr/local/lib -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows + QMAKE_CXXFLAGS = -Os + QMAKE_LFLAGS = -s -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows LIBS = -lminiupnpc \ -lboost_system$$BOOST_SUFFIX \ diff --git a/qt/i2pd_qt/mainwindow.cpp b/qt/i2pd_qt/mainwindow.cpp index fc1e5985..c3761764 100644 --- a/qt/i2pd_qt/mainwindow.cpp +++ b/qt/i2pd_qt/mainwindow.cpp @@ -449,7 +449,7 @@ void MainWindow::createTrayIcon() { } void MainWindow::setIcon() { - QIcon icon(":/images/icon.png"); + QIcon icon(":icons/mask"); trayIcon->setIcon(icon); setWindowIcon(icon); diff --git a/qt/i2pd_qt/resources/icons/mask.ico b/qt/i2pd_qt/resources/icons/mask.ico new file mode 100644 index 0000000000000000000000000000000000000000..f5807de5ccae7dae7b7234badc651a7a57b62367 GIT binary patch literal 156564 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%0AO>91EHxccO^29Yysmoh8MthpVdk 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%0A2cpAh53rB_uKqdNWrzK_ zu*)6v_Hm7*`4q)zudm|WsZL|#0(hnbajm@-s@s&nVUH{D=L@6tz(k|Ym=Z77z72mp zL;6`9d%>pziu*lee)%%DAH3zt$Ka3XWXE3otiR6REnQXAxy-N~z9a3tK!OACfBJwc|61be z%8gP#S}(ziP)EUT_G!d#hQ9fU*8#duajRih|1$iuM&W&0dw)Jg?g1&Sz3@BInx%02 zdaw+ZaBA3e_UFh92Q6~p0i)3vevs^F(dj~Q-2%RnEjR4*;#6xKHdkYQ;{VwL*jr80{9T2I zD->pt&qs5f_WL^U^KCNiR~~|LC+58e`sK9oR-Mk>J6&1SiC9UZUrF)_zZCxBQ~AO- zbqZbdxcVOJfJLQE6W0Og;+_x&&=!JWn_T-L`Cd3#`}%R!=coISRBzE6d^pXu1#;s7 zJHBuC+c29m_eROoZ+ocpEhGav6Jt-^M1GW_)bTsXm!imn7kR9^`tns(pX*%Q^2Yle zqMv=#`lMt|{~YAShX>mVIM;t?O7H<2HPD3WSB#(u&tuw~y6Bu8yp?{Lg+kp&qR%<_gl}`Zst@sVzHxIg*P!v^*25vW zinrDno51OwguHM}oN%_n{Ya2p^CuU?rV(|t*H>+nOu zN;%V!Xu0NnPeHR4GCbc?#q-9hE`Kqvi&tbm+@jljgz&&Nw=4UqRjvCyUH#V#;{lIV z6zma7CVJ^~A+ZYG{S>eDMEn6--{5QI2Y+65E7mlUQ~iH~0ctOJ-{F%zRr-cRx$a}g zO#2Y$^oY$>_pwDL++i}9GA&USqQlQ#-+*@p`C0QE_402B52SqnJ}3_d%dNLMWWZyO z`_LyQ=y;t^xLkA7EYAEb@uo+^35d4Il^MD%SodiC8(Pf&Y)%>n{q;cbo_#|iY@T|z zYT}QAhkH0qruoE|Q{IIby@l}OqB>i1et6Gn;72!uoAvkQh8?}c4}>qW-Y9v}Gor4= z@Q25jmxABx7R;+R=gZwb!m)q0!e@>lpX5H|K}-olItuhsk2Ep08bVh{hkj zD0^*+oPR)W;y>)slgUcSKFOC;FV2t~-hywnqvU64_*x*^B|)YbC)d8`rdF5!kJAJb zeEoyd<=Za*VB*`*J?Q$ZZ}JVd9Wn%l++Zf3Q+7GjIUDyugE`U7#XS#tzn`oyfFEqw z)tk+geP~b^GVsjl)JID8w_aG!YzqDJ)!-M8gcA^N2)yK@SO@=V&}eMuYTX;u{&V$n zf`3qGkz(7fFD}0Nrv3|`=^e3P_3PZ&K;4QJiM?Ecn_Y`o>8QI#Ysy*_^2-NTz-m_%PW3oDSTL zQk5V5vNSk&x*72Tel4$U+&5**u5O{R`PqQTn27TL<}b9kOsj)T=nL!teZU6~ySq%= zfF3&&vT+*AiAwm`3gR@|^W?@)VP6P6l4H0b(YvJIP^>T>@Plm&cwE{aeYDO(1==C| z@P)3SF)6*?ke=P-8yFULzVJYJ_@PfwWIavslN&Ybk4}??-DnwI4tOfPLwF(Yt5DUa za=L#)CUj)*en7YZ>6T>3^?N;e%`)|Qs*`gn4IlQ7l*Zg43q605Ln+n16gnqe5Bhnf`djb4) z9fghK6R=zIK_TbQ963>oe){*M618NCuRN%IwrcVI6rt#27sM$zi_IeaHvKd~vko-k z$D}Vx_#nXm;R4_t?Dtk`FLn6-K8w}(x#&SXAmv>(kx7cYSCSR^U#ey5WC=UF^UotZ zTn68})mZaigX}<*Arfzhc;Vm+ybFD!Ow{jZosmI zt4N!k#ZOA4#Py6S)O zm(<LCl!pQr?DduN z_qzGGkxtsrxZv3x1gUx2w1lbKcD*y{?g!W}9(tmG(tQto2YyA9MFL&>v)}(BugHY; z-ok5F!Po!Cb08PB$uu8pIay)xkIW}MyIp^NlpYD+Qo0$lu9P>nd^36L6+g(Cbm4A%H(O_{l?yaPgmY!b09&S!s4&ukA&@D2)c zHzuSkQD)>_;T{`57cq23(+5AZUmjK(@IqIk*|J_)IrsZXk3HRc@x8zMUR6=MJJ=`K zj7NXo3x@UAnamP@(hoM<4nF0XEPC8!g-Z~JGc{)!VyW!E;H6jkC%*qtpUaike&}wo zQ2f_(fsc&4u0KC`=6itl9QU;Bxh`uqd_8qldp}}o^>H1SzTxTVOZv$_&;K*u<>y_A zfl)StE+hLIdHtdfxFsw8<&mD(5)~CSntj@NSO1?YblN>2B|kQLy?c<+Xz|0EK)&IA zK7Q_j^Qs5n<6`8a$2RnOw`q*L?8NtbhP~fAR(@D{y~Fd92ENzbj-S1Q@;1s(#6SDd zWkPzQJUu$-ez%nJ^v{0uO4^6yS%+z8z7uupKlVK9LV^7f$J;-E5t^~BnUS|qv*{2~Trux>fG*m)aG$YG z`b~!;Z-0aGo#K7gMK^Jn9C@k|9j13pB6i!N|xsrmrYf!a{@d!!-&*dIal zx+Wbf4%H>N>==081TeCFhOF6s$%aq0cS>YlrMfMJM3_tF-qp(38MjeD>p#O)D zhfTaaaNqIxK#$nJcC5qO?T-&M*O7JRP0oA=Rn=KpT|=XgYJUP9 zN%?-sM8_L6Qz<`5a*^YmcI0~{lRBnKq@8rho|hpn5qkl*F~Ft{%vYKOVv8_{9Wp%H zBe@XJC9VyfvJOXIA>Sool(*46Iy$AS3we{6!M~1)$afvfV-&h%-A>@f=*d_&Ru^Y) z_2bFzNmyVU@Au<6urJp?C?d>3vY-{H)6pdhUEoz4oGw31NX#D!g&FO3|`;a@O%diQ@itN z^cjA4*aqeCyTf!W--&#eqr!Bx6DK-h;P`G>o^A}jPx)i3Kjn}&iOH~kWH6nGvjeBp zIg^u-`KcVR-}1>X(*@6%F_y2UpGu2Ps-A|5ib&Vn?s}2ie&x+3o9+KyALy!TQ>R{b z<>#)uZg}2(db0YYI`4nrVY09F_6xZoz$@U&|8YB%(MC&(SSu}vtMs}br|}!@q4Vte z8RY>+^4i((ZS`FuXSn1%k0m8F78maDW|W#SCTFA{V1JPezu{k(aOPL1X*8zu zY^(>W6Rr)P%&*^c$3?$?;(NuV+kXog#Tk)L`O1XwD&FI>dA#R=XhBzYruucD;%Dy1 z1Z{O)I=5u`&Dyl$M?)1c8@;Cblidu-Xixd@;xyCBc=r!U4?j(&za9mVukG{o5cTMd zGH>@-MaFG2lr^uWDT`JI%S74WDanSsR7MZL{#&GjT*ABmH9)2MN$i6jY7bSikiTP& zX4zio?*282%c(ZOuJdFE&c|dj&*aT-lKy3t;Q8MmK^J-QxKGg@F< z>11$>R6A*ypsj}=3dEDN>*3GfjlUzm?IS&mYf_=0tWGw+2EcXhYshamT>={M>mh<&%_u7Pcd za=@2;-ZWwQmQ&P`km}|x$aA^*SDpN_wiVq{4s=T_*@xYXY$#Q%^6-LTU@7I?rh%u-zUHW zFHGWcFH7bMx8tMZUSpjVEFqiF#^$z*Z@yzc*VOuYm_l<~ib8j1w%qORT+VnOV$VF7 z&zl~ZL8oAPD33Sp&f<(Wrt^j^$%4K)iq{qZUc(I>r>FixxySAKdbxWLY?J#8ukwY&)gZg2yL5auxa1KJM^T z_`4OW8eY!g%C_R>ao;}fg%K_otjT&^ae0|BJ}D-UQx+#EwAbcyZZFP8{7d+J{sF#U z`>+m)u}~=`TRFnb0Ha5+KYb5ebx$ep{wn(F-c-(QtyQke3xpk~!++~U18n-D6{)ur zdN1jVum3gTEdBwpR}rHXvDgvw^=Me_m4Eh(%!u~z2$v=By5FMQfw8Cd!yeH>9y~tI-xQYserAuthUj~Y;d@1#>5&Z1ZA~Qny}SD*Y_~T2>L|seqd21!e>mfY zz4qAWSAX0e*>L0Ed@X@XG1tFxmgh|2ckkOc!EAF@)S1%y3Lo_+A7=9&-4xT$S;vu5`~L0So9!0-O;r6&-&5SR%$pE@8GYbv zJH>vd6MfPbnb!DSE#hepFN^v!R?ue+Yn(KSvz5%(uKT({yKc{PSsb>)b8?9G!~RN5 zQ6i^%4C4%60>{>>;dQUp`?I)Nn-F92GsK<#oF%s0&l0j)SWGeElOs-LCSsCO`;)f) zi%my-+8O@Ih?6}OzTm%nKjuaL;MTz}g2znN+!vN3RvO~c{L$CNmmhrp9Q-DG8D)T= zt4miHUYcX~!#reOh_RlN=Zf&2e%ah`AO7-0_@!8sSr|)O`(F@$@G}-veHAkWC$aeAB`l+MJ#uU_3>aKEE$MI>q7Ui z6HZ`(e+?UNcz|Mom$?P8xOq3SHUG)bxz2&zo3Ne@y5pz8p@mEd3 z87!>fR?LI15wH1Ez`#8$39-57BTl$;E&;Z_3RS40-La3t9{8R{?YcwcCmOUxZ;rD3 zDwE`d_~dD~Z5#zieRv<22tFY)S(UdnMiujoHNTA|Y<`sm*Wb#bRy>ZjeImklYymfwi|KzrZxCHIOkTIpWQy60G6&DG+)*tO5FxHW%Z*)in&Na|Ebfb0q1C|)U>?3M8D|((Rc)tH6E*2ctJah zUGt2%$5T5~DAxFO_V&{pr`W_Pm>2fgq+j6P7c73`%Pe}u?-8H;E*7)uNtRr;g*9rX ziF-knkik|Mb`G?ie5E5s)~miQk>@mC7|pXlSy&kSv~8QCUiKgO?Cn9D2jcf2mJ7?8 zzDnH7D9*OHuOrTC;?~y&+8?vx5yW19N4!USAI0jXH6eEGAF(c=?QM^tA0AX98=1npUUTDv7vKN;kMwirCywN!K{TqDIfOZ`^-pAU$i!IT&vr0ae<@?03q|N_ewr&4ro|!F7uJ>V6IW==t znV4H#CF0HB#%O(yUH=@5S_T-u?9JowU?<#AAH?Uhu^P>MvF!w3^}015#VOJ=QTLOg z^N7ZFbGM|)8y3GY_3~@}p{=M7J3;#|40MUI=JS>JAO>1D_4(ko(i)7ojeU*U^*z<< zmVJng_2CRI?ob2fX|ipOxQ^)}3)ti-Q`zLnQ`i(&Ia9d#G2g8- zFIvWXgfX9@OPFozzeJdEwp~mC2evu4u|>uWBHsjYIKNC)7S@t2#tG^^rlYl*72PGO`gc8BSnBVgBz{_wC>GvA!0V(f8R z^C|vxWb<#COyj|(Oum>+nd-unzHuy~Zt$G{AIn2z^X)3z?svZ7Rj43YEnc6)0=}h_&^H z7WiUs(5~HIrfPgAm7DosxIAI4hs(5V;N9L1f2=eHQeQ#P!oTV>ME?|c$FhWVe`40w z-yz2OJ=n+YWI^RySYSyz3#r^L();m%`;=WDh<)J${7CI*!`v{kDKd_^>bzOtoa@Ed z+)g;Kt^E^AG8G?4P^Py9sZSUi%`SsMeS+ocoK(*6OYo21N8LV3ROs7$;qPe(U?5{E zD))80>V^lnmQ}kWINRL?LTy)>YEf67va&N?k#R*J7hbG$arbuc9Y>mP6F>(qL@c@k z3xVq=B~*cz@g_@0obl9a_K5gR*xc*IzKB`*DE9Ox5O@ADI}W(uS6Bys&%zsSW+8L0 zW5Kmovmn4u#FG0&9N5|Zq)T66$)3}XBp|*;2(Me_sno@gYy^BJ!J@G!p@{D84ITx_ zi<5Z6mEcjm3_I0t>%lvn$(di6F6gsI!;iEz4F!{Ly5l>S9oPQHPi0*ipa{?QPtmtlvFk9iz|~)!H#oc3U633U+FFK zsH^k=ET;Y@ac#Hn^`Eok?f=0({+x)*5x;}z|M-r-h;R|rbhn5fCyKPMo&G00nBJv! z6Yi7-7)r)kS?m)(fCoG2q2Vd{FN1H>1BJZt$w)z$>)=BUY40c*C(mn?+aq5N#adF% zd%RBk9jZ$O@U%GK`_`oo4B$S5%UChi#Oq4LzL%C<&k_LRv@Q_uFAi(L^yc3yjSpDzoE4^Nuh7>SLnz;+DL2Ehz&TMi~dSC z&sn_tYtFH!K4=83o|4ll!aI#E?EyB7WlG)(R%-|vfM1HqkR6)CBIey7KI0sJ8gHV7 zoOB`K|51zYV%bqMLI2ogK-6aN0ryu4?r(t4a)S+U9)g(8=c?U~KDz+?jTH@ompJd8 z<4=7t3u7ERa|=sc`Up#!{~MMOyM~o(!Uo#ze3p1y@qh_}0UB=_cX3_B{E1!t6yW|T z5sqSJZ#z6o@4vU2^L%?2Z~j9L=e8r6GZfOgJKCClI^#N?cWF&6hE5LYB{n|1 zsS2;JD|Jo14W1orp?M=>xNKvyg>+GVIrv%Xs}k=x77rc}!3njWXg^y2i4LSa(z@ub z{r=&ZYa_w)iNH50PA}>==AJa3Hk`&Y7sT_1<>1908r&X|1ppe1A2dR543}~(aFYAL3q6jfPxE@F zKA=B5tn}NY)Qvsa$(#F2L)Np!=1Uo3K(NKSO^5T9)z1gZV=7EaF=pd}E_PKa2YjvPJDxPQ*lEtEpgo9I`P(ST_*VH!nY(;r&3kpqfWj^ zqi+2H_;W8LosVqJ*xo3351BVq4&iLjaeNs4$N36j+7ux#7xA+%2XE`o^L6V#17Gb2 z;yIvC`oZfzI#0KHZ@GHW>xJsthcdXC*F&FTOO)KU+E*6Ts#JTlP1$n!o0ER`=nulu zm0L+>MfEz}gZedz*KG!#f5h2##186-=5#k99y;wm&SPS(P=47mj3131=E8Sj@@P?J z5hfV5MV$E&;ITvI0rh7Y=l;2$8vgB$9T!+MA<8fzIV@htD$P=st;|7c|dEAAwTJ({-G5-+9pQ!FlKPQ%VUg(>TMufKzeYFkcT*+r8i~ za-;%7gy~-O+O~IS4qz_#MDa;ggPxlh%j@zWEB|QlJfc2;Px@~omAW~2cD!rcpkmL? zPtq%j;57?DcYRMZ9%L{FVi9xKIKsF)Ye4$2C7Ooav~D4W5;80&wd z*t)sA@#PSu9xkuO#e0&_9H`YU{}1?t{rSqO-;6|y5??MssV}PFJ>MO?9)PAglEN8o z41})1S!vmc3!PxGWg0yEgJa|6y2pWU|8qL0TT8mQcx~Kk3gPDYLe(|P3_FgNsu#SZ zp5mbz<+Br`p%;QU=6?f>^*Z+!tT6|$x2zj)e~(i3NnbYEGR!X5-t?5qj;lZNcK5!@ zgxH%jPA6RuUYO1$r(yrw3w-DUFHY14AFY@PfUV#eoarHq{eZq-CGY-KB&Q+y&XBn? zS|MlRx<^iqmu%gof17gcEpHkMXPwqMM0z*@xs#P!F7tN*Dji(Jh zz#rpz{e1BAJ|sRKt-H{_d(Oef8&vd9;9aRd2r&YjsI>5a5?3I=8h@1-n~b;d`&gp93KogKHUr9P}YtQ^x=G z^Ww<~+@jW|$yeXBk6X9#hB0)$N31Z>1ThNj%4D_qJ@WnOZOi>h#pg4@epj1Ql;NIL zKzbUd1i#b=%K1$XU-Y|2zT;P{S%GrS8yaE6K|8}2iH;ZMHLh{lvg0lFlBKmM{-oy7 z8LrNcQXI)>)$#G}fT!{5ZH!q@avRXwPXCeG#AKB8TZfOCO~+EvlR*dT7t>CgJ?$~s z)80w_vQJT9|HS@DqVol*Bkkx#9(Ov|P5Ko69&DWkX< zI}<8JGO^b_rzUmDBygR9!-RMPbl7Be=Q8Y1VhW!T;){-Y(Ran~;PaGH^{7Bx7Sl<+ zj_<=)M3teao=5R%< z@nm2*M9_PIC;MSF=l*VlOhxO^k8%V|2~ej9g*#z8_FS&2e4d}{`21f7t4-!g-t!%@ z8%a@`NMG#P~}FWGAHEg5X}#|=RM+!<#47JJU`MtF!5Z; z!HI_n-QQ&--<_{p_kOmrbk2!%@q!fU0PuJBfRDqVt3dt>j>Fmc))gtPJFmLJID7Uv zq3g)*nDpflSNkyR#lFr{RV}khMyAWjUX5%{h$mSpn14SK4;S-m?v!0OyrQjYpt&{X zL3q4xK>;tp{yyfzqdWi`3tfq-p_63m(8>HZPg&9AkOK{VLke4=7Q}5jO7*YgJzn;O zz0lwX=(mqPHqqTTWhvEb_`Q+x%I4OLsW;v6tbS%`Q5bY!XpIO5k2F%D_NOxf@#aG{ z8dJ~|wJONYuc2~>!>!aRPpN=kfCDRym$ORavM&OI3tN1wS%FSiCST_{p!c0IDQkJl zJBZC~9#*FbN&JlEDSXj_Vb@2^DytgeWa~HWb`Of!maNd-U!*X-3f{^Gknen2r}W%Y zD_FYl`)&AJc%_s#KUTn*uFZsgqm9=m()S%OAT>^gEK@b_uVq2)EWYX{W^KOzaNO#r zJ`O40xQTSELsh!y3f|-MWBhE(XVI7sgDd8%i{wkyjZfzZm2(GQALI7~1Nca^w}MxQ zIKhU5bk5iW+uUE33+BJq3Z7p(<#Zws6+A@bk3weuE!y=K`0NkP;N9Bdd1G==Sm`~9 zE1zM>JKtua9p;T+v*aCb_k>pO_@zx{zLnwtI_nP}+&<{~4dw?+6ZyOu_1e{YvW2n% zo3t_NKH6>3)|B+tNKYVDaN7=DpLbwyJ0K5m)`Qvv`EhTackH+EZBHD?fKFlRj<;Fr zu8#)v25lQ(I-D7td5Cl?obSz7dVQNLxWzjw;62q_DAz1{6!Q5G6S!3J)pEuTnoIuh z-IB!VFDvKWKXHzSvo1BzkxGKCSK8Hkm<_tInNfMnX02k0Ydgg@ByRZ!i-XQ^omS-1HI) zF1?fmly0`$AwfsgR=I;U=mNztn8#bb2vO*>lKGrX&PA8v1i_>pNjT^9c)EbPtP$ePI&x zZeuY9W&WvR{j2$D2MUxGZvkcwlU@e(eU?x@4;4S-v<<;$tUNuqSUGP4`s#Jqs2yrH zZemg5F<#MMrCs{C_tfw+$_FL8F5t;l=#^Xp9Ot9uZaa@sUy&X|mCriN&*w#*WU|ps z-tm@Lf3o8unpJ$NsSA_G2{eQ! zj5R+lh>_Vsid41Nq27B?Z&CM#zGeU3YL;TP+t-qQG1*zsnRMCfVjm|`{T=p4bU*Ey zPeuEKlq=q2DQo`7%8k=ud(|qI)vDk21N?Zb83Erot~thvdaZ2d)mwBc*Tx)o(|$o3 z%vtq$LdBW}?V1mU*PHsi#juf8DHB;rQ9Db*I`4~A<(GidhToUi?T7#`G3%daDOYt3 z>;ca9q(YB4Z(0RlXB+8}^#b?4C(0{Rj`yB&StQ*?#3aLi^^j;r9uTOyG0! zVf)hw_&!W^C0T#7e#=qPd4lfN2Q$^Re=g+XzpBy)vv|x~bJ9HK!t0m}wjN5a=`5gp zt7r!lw&-pq(^;4{B!h)7-VHnH&j-eX^uUW^wlM0OTFvrjY|3=Y@VX4nQ@tbLCn{Q@ z-QdeWTlAZcU@hJ|TfOj=ETQan_$O)(ah;w9 zI~T8b_*;U0^%sRfnJfl24OCzMSsR&eZY%RIS|jSLQvIyBrwL49E^y z@1E9?Cse-(zglm==IZ`rF0Um-5gntN;!eK7NEb>hKMJwZ%X#x}7eI!blD!oA_wNCI z{v+xehu8m7T=U5WkJcEn(}{rIdI0uZpZpcfd&X)Oi#%AE9OnT>c)Rtrq5Uy zp@@(1Q1}@gx(y@shct~d6#{4Jo~!j|v9m7|_2J1DB6f zn>tcCy>;;XJk4r5UzY-BF2Z-dbM_VS4)S$TNAeOjvzdukz;93st5@1_GJ1w^{27rm zSv2g+31^@_kAeLF;TYDI2M$Lk)&8MCaNhy`NgeD4q9W}y@EON>r~&rAH$OsfNp+(! z$9&t9!YAKH`n$A8kPT%qAF>y?^nlLW0?hGT*agMGZis9j2!F6OJ;<`+Hy(`DX11M* z??7jVoPR3Fn7y*1~L|WESCYgiC<$^Hn@IZ5?=O`wPve^78~5|2x=I{@Ti=q#9*`#N!#M@1;t_k zCW8BF;!{%H0slQIyxTRPl|*3~;Yba-HE&S;p;P#8FX-4OfbRkx#mj)P0XPDU0w0O` zN|ZM-SZ0$a3x#1sJIvC|-&Ub%+6{Z&CqXYhGfTDL@myua?j#|1C+v3{ResUsvQ_O@ z3(M9J9Y50ejMnYqb>jDcKSp&SILcZq?Aw`I72SC-nP! zGI+NQPCnMKa?W&iLJa0CY!JT0`8DjEH;$$=nxM)GT&VB(80S9d$`9bL43iFZdZNGEu2?^u<_Sk&XBVPdSht83<)73(*31bO(~7tiTi{Nbx_Jm1L@N=so6 z^If*GylsT%FQEAVKJ7oSF7?qK0=(wWgkz3XpOd|>ZCsQw<(iw`6Baht;oWhMaTsh< zpv(GVk!tR(cyA<~*a>keeb6ky{o#d5ufrs_fX~X`46_e8+3Ftmyf!>{vZ{D?pkGPJ z$@0A>Lp3>GYSnW|#@}sxGBQ;fq zx{J8QIJFZSQHP=Xy-`v|naV*~FeW@%;@waqXhwlbF85x>_jTDe011alaE^bfzK5_ajd! z>2H}SCH*Q&aYO3Jiz)6;qnHu{!Y&hmlr%8vRKqqYEh@ z(nP5Z?{^}la*#U9Lo_R~T(O-PuL=*zmT}n`@OCgLF)?NHBE&Y7rtwA zg&LPc($&6IOh0B@J1GAt+uGHRl(Cl(=8n=AdTjO-0evX{dA~hnj8f9mGUH6dIiRO? zG7yaSPV$g)TeqhQmtJ-vA3-Ns*019wK4IcRE1j{^oO>e z$-R%}pKRN%R-w5qVXQjXATO;DJYLRFxDns*%p8!-$=lW@_yy#*X7a@qHtp!T)bKUw zQ3Knoncy9qnU5bUC0vmzgI(CFDs}VyGgWmdBl&Z(L4Reg;PrmF;BhV9Jcg%1cYyTM zj+J(5zsOFpSY5vw{Ob3>|6O7=4Ap~(MZbeT_}Lu6;|anioL_Ni-)4n<)Y2h!a^6H5 z-q3KNM?(Ycb7R67(Q~uai|&FxMt7yAX?dpPSi4dMQ#SCg19O#L|A@2uxDdZ$PAr?Q zE2^6_TKU4N_2KeO+is51TS{_xV@09TR9hgJ>Iwx@Spnyko+;M{O^2-6A;XZyE3#ui z-+x)|-~LTR;iZ=c&0Ig7V7@>w?j#!=(6k?>*?ka*-_PV{Rb(h;Wr&#({_%BnF3MSz z!BMbV!u(kcKHme-gL$)F>Gd7#d=J3J>kH7muR*u!-cp5oYl_k{Gcj@POG&FbnQhbG z`@sk8ipX8M0{q#xiKfK!pTmEZs5?M#gMPZFNT{RvGoC~AgB1-LJzFr(0xtC^%G*yg z5|zCOJ_!m#^172YymTnF?Maq$$zQ;K-OCc1?mbjvju7SlkUj6q6^yhFPAF8@ltJeI z_iW8<(|A5U{Fz|*lS=`u1{>&?NH1ZIhn1x?-_N4p11dGUjwQvFG8_1i#0$<%ZUCQh z8u``uDn~Ge!#_Y=gKq7ckh|VA9{JPoZ#(le*AkW1*j(-Omr1TPedQA@3UXj8{P4uD zev*aOT`%(66X)N`>P?ZvkJ+0oq;G-G`gZ`QU&2S0%~{sTr0}nFQGvQTs#d#V2kfHX z%#UjCiEe#JlmXc;{Ts&qZ)o>FSR(x9#llZi%(4erc|;NTol$*Fx_0>eyrK{9-bm$~ z%y&&D+eNUGkH@>WLq7Ex_%MBoO`BNy{0HH8?<>dxyYTIo?0!h?Iv^q+m~=w&^4G8i zb)M+29(8^rTT?Znu7skkO{QA1dU$-637K56U$wU7I>bKs62ALH-=M9AOIev`Is6(u z!-8tAVO}}QSU7xv#bDfR@C8kNqDb~aa)9&&yV-n03j_agUzvL0EVP^Ut|15UaJYp_ zm&mqXwcL5b>YC0KE9)05)LrtQX2X_)@Zq-y?SFBWy5Y`Qg6*FL@VV?)vWcmu#WH

EoDk^}n&@p>u+kE@|q(w6}J6OsXwyh8N5f|Pu;67N45via02 zKeEeVHowLyz2p1lDZRcX9jrQ~zXRA4 zS7Hy@4F59lBii)GO2P6F_5+cRJVzIJBr&4y`LfW>A2yk{9$utx|D;^A@UkrB>{y#J zTjS)ta!o4@vi3_u#-qoWjyZz8?=!69hY&krJL&epe@Pi^!w*9qe|sipveLMU`w@8Z zxxD+vOihI!e8JSzX;)m|pj-X;9Bu34rKDrujiIzWf+t)yc(=S0GjY~ZKI z$#N7?T$C%p67l(MW9Up`lei#U=ugOmk*EB7_UjA~NCQjsc zX97-&1^2m)O7A@jl|DU~-o zUKY*O5Nl$)vTfZ8$tn6l7)QsAxL-KmvC zcI>&2Z)aT}@7mQ}yQ@PYy&b2i8(a%UXNSZFUWpBSl674Y$*NAUDo~abi^?P**fOwE zY={z1Y!_muOzKXQ%ep#c?78P*R`%&c_S&bL82j#HoL~x1VEKVAF z@iGq0g63(ud5gU5xfB2ASG4Qg%a#nmzp`}h-UYz<@#cv0Z*Ym4rY2>gS4B>lx-mJ^ z(j?NBd1|9O=D;=L-qYTGf#jFJJhtzF##1j_HihPWy(2sN!`75OFuMKnknF09rpOIW1eaBs6=FJGpHsJQ93QxB=?oqF$z=vQm zXsJZ_l_vcl@+VoCxB5tf((7IL)OY}NXl3g0>xdLKla7D}NSV_}WB5YA7WoF=rguTLuQs{zEETwMrprKQ^W|Q_V$DQS*y|)BJ*9x?Ew-C>A`|Mp>f9>tkLj zYil>7Oeanv4a~?5Q!QG0T+AwVyF{{o7x<%_P#MC|adzPBqSU!wprxJ2cOvb;d65Zg K0(OBOy!U^N3-aIq literal 0 HcmV?d00001 diff --git a/qt/i2pd_qt/images/icon.png b/qt/i2pd_qt/resources/images/icon.png similarity index 100% rename from qt/i2pd_qt/images/icon.png rename to qt/i2pd_qt/resources/images/icon.png From 4643c92d33a1d43861b0aac74dec2f01689d01bf Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 24 Apr 2018 09:45:16 -0400 Subject: [PATCH 05/17] Initial SAM cleanup --- daemon/HTTPServer.cpp | 2 +- daemon/I2PControl.cpp | 2 +- libi2pd/Streaming.cpp | 4 +- libi2pd/Streaming.h | 3 + libi2pd_client/SAM.cpp | 208 ++++++++++++++++++----------------------- libi2pd_client/SAM.h | 52 ++++------- 6 files changed, 118 insertions(+), 153 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 6f884a9b..554ba45d 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -649,7 +649,7 @@ namespace http { s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "
\r\n"; s << "
\r\n"; s << "Streams:
\r\n"; - for (const auto& it: session->ListSockets()) + for (const auto& it: sam->ListSockets(id)) { switch (it->GetSocketType ()) { diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index fcff78cd..6ac87cbb 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -727,7 +727,7 @@ namespace client sam_session.put("name", name); sam_session.put("address", i2p::client::context.GetAddressBook ().ToAddress(ident)); - for (const auto& socket: it.second->ListSockets()) + for (const auto& socket: sam->ListSockets(it.first)) { boost::property_tree::ptree stream; stream.put("type", socket->GetSocketType ()); diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index dd8e3634..e8386b61 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -578,9 +578,7 @@ namespace stream if (m_SentPackets.empty () && m_SendBuffer.IsEmpty ()) // nothing to send { m_Status = eStreamStatusClosed; - // close could be called from another thread so do SendClose from the destination thread - // this is so m_LocalDestination.NewPacket () does not trigger a race condition - m_Service.post(std::bind(&Stream::SendClose, shared_from_this())); + SendClose(); } break; case eStreamStatusClosed: diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 47f99833..7f2598c0 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -165,6 +165,9 @@ namespace stream void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; + void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; + + /** only call close from destination thread, use Stream::AsyncClose for other threads */ void Close (); void Cancel () { m_ReceiveTimer.cancel (); }; diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 05943981..b8a72f0c 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -15,8 +15,8 @@ namespace i2p { namespace client { - SAMSocket::SAMSocket (SAMBridge& owner, std::shared_ptr socket): - m_Owner (owner), m_Socket(socket), m_Timer (m_Owner.GetService ()), + SAMSocket::SAMSocket (SAMBridge& owner): + m_Owner (owner), m_Socket(owner.GetService()), m_Timer (m_Owner.GetService ()), m_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_IsAccepting (false), m_Stream (nullptr) @@ -25,51 +25,18 @@ namespace client SAMSocket::~SAMSocket () { - if(m_Stream) - { - m_Stream->Close (); - m_Stream.reset (); - } - auto Session = m_Owner.FindSession(m_ID); - - switch (m_SocketType) - { - case eSAMSocketTypeSession: - m_Owner.CloseSession (m_ID); - break; - case eSAMSocketTypeStream: - { - if (Session) - Session->DelSocket (this); - break; - } - case eSAMSocketTypeAcceptor: - { - if (Session) - { - Session->DelSocket (this); - if (m_IsAccepting && Session->localDestination) - Session->localDestination->StopAcceptingStreams (); - } - break; - } - default: - ; - } - m_SocketType = eSAMSocketTypeTerminated; - if (m_Socket && m_Socket->is_open()) m_Socket->close (); - m_Socket.reset (); + m_Stream.reset (); + if (m_Socket.is_open()) m_Socket.close (); } void SAMSocket::Terminate (const char* reason) { if(m_Stream) { - m_Stream->Close (); + m_Stream->AsyncClose (); m_Stream.reset (); } auto Session = m_Owner.FindSession(m_ID); - switch (m_SocketType) { case eSAMSocketTypeSession: @@ -77,15 +44,12 @@ namespace client break; case eSAMSocketTypeStream: { - if (Session) - Session->DelSocket (this); break; } case eSAMSocketTypeAcceptor: { if (Session) { - Session->DelSocket (this); if (m_IsAccepting && Session->localDestination) Session->localDestination->StopAcceptingStreams (); } @@ -95,16 +59,15 @@ namespace client ; } m_SocketType = eSAMSocketTypeTerminated; - if (m_Socket && m_Socket->is_open()) m_Socket->close (); - m_Socket.reset (); + if (m_Socket.is_open()) m_Socket.close (); + m_Owner.RemoveSocket(this); } void SAMSocket::ReceiveHandshake () - { - if(m_Socket) - 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)); + { + 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)); } static bool SAMVersionAcceptable(const std::string & ver) @@ -125,7 +88,7 @@ namespace client void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) - { + { LogPrint (eLogError, "SAM: handshake read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: handshake read error"); @@ -184,7 +147,7 @@ namespace client #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 (), + boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (), std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2)); } @@ -199,17 +162,22 @@ namespace client } } + bool SAMSocket::IsSession(const std::string & id) const + { + return id == m_ID; + } + void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) - { + { LogPrint (eLogError, "SAM: handshake reply send error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: handshake reply send error"); } - else if(m_Socket) + else { - m_Socket->async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE), + 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)); } @@ -220,7 +188,7 @@ namespace client LogPrint (eLogDebug, "SAMSocket::SendMessageReply, close=",close?"true":"false", " reason: ", msg); if (!m_IsSilent) - boost::asio::async_write (*m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), + boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (), std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, close)); else @@ -501,7 +469,6 @@ namespace client if(session) { m_SocketType = eSAMSocketTypeStream; - session->AddSocket (shared_from_this ()); m_Stream = session->localDestination->CreateStream (remote); m_Stream->Send ((uint8_t *)m_Buffer, m_BufferOffset); // connect and send m_BufferOffset = 0; @@ -534,7 +501,6 @@ namespace client if (session) { m_SocketType = eSAMSocketTypeAcceptor; - session->AddSocket (shared_from_this ()); if (!session->localDestination->IsAcceptingStreams ()) { m_IsAccepting = true; @@ -704,17 +670,9 @@ namespace client void SAMSocket::Receive () { - if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE) - { - LogPrint (eLogError, "SAM: Buffer is full, terminate"); - Terminate ("Buffer is full"); - return; - } else if (m_Socket) - 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)); - else - LogPrint(eLogError, "SAM: receive with no native socket"); + 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)); } void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -731,15 +689,12 @@ namespace client { bytes_transferred += m_BufferOffset; m_BufferOffset = 0; - auto s = shared_from_this (); m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred, - [s](const boost::system::error_code& ecode) - { - if (!ecode) - s->m_Owner.GetService ().post ([s] { s->Receive (); }); - else - s->m_Owner.GetService ().post ([s] { s->Terminate ("AsyncSend failed"); }); - }); + std::bind(&SAMSocket::HandleStreamSend, shared_from_this(), std::placeholders::_1)); + } + else + { + Terminate("No Stream Remaining"); } } } @@ -773,14 +728,11 @@ namespace client void SAMSocket::WriteI2PDataImmediate(uint8_t * buff, size_t sz) { - if(m_Socket) - boost::asio::async_write ( - *m_Socket, - boost::asio::buffer (buff, sz), - boost::asio::transfer_all(), - std::bind (&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this (), std::placeholders::_1, buff)); // postpone termination - else - LogPrint(eLogError, "SAM: no native socket"); + boost::asio::async_write ( + m_Socket, + boost::asio::buffer (buff, sz), + boost::asio::transfer_all(), + std::bind (&SAMSocket::HandleWriteI2PDataImmediate, shared_from_this (), std::placeholders::_1, buff)); // postpone termination } void SAMSocket::HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff) @@ -858,7 +810,7 @@ namespace client if (session) { // find more pending acceptors - for (auto it: session->ListSockets ()) + for (auto it: m_Owner.ListSockets (m_ID)) if (it->m_SocketType == eSAMSocketTypeAcceptor) { it->m_IsAccepting = true; @@ -930,12 +882,19 @@ namespace client } } - SAMSession::SAMSession (std::shared_ptr dest): + void SAMSocket::HandleStreamSend(const boost::system::error_code & ec) + { + m_Owner.GetService ().post (std::bind( !ec ? &SAMSocket::Receive : &SAMSocket::TerminateClose, shared_from_this())); + } + + SAMSession::SAMSession (SAMBridge & parent, const std::string & id, std::shared_ptr dest): + m_Bridge(parent), localDestination (dest), - UDPEndpoint(nullptr) + UDPEndpoint(nullptr), + Name(id) { } - + SAMSession::~SAMSession () { CloseStreams(); @@ -944,15 +903,10 @@ namespace client void SAMSession::CloseStreams () { - std::vector > socks; + for(const auto & itr : m_Bridge.ListSockets(Name)) { - std::lock_guard lock(m_SocketsMutex); - for (const auto& sock : m_Sockets) { - socks.push_back(sock); - } + itr->Terminate(nullptr); } - for (auto & sock : socks ) sock->Terminate("SAMSession::CloseStreams()"); - m_Sockets.clear(); } SAMBridge::SAMBridge (const std::string& address, int port): @@ -1009,12 +963,16 @@ namespace client void SAMBridge::Accept () { - auto native = std::make_shared(m_Service); - auto newSocket = std::make_shared (*this, native); - m_Acceptor.async_accept (*native, std::bind (&SAMBridge::HandleAccept, this, + auto newSocket = std::make_shared(*this); + m_Acceptor.async_accept (newSocket->GetSocket(), std::bind (&SAMBridge::HandleAccept, this, std::placeholders::_1, newSocket)); } + void SAMBridge::RemoveSocket(const SAMSocket * socket) + { + m_OpenSockets.remove_if([socket](const std::shared_ptr & item) -> bool { return item.get() == socket; }); + } + void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) { if (!ecode) @@ -1024,6 +982,7 @@ namespace client if (!ec) { LogPrint (eLogDebug, "SAM: new connection from ", ep); + m_OpenSockets.push_back(socket); socket->ReceiveHandshake (); } else @@ -1066,7 +1025,7 @@ namespace client if (localDestination) { localDestination->Acquire (); - auto session = std::make_shared(localDestination); + auto session = std::make_shared(*this, id, localDestination); std::unique_lock l(m_SessionsMutex); auto ret = m_Sessions.insert (std::make_pair(id, session)); if (!ret.second) @@ -1105,6 +1064,18 @@ namespace client return nullptr; } + std::list > SAMBridge::ListSockets(const std::string & id) const + { + std::list > list; + { + std::unique_lock l(m_SessionsMutex); + for (const auto & itr : m_OpenSockets) + if (itr->IsSession(id)) + list.push_back(itr); + } + return list; + } + void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote) { if(remote) @@ -1127,33 +1098,38 @@ 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); - LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen); - char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' '); - if (sessionID) + if(eol) { - sessionID++; - char * destination = strchr (sessionID, ' '); - if (destination) + *eol = 0; eol++; + 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) { - *destination = 0; destination++; - auto session = FindSession (sessionID); - if (session) + sessionID++; + char * destination = strchr (sessionID, ' '); + if (destination) { - i2p::data::IdentityEx dest; - dest.FromBase64 (destination); - session->localDestination->GetDatagramDestination ()-> - SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ()); + *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"); } else - LogPrint (eLogError, "SAM: Session ", sessionID, " not found"); + LogPrint (eLogError, "SAM: Missing destination key"); } else - LogPrint (eLogError, "SAM: Missing destination key"); + LogPrint (eLogError, "SAM: Missing sessionID"); } else - LogPrint (eLogError, "SAM: Missing sessionID"); + LogPrint(eLogError, "SAM: invalid datagram"); ReceiveDatagram (); } else diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 6ecd14a4..d14e5e39 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -77,24 +77,27 @@ namespace client class SAMBridge; struct SAMSession; - class SAMSocket: public std::enable_shared_from_this + class SAMSocket :public std::enable_shared_from_this { public: typedef boost::asio::ip::tcp::socket Socket_t; - SAMSocket (SAMBridge& owner, std::shared_ptr socket); + SAMSocket (SAMBridge& owner); ~SAMSocket (); - boost::asio::ip::tcp::socket& GetSocket () { return *m_Socket; }; + Socket_t& GetSocket () { return m_Socket; }; void ReceiveHandshake (); void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; }; SAMSocketType GetSocketType () const { return m_SocketType; }; void Terminate (const char* reason); + bool IsSession(const std::string & id) const; + private: - - void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void TerminateClose() { Terminate(nullptr); } + + 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); @@ -128,10 +131,12 @@ namespace client void WriteI2PDataImmediate(uint8_t * ptr, size_t sz); void HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff); + void HandleStreamSend(const boost::system::error_code & ec); + private: SAMBridge& m_Owner; - std::shared_ptr m_Socket; + Socket_t m_Socket; boost::asio::deadline_timer m_Timer; char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1]; size_t m_BufferOffset; @@ -145,34 +150,12 @@ namespace client struct SAMSession { + SAMBridge & m_Bridge; std::shared_ptr localDestination; - std::list > m_Sockets; std::shared_ptr UDPEndpoint; - std::mutex m_SocketsMutex; + std::string Name; - /** safely add a socket to this session */ - void AddSocket(std::shared_ptr sock) { - std::lock_guard lock(m_SocketsMutex); - m_Sockets.push_back(sock); - } - - /** safely remove a socket from this session */ - void DelSocket(SAMSocket * sock) { - std::lock_guard lock(m_SocketsMutex); - m_Sockets.remove_if([sock](const std::shared_ptr s) -> bool { return s.get() == sock; }); - } - - /** get a list holding a copy of all sam sockets from this session */ - std::list > ListSockets() { - std::list > l; - { - std::lock_guard lock(m_SocketsMutex); - for(const auto& sock : m_Sockets ) l.push_back(sock); - } - return l; - } - - SAMSession (std::shared_ptr dest); + SAMSession (SAMBridge & parent, const std::string & name, std::shared_ptr dest); ~SAMSession (); void CloseStreams (); @@ -194,15 +177,19 @@ namespace client void CloseSession (const std::string& id); std::shared_ptr FindSession (const std::string& id) const; + std::list > ListSockets(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); + void RemoveSocket(const SAMSocket * socket); + private: void Run (); void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); void ReceiveDatagram (); void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -217,6 +204,7 @@ namespace client boost::asio::ip::udp::socket m_DatagramSocket; mutable std::mutex m_SessionsMutex; std::map > m_Sessions; + std::list > m_OpenSockets; uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; public: From b7a67b4b03c319fd9fc4432e2ac8751c75d5ff39 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 24 Apr 2018 09:56:24 -0400 Subject: [PATCH 06/17] use refernce not copy --- libi2pd_client/SAM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index b8a72f0c..71b5bea1 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -810,7 +810,7 @@ namespace client if (session) { // find more pending acceptors - for (auto it: m_Owner.ListSockets (m_ID)) + for (auto & it: m_Owner.ListSockets (m_ID)) if (it->m_SocketType == eSAMSocketTypeAcceptor) { it->m_IsAccepting = true; From 60463fdafa97ba68f41430c78bb946ff41bbc1d7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 24 Apr 2018 11:11:48 -0400 Subject: [PATCH 07/17] shut down socket and don't allocate buffer for each write in WriteI2PData --- libi2pd_client/SAM.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 71b5bea1..b0ffda51 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -25,8 +25,7 @@ namespace client SAMSocket::~SAMSocket () { - m_Stream.reset (); - if (m_Socket.is_open()) m_Socket.close (); + m_Stream.reset (); } void SAMSocket::Terminate (const char* reason) @@ -59,7 +58,11 @@ namespace client ; } m_SocketType = eSAMSocketTypeTerminated; - if (m_Socket.is_open()) m_Socket.close (); + if (m_Socket.is_open ()) + { + m_Socket.shutdown (); + m_Socket.close (); + } m_Owner.RemoveSocket(this); } @@ -742,9 +745,11 @@ namespace client void SAMSocket::WriteI2PData(size_t sz) { - uint8_t * sendbuff = new uint8_t[sz]; - memcpy(sendbuff, m_StreamBuffer, sz); - WriteI2PDataImmediate(sendbuff, sz); + boost::asio::async_write ( + m_Socket, + boost::asio::buffer (m_StreamBuffer, sz), + boost::asio::transfer_all(), + std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1)); } void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) @@ -778,7 +783,8 @@ namespace client { WriteI2PData(bytes_transferred); } - I2PReceive(); + else + I2PReceive(); } } } @@ -897,7 +903,6 @@ namespace client SAMSession::~SAMSession () { - CloseStreams(); i2p::client::context.DeleteLocalDestination (localDestination); } From 5f525d0e4344bb5d062bb22be285d62aa58d4e52 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 24 Apr 2018 11:16:15 -0400 Subject: [PATCH 08/17] fix previous commit --- libi2pd_client/SAM.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index b0ffda51..6511d8ba 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -60,7 +60,8 @@ namespace client m_SocketType = eSAMSocketTypeTerminated; if (m_Socket.is_open ()) { - m_Socket.shutdown (); + boost::system::error_code ec; + m_Socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); m_Socket.close (); } m_Owner.RemoveSocket(this); @@ -749,7 +750,7 @@ namespace client m_Socket, boost::asio::buffer (m_StreamBuffer, sz), boost::asio::transfer_all(), - std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1)); + std::bind(&SAMSocket::HandleWriteI2PData, shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred) From 73b3fbc2daefc3258d3d1bd1c16f9fd17d528866 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 24 Apr 2018 11:42:37 -0400 Subject: [PATCH 09/17] wrap m_OpenSockets with mutex --- libi2pd_client/SAM.cpp | 16 ++++++++++------ libi2pd_client/SAM.h | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 6511d8ba..0edc8252 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -33,7 +33,7 @@ namespace client if(m_Stream) { m_Stream->AsyncClose (); - m_Stream.reset (); + m_Stream = nullptr; } auto Session = m_Owner.FindSession(m_ID); switch (m_SocketType) @@ -64,7 +64,7 @@ namespace client m_Socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); m_Socket.close (); } - m_Owner.RemoveSocket(this); + m_Owner.RemoveSocket(shared_from_this()); } void SAMSocket::ReceiveHandshake () @@ -974,9 +974,10 @@ namespace client std::placeholders::_1, newSocket)); } - void SAMBridge::RemoveSocket(const SAMSocket * socket) + void SAMBridge::RemoveSocket(const std::shared_ptr & socket) { - m_OpenSockets.remove_if([socket](const std::shared_ptr & item) -> bool { return item.get() == socket; }); + std::unique_lock lock(m_OpenSocketsMutex); + m_OpenSockets.remove_if([socket](const std::shared_ptr & item) -> bool { return item == socket; }); } void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) @@ -988,7 +989,10 @@ namespace client if (!ec) { LogPrint (eLogDebug, "SAM: new connection from ", ep); - m_OpenSockets.push_back(socket); + { + std::unique_lock l(m_OpenSocketsMutex); + m_OpenSockets.push_back(socket); + } socket->ReceiveHandshake (); } else @@ -1074,7 +1078,7 @@ namespace client { std::list > list; { - std::unique_lock l(m_SessionsMutex); + std::unique_lock l(m_OpenSocketsMutex); for (const auto & itr : m_OpenSockets) if (itr->IsSession(id)) list.push_back(itr); diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index d14e5e39..23cdf170 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -182,7 +182,7 @@ namespace client /** send raw data to remote endpoint from our UDP Socket */ void SendTo(const uint8_t * buf, size_t len, std::shared_ptr remote); - void RemoveSocket(const SAMSocket * socket); + void RemoveSocket(const std::shared_ptr & socket); private: @@ -204,6 +204,7 @@ namespace client boost::asio::ip::udp::socket m_DatagramSocket; mutable std::mutex m_SessionsMutex; std::map > m_Sessions; + mutable std::mutex m_OpenSocketsMutex; std::list > m_OpenSockets; uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; From 623433099b2bd8456adad3f40abeda4ce8dee26d Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 24 Apr 2018 11:50:51 -0400 Subject: [PATCH 10/17] don't use reset --- libi2pd_client/SAM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 0edc8252..41db644d 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -25,7 +25,7 @@ namespace client SAMSocket::~SAMSocket () { - m_Stream.reset (); + m_Stream = nullptr; } void SAMSocket::Terminate (const char* reason) From 1e1e4da14471b190be0997b9be3948183fb7fbd7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 24 Apr 2018 14:02:48 -0400 Subject: [PATCH 11/17] delete buffer --- libi2pd_client/SAM.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index 41db644d..d228e317 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -725,7 +725,10 @@ namespace client WriteI2PDataImmediate(buff, len); } else // no more data + { + delete [] buff; Terminate ("no more data"); + } } } } From 66de7ad0491264f4a0c179dac7e46dfbbd7ce7a1 Mon Sep 17 00:00:00 2001 From: Arm64 plaz Date: Tue, 24 Apr 2018 18:23:40 +0000 Subject: [PATCH 12/17] for first time disable aesenc for arm64 --- Makefile.linux | 7 ++++++- libi2pd/Crypto.cpp | 7 +++++++ libi2pd/Crypto.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Makefile.linux b/Makefile.linux index 4a82591a..2c30bbb0 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -60,7 +60,12 @@ endif ifeq ($(USE_AESNI),yes) #check if AES-NI is supported by CPU ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0) - CPU_FLAGS += -maes -DAESNI + machine := $(shell uname -m) + ifeq ($(machine), aarch64) + CXXFLAGS += -DARM64AES + else + CPU_FLAGS += -maes -DAESNI + endif endif endif diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index 5ba3334d..b0473410 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -594,6 +594,13 @@ namespace crypto // AES #ifdef AESNI + #ifdef ARM64AES + void init_aesenc(void) __attribute__((constructor)){ + + } + + #endif + #define KeyExpansion256(round0,round1) \ "pshufd $0xff, %%xmm2, %%xmm2 \n" \ "movaps %%xmm1, %%xmm4 \n" \ diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h index 6e4ddb3d..859f2d97 100644 --- a/libi2pd/Crypto.h +++ b/libi2pd/Crypto.h @@ -124,6 +124,9 @@ namespace crypto #ifdef AESNI + #ifdef ARM64AES + void init_aesenc(void) __attribute__((constructor)); + #endif class ECBCryptoAESNI { public: From 97127e86dc1d600f667f7c0c0b205371f74eb8a0 Mon Sep 17 00:00:00 2001 From: Sammael <36346388+borned-mind@users.noreply.github.com> Date: Wed, 25 Apr 2018 01:59:11 +0700 Subject: [PATCH 13/17] Delete some for correct compilation --- libi2pd/Crypto.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp index b0473410..8ba99a15 100644 --- a/libi2pd/Crypto.cpp +++ b/libi2pd/Crypto.cpp @@ -595,8 +595,8 @@ namespace crypto // AES #ifdef AESNI #ifdef ARM64AES - void init_aesenc(void) __attribute__((constructor)){ - + void init_aesenc(void){ + // TODO: Implementation } #endif From 72252318142076f5ed0486ee12dc6737ccfa3fd2 Mon Sep 17 00:00:00 2001 From: "mewmew@i2p" Date: Wed, 25 Apr 2018 16:15:40 +0800 Subject: [PATCH 14/17] perfecting qt status page --- daemon/HTTPServer.cpp | 43 ++- daemon/HTTPServer.h | 3 +- qt/i2pd_qt/i2pd_qt.pro | 618 +++++++++++++++++++------------------- qt/i2pd_qt/mainwindow.cpp | 4 +- 4 files changed, 342 insertions(+), 326 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 6f884a9b..12edf276 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -198,7 +198,10 @@ namespace http { s << "ERROR: " << string << "
\r\n"; } - void ShowStatus (std::stringstream& s, bool includeHiddenContent) + void ShowStatus ( + std::stringstream& s, + bool includeHiddenContent, + i2p::http::OutputFormatEnum outputFormat) { s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ()); @@ -245,9 +248,12 @@ namespace http { ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)
\r\n"; s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n"; - s << "

\r\n\r\n

\r\n"; - if(includeHiddenContent) { - s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; + s << "

"; + if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { + s << "\r\n\r\n

\r\n"; + } + if(includeHiddenContent) { + s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; s << "Router Family: " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; s << "Router Caps: " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; s << "Our external address:" << "
\r\n" ; @@ -272,9 +278,12 @@ namespace http { } s << address->host.to_string() << ":" << address->port << "
\r\n"; } - } + } s << "

\r\n
\r\n"; - s << "Routers: " << i2p::data::netdb.GetNumRouters () << " "; + if(outputFormat==OutputFormatEnum::forQtUi) { + s << "
"; + } + s << "Routers: " << i2p::data::netdb.GetNumRouters () << " "; s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; @@ -285,15 +294,17 @@ namespace http { s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); - s << "\r\n"; - s << "
Services
ServiceState
" << "HTTP Proxy" << "
" << "SOCKS Proxy" << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
" << "I2PControl" << "
\r\n"; + if(outputFormat==OutputFormatEnum::forWebConsole) { + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + s << "\r\n"; + bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); + s << "\r\n"; + s << "
Services
ServiceState
" << "HTTP Proxy" << "
" << "SOCKS Proxy" << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
" << "I2PControl" << "
\r\n"; + } } void ShowLocalDestinations (std::stringstream& s) @@ -863,7 +874,7 @@ namespace http { } else if (req.uri.find("cmd=") != std::string::npos) { HandleCommand (req, res, s); } else { - ShowStatus (s, true); + ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); res.add_header("Refresh", "10"); } ShowPageTail (s); diff --git a/daemon/HTTPServer.h b/daemon/HTTPServer.h index 46477dae..a1b82875 100644 --- a/daemon/HTTPServer.h +++ b/daemon/HTTPServer.h @@ -80,7 +80,8 @@ namespace http }; //all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml - void ShowStatus (std::stringstream& s, bool includeHiddenContent); + enum OutputFormatEnum { forWebConsole, forQtUi }; + void ShowStatus (std::stringstream& s, bool includeHiddenContent, OutputFormatEnum outputFormat); void ShowLocalDestinations (std::stringstream& s); void ShowLeasesSets(std::stringstream& s); void ShowTunnels (std::stringstream& s); diff --git a/qt/i2pd_qt/i2pd_qt.pro b/qt/i2pd_qt/i2pd_qt.pro index 21ef6358..12d13a17 100644 --- a/qt/i2pd_qt/i2pd_qt.pro +++ b/qt/i2pd_qt/i2pd_qt.pro @@ -1,308 +1,310 @@ -QT += core gui - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = i2pd_qt -TEMPLATE = app -QMAKE_CXXFLAGS *= -std=c++11 -DEFINES += USE_UPNP - -# change to your own path, where you will store all needed libraries with 'git clone' commands below. -MAIN_PATH = /path/to/libraries - -# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git -# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git -# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git -# git clone https://github.com/PurpleI2P/android-ifaddrs.git -BOOST_PATH = $$MAIN_PATH/Boost-for-Android-Prebuilt -OPENSSL_PATH = $$MAIN_PATH/OpenSSL-for-Android-Prebuilt -MINIUPNP_PATH = $$MAIN_PATH/MiniUPnP-for-Android-Prebuilt -IFADDRS_PATH = $$MAIN_PATH/android-ifaddrs - -# Steps in Android SDK manager: -# 1) Check Extras/Google Support Library https://developer.android.com/topic/libraries/support-library/setup.html -# 2) Check API 11 -# Finally, click Install. - -SOURCES += DaemonQT.cpp mainwindow.cpp \ - ../../libi2pd/api.cpp \ - ../../libi2pd/Base.cpp \ - ../../libi2pd/BloomFilter.cpp \ - ../../libi2pd/Config.cpp \ - ../../libi2pd/CPU.cpp \ - ../../libi2pd/Crypto.cpp \ - ../../libi2pd/CryptoKey.cpp \ - ../../libi2pd/Datagram.cpp \ - ../../libi2pd/Destination.cpp \ - ../../libi2pd/Event.cpp \ - ../../libi2pd/Family.cpp \ - ../../libi2pd/FS.cpp \ - ../../libi2pd/Garlic.cpp \ - ../../libi2pd/Gost.cpp \ - ../../libi2pd/Gzip.cpp \ - ../../libi2pd/HTTP.cpp \ - ../../libi2pd/I2NPProtocol.cpp \ - ../../libi2pd/I2PEndian.cpp \ - ../../libi2pd/Identity.cpp \ - ../../libi2pd/LeaseSet.cpp \ - ../../libi2pd/Log.cpp \ - ../../libi2pd/NetDb.cpp \ - ../../libi2pd/NetDbRequests.cpp \ - ../../libi2pd/NTCPSession.cpp \ - ../../libi2pd/Profiling.cpp \ - ../../libi2pd/Reseed.cpp \ - ../../libi2pd/RouterContext.cpp \ - ../../libi2pd/RouterInfo.cpp \ - ../../libi2pd/Signature.cpp \ - ../../libi2pd/SSU.cpp \ - ../../libi2pd/SSUData.cpp \ - ../../libi2pd/SSUSession.cpp \ - ../../libi2pd/Streaming.cpp \ - ../../libi2pd/Timestamp.cpp \ - ../../libi2pd/TransitTunnel.cpp \ - ../../libi2pd/Transports.cpp \ - ../../libi2pd/Tunnel.cpp \ - ../../libi2pd/TunnelEndpoint.cpp \ - ../../libi2pd/TunnelGateway.cpp \ - ../../libi2pd/TunnelPool.cpp \ - ../../libi2pd/util.cpp \ - ../../libi2pd_client/AddressBook.cpp \ - ../../libi2pd_client/BOB.cpp \ - ../../libi2pd_client/ClientContext.cpp \ - ../../libi2pd_client/HTTPProxy.cpp \ - ../../libi2pd_client/I2CP.cpp \ - ../../libi2pd_client/I2PService.cpp \ - ../../libi2pd_client/I2PTunnel.cpp \ - ../../libi2pd_client/MatchedDestination.cpp \ - ../../libi2pd_client/SAM.cpp \ - ../../libi2pd_client/SOCKS.cpp \ - ../../libi2pd_client/Websocket.cpp \ - ../../libi2pd_client/WebSocks.cpp \ - ClientTunnelPane.cpp \ - MainWindowItems.cpp \ - ServerTunnelPane.cpp \ - SignatureTypeComboboxFactory.cpp \ - TunnelConfig.cpp \ - TunnelPane.cpp \ - ../../daemon/Daemon.cpp \ - ../../daemon/HTTPServer.cpp \ - ../../daemon/i2pd.cpp \ - ../../daemon/I2PControl.cpp \ - ../../daemon/UnixDaemon.cpp \ - ../../daemon/UPnP.cpp \ - textbrowsertweaked1.cpp \ - pagewithbackbutton.cpp \ - widgetlock.cpp \ - widgetlockregistry.cpp - -#qt creator does not handle this well -#SOURCES += $$files(../../libi2pd/*.cpp) -#SOURCES += $$files(../../libi2pd_client/*.cpp) -#SOURCES += $$files(../../daemon/*.cpp) -#SOURCES += $$files(./*.cpp) - -SOURCES -= ../../daemon/UnixDaemon.cpp - -HEADERS += DaemonQT.h mainwindow.h \ - ../../libi2pd/api.h \ - ../../libi2pd/Base.h \ - ../../libi2pd/BloomFilter.h \ - ../../libi2pd/Config.h \ - ../../libi2pd/Crypto.h \ - ../../libi2pd/CryptoKey.h \ - ../../libi2pd/Datagram.h \ - ../../libi2pd/Destination.h \ - ../../libi2pd/Event.h \ - ../../libi2pd/Family.h \ - ../../libi2pd/FS.h \ - ../../libi2pd/Garlic.h \ - ../../libi2pd/Gost.h \ - ../../libi2pd/Gzip.h \ - ../../libi2pd/HTTP.h \ - ../../libi2pd/I2NPProtocol.h \ - ../../libi2pd/I2PEndian.h \ - ../../libi2pd/Identity.h \ - ../../libi2pd/LeaseSet.h \ - ../../libi2pd/LittleBigEndian.h \ - ../../libi2pd/Log.h \ - ../../libi2pd/NetDb.hpp \ - ../../libi2pd/NetDbRequests.h \ - ../../libi2pd/NTCPSession.h \ - ../../libi2pd/Profiling.h \ - ../../libi2pd/Queue.h \ - ../../libi2pd/Reseed.h \ - ../../libi2pd/RouterContext.h \ - ../../libi2pd/RouterInfo.h \ - ../../libi2pd/Signature.h \ - ../../libi2pd/SSU.h \ - ../../libi2pd/SSUData.h \ - ../../libi2pd/SSUSession.h \ - ../../libi2pd/Streaming.h \ - ../../libi2pd/Tag.h \ - ../../libi2pd/Timestamp.h \ - ../../libi2pd/TransitTunnel.h \ - ../../libi2pd/Transports.h \ - ../../libi2pd/TransportSession.h \ - ../../libi2pd/Tunnel.h \ - ../../libi2pd/TunnelBase.h \ - ../../libi2pd/TunnelConfig.h \ - ../../libi2pd/TunnelEndpoint.h \ - ../../libi2pd/TunnelGateway.h \ - ../../libi2pd/TunnelPool.h \ - ../../libi2pd/util.h \ - ../../libi2pd/version.h \ - ../../libi2pd_client/AddressBook.h \ - ../../libi2pd_client/BOB.h \ - ../../libi2pd_client/ClientContext.h \ - ../../libi2pd_client/HTTPProxy.h \ - ../../libi2pd_client/I2CP.h \ - ../../libi2pd_client/I2PService.h \ - ../../libi2pd_client/I2PTunnel.h \ - ../../libi2pd_client/MatchedDestination.h \ - ../../libi2pd_client/SAM.h \ - ../../libi2pd_client/SOCKS.h \ - ../../libi2pd_client/Websocket.h \ - ../../libi2pd_client/WebSocks.h \ - ClientTunnelPane.h \ - MainWindowItems.h \ - ServerTunnelPane.h \ - SignatureTypeComboboxFactory.h \ - TunnelConfig.h \ - TunnelPane.h \ - TunnelsPageUpdateListener.h \ - ../../daemon/Daemon.h \ - ../../daemon/HTTPServer.h \ - ../../daemon/I2PControl.h \ - ../../daemon/UPnP.h \ - textbrowsertweaked1.h \ - pagewithbackbutton.h \ - widgetlock.h \ - widgetlockregistry.h - -INCLUDEPATH += ../../libi2pd -INCLUDEPATH += ../../libi2pd_client -INCLUDEPATH += ../../daemon -INCLUDEPATH += . - -FORMS += mainwindow.ui \ - tunnelform.ui \ - statusbuttons.ui \ - routercommandswidget.ui \ - generalsettingswidget.ui - -LIBS += -lz - -macx { - message("using mac os x target") - BREWROOT=/usr/local - BOOSTROOT=$$BREWROOT/opt/boost - SSLROOT=$$BREWROOT/opt/libressl - UPNPROOT=$$BREWROOT/opt/miniupnpc - INCLUDEPATH += $$BOOSTROOT/include - INCLUDEPATH += $$SSLROOT/include - INCLUDEPATH += $$UPNPROOT/include - LIBS += $$SSLROOT/lib/libcrypto.a - LIBS += $$SSLROOT/lib/libssl.a - LIBS += $$BOOSTROOT/lib/libboost_system.a - LIBS += $$BOOSTROOT/lib/libboost_date_time.a - LIBS += $$BOOSTROOT/lib/libboost_filesystem.a - LIBS += $$BOOSTROOT/lib/libboost_program_options.a - LIBS += $$UPNPROOT/lib/libminiupnpc.a -} - -android { - message("Using Android settings") - DEFINES += ANDROID=1 - DEFINES += __ANDROID__ - - CONFIG += mobility - - MOBILITY = - - INCLUDEPATH += $$BOOST_PATH/boost_1_53_0/include \ - $$OPENSSL_PATH/openssl-1.0.2/include \ - $$MINIUPNP_PATH/miniupnp-2.0/include \ - $$IFADDRS_PATH - DISTFILES += android/AndroidManifest.xml - - ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android - - SOURCES += $$IFADDRS_PATH/ifaddrs.c - HEADERS += $$IFADDRS_PATH/ifaddrs.h - - equals(ANDROID_TARGET_ARCH, armeabi-v7a){ - DEFINES += ANDROID_ARM7A - # http://stackoverflow.com/a/30235934/529442 - LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \ - -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ - -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ - -L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl \ - -L$$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/ -lminiupnpc - - PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \ - $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a - DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include - - ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \ - $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so \ - $$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/libminiupnpc.so - } - - equals(ANDROID_TARGET_ARCH, x86){ - # http://stackoverflow.com/a/30235934/529442 - LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \ - -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ - -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ - -L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl \ - -L$$MINIUPNP_PATH/miniupnp-2.0/x86/lib/ -lminiupnpc - - PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \ - $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a - - DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include - - ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \ - $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so \ - $$MINIUPNP_PATH/miniupnp-2.0/x86/lib/libminiupnpc.so - } -} - -linux:!android { - message("Using Linux settings") - LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc -} - -windows { - message("Using Windows settings") - RC_FILE = i2pd.rc - DEFINES += BOOST_USE_WINDOWS_H WINDOWS _WINDOWS WIN32_LEAN_AND_MEAN MINIUPNP_STATICLIB - DEFINES -= UNICODE _UNICODE - BOOST_SUFFIX = -mt - QMAKE_CXXFLAGS = -Os - QMAKE_LFLAGS = -s -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows - - LIBS = -lminiupnpc \ - -lboost_system$$BOOST_SUFFIX \ - -lboost_date_time$$BOOST_SUFFIX \ - -lboost_filesystem$$BOOST_SUFFIX \ - -lboost_program_options$$BOOST_SUFFIX \ - -lssl \ - -lcrypto \ - -lz \ - -lwsock32 \ - -lws2_32 \ - -lgdi32 \ - -liphlpapi \ - -lstdc++ \ - -lpthread -} - -!android:!symbian:!maemo5:!simulator { - message("Build with a system tray icon") - # see also http://doc.qt.io/qt-4.8/qt-desktop-systray-systray-pro.html for example on wince* - #sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS i2pd_qt.pro resources images - RESOURCES = i2pd.qrc - QT += xml - #INSTALLS += sources -} - +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = i2pd_qt +TEMPLATE = app +QMAKE_CXXFLAGS *= -std=c++11 +DEFINES += USE_UPNP + +# change to your own path, where you will store all needed libraries with 'git clone' commands below. +MAIN_PATH = /path/to/libraries + +# git clone https://github.com/PurpleI2P/Boost-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/OpenSSL-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/MiniUPnP-for-Android-Prebuilt.git +# git clone https://github.com/PurpleI2P/android-ifaddrs.git +BOOST_PATH = $$MAIN_PATH/Boost-for-Android-Prebuilt +OPENSSL_PATH = $$MAIN_PATH/OpenSSL-for-Android-Prebuilt +MINIUPNP_PATH = $$MAIN_PATH/MiniUPnP-for-Android-Prebuilt +IFADDRS_PATH = $$MAIN_PATH/android-ifaddrs + +# Steps in Android SDK manager: +# 1) Check Extras/Google Support Library https://developer.android.com/topic/libraries/support-library/setup.html +# 2) Check API 11 +# Finally, click Install. + +SOURCES += DaemonQT.cpp mainwindow.cpp \ + ../../libi2pd/api.cpp \ + ../../libi2pd/Base.cpp \ + ../../libi2pd/BloomFilter.cpp \ + ../../libi2pd/Config.cpp \ + ../../libi2pd/CPU.cpp \ + ../../libi2pd/Crypto.cpp \ + ../../libi2pd/CryptoKey.cpp \ + ../../libi2pd/Datagram.cpp \ + ../../libi2pd/Destination.cpp \ + ../../libi2pd/Event.cpp \ + ../../libi2pd/Family.cpp \ + ../../libi2pd/FS.cpp \ + ../../libi2pd/Garlic.cpp \ + ../../libi2pd/Gost.cpp \ + ../../libi2pd/Gzip.cpp \ + ../../libi2pd/HTTP.cpp \ + ../../libi2pd/I2NPProtocol.cpp \ + ../../libi2pd/I2PEndian.cpp \ + ../../libi2pd/Identity.cpp \ + ../../libi2pd/LeaseSet.cpp \ + ../../libi2pd/Log.cpp \ + ../../libi2pd/NetDb.cpp \ + ../../libi2pd/NetDbRequests.cpp \ + ../../libi2pd/NTCPSession.cpp \ + ../../libi2pd/Profiling.cpp \ + ../../libi2pd/Reseed.cpp \ + ../../libi2pd/RouterContext.cpp \ + ../../libi2pd/RouterInfo.cpp \ + ../../libi2pd/Signature.cpp \ + ../../libi2pd/SSU.cpp \ + ../../libi2pd/SSUData.cpp \ + ../../libi2pd/SSUSession.cpp \ + ../../libi2pd/Streaming.cpp \ + ../../libi2pd/Timestamp.cpp \ + ../../libi2pd/TransitTunnel.cpp \ + ../../libi2pd/Transports.cpp \ + ../../libi2pd/Tunnel.cpp \ + ../../libi2pd/TunnelEndpoint.cpp \ + ../../libi2pd/TunnelGateway.cpp \ + ../../libi2pd/TunnelPool.cpp \ + ../../libi2pd/util.cpp \ + ../../libi2pd_client/AddressBook.cpp \ + ../../libi2pd_client/BOB.cpp \ + ../../libi2pd_client/ClientContext.cpp \ + ../../libi2pd_client/HTTPProxy.cpp \ + ../../libi2pd_client/I2CP.cpp \ + ../../libi2pd_client/I2PService.cpp \ + ../../libi2pd_client/I2PTunnel.cpp \ + ../../libi2pd_client/MatchedDestination.cpp \ + ../../libi2pd_client/SAM.cpp \ + ../../libi2pd_client/SOCKS.cpp \ + ../../libi2pd_client/Websocket.cpp \ + ../../libi2pd_client/WebSocks.cpp \ + ClientTunnelPane.cpp \ + MainWindowItems.cpp \ + ServerTunnelPane.cpp \ + SignatureTypeComboboxFactory.cpp \ + TunnelConfig.cpp \ + TunnelPane.cpp \ + ../../daemon/Daemon.cpp \ + ../../daemon/HTTPServer.cpp \ + ../../daemon/i2pd.cpp \ + ../../daemon/I2PControl.cpp \ + ../../daemon/UnixDaemon.cpp \ + ../../daemon/UPnP.cpp \ + textbrowsertweaked1.cpp \ + pagewithbackbutton.cpp \ + widgetlock.cpp \ + widgetlockregistry.cpp + +#qt creator does not handle this well +#SOURCES += $$files(../../libi2pd/*.cpp) +#SOURCES += $$files(../../libi2pd_client/*.cpp) +#SOURCES += $$files(../../daemon/*.cpp) +#SOURCES += $$files(./*.cpp) + +SOURCES -= ../../daemon/UnixDaemon.cpp + +HEADERS += DaemonQT.h mainwindow.h \ + ../../libi2pd/api.h \ + ../../libi2pd/Base.h \ + ../../libi2pd/BloomFilter.h \ + ../../libi2pd/Config.h \ + ../../libi2pd/Crypto.h \ + ../../libi2pd/CryptoKey.h \ + ../../libi2pd/Datagram.h \ + ../../libi2pd/Destination.h \ + ../../libi2pd/Event.h \ + ../../libi2pd/Family.h \ + ../../libi2pd/FS.h \ + ../../libi2pd/Garlic.h \ + ../../libi2pd/Gost.h \ + ../../libi2pd/Gzip.h \ + ../../libi2pd/HTTP.h \ + ../../libi2pd/I2NPProtocol.h \ + ../../libi2pd/I2PEndian.h \ + ../../libi2pd/Identity.h \ + ../../libi2pd/LeaseSet.h \ + ../../libi2pd/LittleBigEndian.h \ + ../../libi2pd/Log.h \ + ../../libi2pd/NetDb.hpp \ + ../../libi2pd/NetDbRequests.h \ + ../../libi2pd/NTCPSession.h \ + ../../libi2pd/Profiling.h \ + ../../libi2pd/Queue.h \ + ../../libi2pd/Reseed.h \ + ../../libi2pd/RouterContext.h \ + ../../libi2pd/RouterInfo.h \ + ../../libi2pd/Signature.h \ + ../../libi2pd/SSU.h \ + ../../libi2pd/SSUData.h \ + ../../libi2pd/SSUSession.h \ + ../../libi2pd/Streaming.h \ + ../../libi2pd/Tag.h \ + ../../libi2pd/Timestamp.h \ + ../../libi2pd/TransitTunnel.h \ + ../../libi2pd/Transports.h \ + ../../libi2pd/TransportSession.h \ + ../../libi2pd/Tunnel.h \ + ../../libi2pd/TunnelBase.h \ + ../../libi2pd/TunnelConfig.h \ + ../../libi2pd/TunnelEndpoint.h \ + ../../libi2pd/TunnelGateway.h \ + ../../libi2pd/TunnelPool.h \ + ../../libi2pd/util.h \ + ../../libi2pd/version.h \ + ../../libi2pd_client/AddressBook.h \ + ../../libi2pd_client/BOB.h \ + ../../libi2pd_client/ClientContext.h \ + ../../libi2pd_client/HTTPProxy.h \ + ../../libi2pd_client/I2CP.h \ + ../../libi2pd_client/I2PService.h \ + ../../libi2pd_client/I2PTunnel.h \ + ../../libi2pd_client/MatchedDestination.h \ + ../../libi2pd_client/SAM.h \ + ../../libi2pd_client/SOCKS.h \ + ../../libi2pd_client/Websocket.h \ + ../../libi2pd_client/WebSocks.h \ + ClientTunnelPane.h \ + MainWindowItems.h \ + ServerTunnelPane.h \ + SignatureTypeComboboxFactory.h \ + TunnelConfig.h \ + TunnelPane.h \ + TunnelsPageUpdateListener.h \ + ../../daemon/Daemon.h \ + ../../daemon/HTTPServer.h \ + ../../daemon/I2PControl.h \ + ../../daemon/UPnP.h \ + textbrowsertweaked1.h \ + pagewithbackbutton.h \ + widgetlock.h \ + widgetlockregistry.h \ + i2pd.rc \ + i2pd.rc + +INCLUDEPATH += ../../libi2pd +INCLUDEPATH += ../../libi2pd_client +INCLUDEPATH += ../../daemon +INCLUDEPATH += . + +FORMS += mainwindow.ui \ + tunnelform.ui \ + statusbuttons.ui \ + routercommandswidget.ui \ + generalsettingswidget.ui + +LIBS += -lz + +macx { + message("using mac os x target") + BREWROOT=/usr/local + BOOSTROOT=$$BREWROOT/opt/boost + SSLROOT=$$BREWROOT/opt/libressl + UPNPROOT=$$BREWROOT/opt/miniupnpc + INCLUDEPATH += $$BOOSTROOT/include + INCLUDEPATH += $$SSLROOT/include + INCLUDEPATH += $$UPNPROOT/include + LIBS += $$SSLROOT/lib/libcrypto.a + LIBS += $$SSLROOT/lib/libssl.a + LIBS += $$BOOSTROOT/lib/libboost_system.a + LIBS += $$BOOSTROOT/lib/libboost_date_time.a + LIBS += $$BOOSTROOT/lib/libboost_filesystem.a + LIBS += $$BOOSTROOT/lib/libboost_program_options.a + LIBS += $$UPNPROOT/lib/libminiupnpc.a +} + +android { + message("Using Android settings") + DEFINES += ANDROID=1 + DEFINES += __ANDROID__ + + CONFIG += mobility + + MOBILITY = + + INCLUDEPATH += $$BOOST_PATH/boost_1_53_0/include \ + $$OPENSSL_PATH/openssl-1.0.2/include \ + $$MINIUPNP_PATH/miniupnp-2.0/include \ + $$IFADDRS_PATH + DISTFILES += android/AndroidManifest.xml + + ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android + + SOURCES += $$IFADDRS_PATH/ifaddrs.c + HEADERS += $$IFADDRS_PATH/ifaddrs.h + + equals(ANDROID_TARGET_ARCH, armeabi-v7a){ + DEFINES += ANDROID_ARM7A + # http://stackoverflow.com/a/30235934/529442 + LIBS += -L$$BOOST_PATH/boost_1_53_0/armeabi-v7a/lib \ + -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ + -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ + -L$$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/ -lcrypto -lssl \ + -L$$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/ -lminiupnpc + + PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl.a + DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + + ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/armeabi-v7a/lib/libssl_1_0_0.so \ + $$MINIUPNP_PATH/miniupnp-2.0/armeabi-v7a/lib/libminiupnpc.so + } + + equals(ANDROID_TARGET_ARCH, x86){ + # http://stackoverflow.com/a/30235934/529442 + LIBS += -L$$BOOST_PATH/boost_1_53_0/x86/lib \ + -lboost_system-gcc-mt-1_53 -lboost_date_time-gcc-mt-1_53 \ + -lboost_filesystem-gcc-mt-1_53 -lboost_program_options-gcc-mt-1_53 \ + -L$$OPENSSL_PATH/openssl-1.0.2/x86/lib/ -lcrypto -lssl \ + -L$$MINIUPNP_PATH/miniupnp-2.0/x86/lib/ -lminiupnpc + + PRE_TARGETDEPS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto.a \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl.a + + DEPENDPATH += $$OPENSSL_PATH/openssl-1.0.2/include + + ANDROID_EXTRA_LIBS += $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libcrypto_1_0_0.so \ + $$OPENSSL_PATH/openssl-1.0.2/x86/lib/libssl_1_0_0.so \ + $$MINIUPNP_PATH/miniupnp-2.0/x86/lib/libminiupnpc.so + } +} + +linux:!android { + message("Using Linux settings") + LIBS += -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread -lminiupnpc +} + +windows { + message("Using Windows settings") + RC_FILE = i2pd.rc + DEFINES += BOOST_USE_WINDOWS_H WINDOWS _WINDOWS WIN32_LEAN_AND_MEAN MINIUPNP_STATICLIB + DEFINES -= UNICODE _UNICODE + BOOST_SUFFIX = -mt + QMAKE_CXXFLAGS = -Os + QMAKE_LFLAGS = -s -Wl,-Bstatic -static-libgcc -static-libstdc++ -mwindows + + LIBS = -lminiupnpc \ + -lboost_system$$BOOST_SUFFIX \ + -lboost_date_time$$BOOST_SUFFIX \ + -lboost_filesystem$$BOOST_SUFFIX \ + -lboost_program_options$$BOOST_SUFFIX \ + -lssl \ + -lcrypto \ + -lz \ + -lwsock32 \ + -lws2_32 \ + -lgdi32 \ + -liphlpapi \ + -lstdc++ \ + -lpthread +} + +!android:!symbian:!maemo5:!simulator { + message("Build with a system tray icon") + # see also http://doc.qt.io/qt-4.8/qt-desktop-systray-systray-pro.html for example on wince* + #sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS i2pd_qt.pro resources images + RESOURCES = i2pd.qrc + QT += xml + #INSTALLS += sources +} + diff --git a/qt/i2pd_qt/mainwindow.cpp b/qt/i2pd_qt/mainwindow.cpp index c3761764..a095f78c 100644 --- a/qt/i2pd_qt/mainwindow.cpp +++ b/qt/i2pd_qt/mainwindow.cpp @@ -349,7 +349,9 @@ QString MainWindow::getStatusPageHtml(bool showHiddenInfo) { s << ""; switch (statusPage) { - case main_page: i2p::http::ShowStatus(s, showHiddenInfo);break; + case main_page: + i2p::http::ShowStatus(s, showHiddenInfo, i2p::http::OutputFormatEnum::forQtUi); + break; case commands: break; case local_destinations: i2p::http::ShowLocalDestinations(s);break; case leasesets: i2p::http::ShowLeasesSets(s); break; From b046c45a9e7db86906684441818cc7153f269314 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 25 Apr 2018 11:25:49 -0400 Subject: [PATCH 15/17] tabify --- libi2pd_client/SAM.cpp | 6 +++--- libi2pd_client/SAM.h | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index d228e317..ac2dd853 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -207,7 +207,7 @@ namespace client void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close) { if (ecode) - { + { LogPrint (eLogError, "SAM: reply send error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: reply send error"); @@ -224,7 +224,7 @@ namespace client void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred) { if (ecode) - { + { LogPrint (eLogError, "SAM: read error: ", ecode.message ()); if (ecode != boost::asio::error::operation_aborted) Terminate ("SAM: read error"); @@ -569,7 +569,7 @@ namespace client keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); #else size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, - keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); + keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ()); #endif SendMessageReply (m_Buffer, l, false); } diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 23cdf170..0c70758f 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -92,12 +92,12 @@ namespace client void Terminate (const char* reason); - bool IsSession(const std::string & id) const; - + bool IsSession(const std::string & id) const; + private: - void TerminateClose() { Terminate(nullptr); } - - void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred); + void TerminateClose() { Terminate(nullptr); } + + 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); @@ -131,8 +131,8 @@ namespace client void WriteI2PDataImmediate(uint8_t * ptr, size_t sz); void HandleWriteI2PDataImmediate(const boost::system::error_code & ec, uint8_t * buff); - void HandleStreamSend(const boost::system::error_code & ec); - + void HandleStreamSend(const boost::system::error_code & ec); + private: SAMBridge& m_Owner; @@ -150,10 +150,10 @@ namespace client struct SAMSession { - SAMBridge & m_Bridge; + SAMBridge & m_Bridge; std::shared_ptr localDestination; std::shared_ptr UDPEndpoint; - std::string Name; + std::string Name; SAMSession (SAMBridge & parent, const std::string & name, std::shared_ptr dest); ~SAMSession (); @@ -172,24 +172,24 @@ namespace client 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 + 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; - std::list > ListSockets(const std::string & id) const; + std::list > ListSockets(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); - void RemoveSocket(const std::shared_ptr & socket); - + void RemoveSocket(const std::shared_ptr & socket); + private: void Run (); void Accept (); - void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); void ReceiveDatagram (); void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred); @@ -204,8 +204,8 @@ namespace client boost::asio::ip::udp::socket m_DatagramSocket; mutable std::mutex m_SessionsMutex; std::map > m_Sessions; - mutable std::mutex m_OpenSocketsMutex; - std::list > m_OpenSockets; + mutable std::mutex m_OpenSocketsMutex; + std::list > m_OpenSockets; uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1]; public: From 0ced38cdcbcae6f6bfe1ab653d85f201f194c247 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 25 Apr 2018 11:27:56 -0400 Subject: [PATCH 16/17] tabify --- libi2pd/Streaming.h | 4 ++-- libi2pd_client/SAM.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index 7f2598c0..3db8d760 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -165,9 +165,9 @@ namespace stream void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0); size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); }; - void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; + void AsyncClose() { m_Service.post(std::bind(&Stream::Close, shared_from_this())); }; - /** only call close from destination thread, use Stream::AsyncClose for other threads */ + /** only call close from destination thread, use Stream::AsyncClose for other threads */ void Close (); void Cancel () { m_ReceiveTimer.cancel (); }; diff --git a/libi2pd_client/SAM.h b/libi2pd_client/SAM.h index 0c70758f..953af1cd 100644 --- a/libi2pd_client/SAM.h +++ b/libi2pd_client/SAM.h @@ -77,7 +77,7 @@ namespace client class SAMBridge; struct SAMSession; - class SAMSocket :public std::enable_shared_from_this + class SAMSocket: public std::enable_shared_from_this { public: From 2fbbbf298bf272009618332bed2d2e58977fdc21 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 25 Apr 2018 16:18:07 -0400 Subject: [PATCH 17/17] use shared pointers for tunnel reload --- libi2pd_client/ClientContext.cpp | 54 +++++++++++++++++--------------- libi2pd_client/ClientContext.h | 8 ++--- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 6c3a9410..99f34b73 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -488,8 +488,8 @@ namespace client { localDestination = m_SharedLocalDestination; } - auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort); - if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr(clientTunnel))).second) + auto clientTunnel = std::make_shared(name, dest, end, localDestination, destinationPort); + if(m_ClientForwards.insert(std::make_pair(end, clientTunnel)).second) { clientTunnel->Start(); } @@ -498,31 +498,35 @@ namespace client } else { boost::asio::ip::tcp::endpoint clientEndpoint; - I2PService * clientTunnel = nullptr; + std::shared_ptr clientTunnel; if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS) { // socks proxy - clientTunnel = new i2p::proxy::SOCKSProxy(name, address, port, false, "", destinationPort, localDestination); - clientEndpoint = ((i2p::proxy::SOCKSProxy*)clientTunnel)->GetLocalEndpoint (); + auto tun = std::make_shared(name, address, port, false, "", destinationPort, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint (); } else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY) { // http proxy std::string outproxy = section.second.get("outproxy", ""); - clientTunnel = new i2p::proxy::HTTPProxy(name, address, port, outproxy, localDestination); - clientEndpoint = ((i2p::proxy::HTTPProxy*)clientTunnel)->GetLocalEndpoint (); + auto tun = std::make_shared(name, address, port, outproxy, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint (); } else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS) { // websocks proxy - clientTunnel = new WebSocks(address, port, localDestination);; - clientEndpoint = ((WebSocks*)clientTunnel)->GetLocalEndpoint(); + auto tun = std::make_shared(address, port, localDestination); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint(); } else { // tcp client - clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort); - clientEndpoint = ((I2PClientTunnel*)clientTunnel)->GetLocalEndpoint (); + auto tun = std::make_shared (name, dest, address, port, localDestination, destinationPort); + clientTunnel = tun; + clientEndpoint = tun->GetLocalEndpoint (); } uint32_t timeout = section.second.get(I2P_CLIENT_TUNNEL_CONNECT_TIMEOUT, 0); if(timeout) @@ -531,8 +535,7 @@ namespace client LogPrint(eLogInfo, "Clients: I2P Client tunnel connect timeout set to ", timeout); } - auto clientTunnelDest = clientTunnel->GetLocalDestination (); // make copy of destination for possible update - auto ins = m_ClientTunnels.insert (std::make_pair (clientEndpoint, std::unique_ptr(clientTunnel))); + auto ins = m_ClientTunnels.insert (std::make_pair (clientEndpoint, clientTunnel)); if (ins.second) { clientTunnel->Start (); @@ -541,10 +544,10 @@ namespace client else { // TODO: update - if (ins.first->second->GetLocalDestination () != clientTunnelDest) + if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ()) { LogPrint (eLogInfo, "Clients: I2P client tunnel destination updated"); - ins.first->second->SetLocalDestination (clientTunnelDest); + ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ()); } ins.first->second->isUpdated = true; LogPrint (eLogInfo, "Clients: I2P client tunnel for endpoint ", clientEndpoint, " already exists"); @@ -589,7 +592,7 @@ namespace client // TODO: hostnames auto localAddress = boost::asio::ip::address::from_string(address); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); - I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port); + auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port); if(!isUniqueLocal) { LogPrint(eLogInfo, "Clients: disabling loopback address mapping"); @@ -600,7 +603,7 @@ namespace client std::make_pair( std::make_pair( localDestination->GetIdentHash(), port), - std::unique_ptr(serverTunnel))).second) + serverTunnel)).second) { serverTunnel->Start(); LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32()); @@ -611,13 +614,13 @@ namespace client continue; } - I2PServerTunnel * serverTunnel; + std::shared_ptr serverTunnel; if (type == I2P_TUNNELS_SECTION_TYPE_HTTP) - serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip); + serverTunnel = std::make_shared (name, host, port, localDestination, hostOverride, inPort, gzip); else if (type == I2P_TUNNELS_SECTION_TYPE_IRC) - serverTunnel = new I2PServerTunnelIRC (name, host, port, localDestination, webircpass, inPort, gzip); + serverTunnel = std::make_shared (name, host, port, localDestination, webircpass, inPort, gzip); else // regular server tunnel by default - serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip); + serverTunnel = std::make_shared (name, host, port, localDestination, inPort, gzip); if(!isUniqueLocal) { @@ -640,10 +643,9 @@ namespace client while (comma != std::string::npos); serverTunnel->SetAccessList (idents); } - auto serverTunnelDest = serverTunnel->GetLocalDestination (); auto ins = m_ServerTunnels.insert (std::make_pair ( - std::make_pair (localDestination->GetIdentHash (), inPort), - std::unique_ptr(serverTunnel))); + std::make_pair (localDestination->GetIdentHash (), inPort), + serverTunnel)); if (ins.second) { serverTunnel->Start (); @@ -652,10 +654,10 @@ namespace client else { // TODO: update - if (ins.first->second->GetLocalDestination () != serverTunnelDest) + if (ins.first->second->GetLocalDestination () != serverTunnel->GetLocalDestination ()) { LogPrint (eLogInfo, "Clients: I2P server tunnel destination updated"); - ins.first->second->SetLocalDestination (serverTunnelDest); + ins.first->second->SetLocalDestination (serverTunnel->GetLocalDestination ()); } ins.first->second->isUpdated = true; LogPrint (eLogInfo, "Clients: I2P server tunnel for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), "/", inPort, " already exists"); diff --git a/libi2pd_client/ClientContext.h b/libi2pd_client/ClientContext.h index 922d7acc..06aab3b1 100644 --- a/libi2pd_client/ClientContext.h +++ b/libi2pd_client/ClientContext.h @@ -113,12 +113,12 @@ namespace client i2p::proxy::HTTPProxy * m_HttpProxy; i2p::proxy::SOCKSProxy * m_SocksProxy; - std::map > m_ClientTunnels; // local endpoint->tunnel - std::map, std::unique_ptr > m_ServerTunnels; // ->tunnel + std::map > m_ClientTunnels; // local endpoint->tunnel + std::map, std::shared_ptr > m_ServerTunnels; // ->tunnel std::mutex m_ForwardsMutex; - std::map > m_ClientForwards; // local endpoint -> udp tunnel - std::map, std::unique_ptr > m_ServerForwards; // -> udp tunnel + std::map > m_ClientForwards; // local endpoint -> udp tunnel + std::map, std::shared_ptr > m_ServerForwards; // -> udp tunnel SAMBridge * m_SamBridge; BOBCommandChannel * m_BOBCommandChannel;