diff --git a/.gitignore b/.gitignore index 2b880085b..6f9356d05 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ Thumbs.db *.pro.user .kdev4 *.kdev4 + +!supportlibs/libsam3/Makefile diff --git a/.gitmodules b/.gitmodules index c35876fc6..2f69bb7b2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,3 +14,6 @@ [submodule "supportlibs/rapidjson"] path = supportlibs/rapidjson url = https://github.com/Tencent/rapidjson.git +[submodule "supportlibs/libsam3"] + path = supportlibs/libsam3 + url = https://github.com/i2p/libsam3.git diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index da5f25ba4..30aee9671 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -156,7 +156,7 @@ rs_webui { HEADERS += plugins/pluginmanager.h \ plugins/dlfcn_win32.h \ - rsitems/rspluginitems.h \ + rsitems/rspluginitems.h \ util/i2pcommon.h \ util/rsinitedptr.h @@ -393,7 +393,6 @@ HEADERS += pqi/authssl.h \ pqi/pqissl.h \ pqi/pqissllistener.h \ pqi/pqisslpersongrp.h \ - pqi/pqissli2pbob.h \ pqi/pqisslproxy.h \ pqi/pqistore.h \ pqi/pqistreamer.h \ @@ -454,7 +453,7 @@ HEADERS += rsitems/rsitem.h \ rsitems/rsgxsupdateitems.h \ rsitems/rsserviceinfoitems.h \ -HEADERS += services/autoproxy/p3i2pbob.h \ +HEADERS += \ services/rseventsservice.h \ services/autoproxy/rsautoproxymonitor.h \ services/p3msgservice.h \ @@ -558,7 +557,6 @@ SOURCES += pqi/authgpg.cc \ pqi/pqissl.cc \ pqi/pqissllistener.cc \ pqi/pqisslpersongrp.cc \ - pqi/pqissli2pbob.cpp \ pqi/pqisslproxy.cc \ pqi/pqistore.cc \ pqi/pqistreamer.cc \ @@ -616,7 +614,6 @@ SOURCES += serialiser/rsbaseserial.cc \ SOURCES += services/autoproxy/rsautoproxymonitor.cc \ services/rseventsservice.cc \ - services/autoproxy/p3i2pbob.cc \ services/p3msgservice.cc \ services/p3service.cc \ services/p3statusservice.cc \ @@ -1012,6 +1009,34 @@ rs_broadcast_discovery { } } +rs_sam3 { + SOURCES += \ + services/autoproxy/p3i2psam3.cpp \ + pqi/pqissli2psam3.cpp \ + + HEADERS += \ + services/autoproxy/p3i2psam3.h \ + pqi/pqissli2psam3.h \ +} + +rs_sam3_libsam3 { + DUMMYQMAKECOMPILERINPUT = FORCE + libsam3.name = Generating libsam3. + libsam3.input = DUMMYQMAKECOMPILERINPUT + libsam3.output = $$clean_path($${LIBSAM3_BUILD_PATH}/libsam3.a) + libsam3.CONFIG += target_predeps combine + libsam3.variable_out = PRE_TARGETDEPS + libsam3.commands = \ + cd $${RS_SRC_PATH} && ( \ + git submodule update --init supportlibs/libsam3 || \ + true ) && \ + mkdir -p $${UDP_DISCOVERY_BUILD_PATH} && \ + cp -r $${LIBSAM3_SRC_PATH}/* $${LIBSAM3_BUILD_PATH} && \ + cd $${LIBSAM3_BUILD_PATH} && \ + $(MAKE) build + QMAKE_EXTRA_COMPILERS += libsam3 +} + ########################################################################################################### # OLD CONFIG OPTIONS. # Not used much - but might be useful one day. diff --git a/libretroshare/src/pqi/pqissl.cc b/libretroshare/src/pqi/pqissl.cc index 76d447cc8..1cfff8fbd 100644 --- a/libretroshare/src/pqi/pqissl.cc +++ b/libretroshare/src/pqi/pqissl.cc @@ -1742,6 +1742,13 @@ bool pqissl::moretoread(uint32_t usec) { rslog(RSL_ALERT, pqisslzone, "pqissl::moretoread() Select ERROR!"); + RS_WARN(strerror(errno)); + + if (errno == EBADF) { + // happens when SAM is shut down + rslog(RSL_ALERT, pqisslzone, "pqissl::moretoread() -> calling reset()"); + reset_locked(); + } return 0; } diff --git a/libretroshare/src/pqi/pqissli2pbob.cpp b/libretroshare/src/pqi/pqissli2pbob.cpp deleted file mode 100644 index 5551ce788..000000000 --- a/libretroshare/src/pqi/pqissli2pbob.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * libretroshare/src/pqi: pqissli2pbob.cc * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#include "pqissli2pbob.h" - -bool pqissli2pbob::connect_parameter(uint32_t type, const std::string &value) -{ - if (type == NET_PARAM_CONNECT_DOMAIN_ADDRESS) - { - RS_STACK_MUTEX(mSslMtx); - // a new line must be appended! - mI2pAddr = value + '\n'; - return true; - } - - return pqissl::connect_parameter(type, value); -} - -int pqissli2pbob::Basic_Connection_Complete() -{ - int ret; - - if ((ret = pqissl::Basic_Connection_Complete()) != 1) - { - // basic connection not complete. - return ret; - } - - // send addr. (new line is already appended) - ret = send(sockfd, mI2pAddr.c_str(), mI2pAddr.length(), 0); - if (ret != (int)mI2pAddr.length()) - return -1; - return 1; -} diff --git a/libretroshare/src/pqi/pqissli2pbob.h b/libretroshare/src/pqi/pqissli2pbob.h deleted file mode 100644 index b1a643a75..000000000 --- a/libretroshare/src/pqi/pqissli2pbob.h +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * libretroshare/src/pqi: pqissli2pbob.h * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#ifndef PQISSLI2PBOB_H -#define PQISSLI2PBOB_H - -#include "pqi/pqissl.h" - -/* - * This class is a minimal varied version of pqissl to work with I2P BOB tunnels. - * The only difference is that the [.b32].i2p addresses must be sent first. - * - * Everything else is untouched. - */ - -class pqissli2pbob : public pqissl -{ -public: - pqissli2pbob(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm) - : pqissl(l, parent, lm) {} - - // NetInterface interface -public: - bool connect_parameter(uint32_t type, const std::string &value); - - // pqissl interface -protected: - int Basic_Connection_Complete(); - -private: - std::string mI2pAddr; -}; - -#endif // PQISSLI2PBOB_H diff --git a/libretroshare/src/pqi/pqissli2psam3.cpp b/libretroshare/src/pqi/pqissli2psam3.cpp new file mode 100644 index 000000000..1e0f5d433 --- /dev/null +++ b/libretroshare/src/pqi/pqissli2psam3.cpp @@ -0,0 +1,261 @@ +#include "pqissli2psam3.h" + +#include + +RS_SET_CONTEXT_DEBUG_LEVEL(2) + +static constexpr int pqiDone = 1; +static constexpr int pqiWait = 0; +static constexpr int pqiError = -1; + +pqissli2psam3::pqissli2psam3(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm) + : pqissl(l, parent, lm), mState(pqisslSam3State::NONE), mI2pAddrB32(), mI2pAddrLong() +{ + RS_DBG4(); + mConn = nullptr; +} + +bool pqissli2psam3::connect_parameter(uint32_t type, const std::string &value) +{ + RS_DBG4(); + + if (type == NET_PARAM_CONNECT_DOMAIN_ADDRESS) + { + RS_DBG1("got addr:", value); + RS_STACK_MUTEX(mSslMtx); + mI2pAddrB32 = value; + return true; + } + + return pqissl::connect_parameter(type, value); +} + +int pqissli2psam3::Initiate_Connection() +{ + RS_DBG4(); + + if(waiting != WAITING_DELAY) + { + RS_ERR("Already Attempt in Progress!"); + return pqiError; + } + + switch (mState) { + case(pqisslSam3State::NONE): + RS_DBG2("NONE"); + { + if(mConn) { + // how did we end up here? + RS_ERR("state is NONE but a connection is existing?!"); + } + mConn = 0; + // get SAM session + mConn = 0; + samSettings ss; + ss.session = nullptr; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::getSettings, static_cast(&ss)); + + if (!!ss.session) { + RS_DBG3("NONE->DO_LOOKUP"); + mState = pqisslSam3State::DO_LOOKUP; + } else { + RS_DBG3("NONE->DO_LOOKUP NOPE", ss.session); + } + } + break; + case(pqisslSam3State::DO_LOOKUP): + RS_DBG1("DO_LOOKUP"); + + if (!mI2pAddrLong.empty()) { + // skip lookup, it is highly unlikely/impossible for a public key to change (isn't it?) + mState = pqisslSam3State::WAIT_LOOKUP; + break; + } + + { + i2p::address *addr = new i2p::address; + addr->clear(); + addr->base32 = mI2pAddrB32; + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::lookupKey, this, static_cast(addr)); + } + mState = pqisslSam3State::WAIT_LOOKUP; + break; + case(pqisslSam3State::DO_CONNECT): + RS_DBG2("DO_CONNECT"); + + { + auto wrapper = new samEstablishConnectionWrapper(); + wrapper->address.clear(); + wrapper->address.publicKey = mI2pAddrLong; + wrapper->connection = nullptr; + + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::establishConnection, this, static_cast(wrapper)); + } + mState = pqisslSam3State::WAIT_CONNECT; + break; + case(pqisslSam3State::DONE): + RS_DBG2("DONE"); + + if (setupSocket()) + return pqiDone; + return pqiError; + + /* waiting */ + case(pqisslSam3State::WAIT_LOOKUP): + RS_DBG3("WAIT_LOOKUP"); + break; + case(pqisslSam3State::WAIT_CONNECT): + RS_DBG3("WAIT_CONNECT"); + break; + } + return pqiWait; +} + +int pqissli2psam3::net_internal_close(int fd) +{ + RS_DBG4(); + + // sanity check + if (mConn && fd != mConn->fd) { + // this should never happen! + RS_ERR("fd != mConn"); +// sam3CloseConnection(mConn); + } + + // now to the actuall closing + int ret = pqissl::net_internal_close(fd); + + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::closeConnection, this, mConn); + + // finally cleanup + mConn = 0; + mState = pqisslSam3State::NONE; + + return ret; +} + +void pqissli2psam3::taskFinished(taskTicket *&ticket) +{ + RS_DBG4(); + + switch (ticket->task) { + case autoProxyTask::lookupKey: + { + auto addr = static_cast(ticket->data); + + RS_STACK_MUTEX(mSslMtx); + if (ticket->result == autoProxyStatus::ok) { + mI2pAddrLong = addr->publicKey; + mState = pqisslSam3State::DO_CONNECT; + } else { + waiting = WAITING_FAIL_INTERFACE; + } + + delete addr; + ticket->data = nullptr; + addr = nullptr; + } + break; + case autoProxyTask::establishConnection: + { + auto wrapper = static_cast(ticket->data); + + RS_STACK_MUTEX(mSslMtx); + if (ticket->result == autoProxyStatus::ok) { + mConn = wrapper->connection; + mState = pqisslSam3State::DONE; + } else { + waiting = WAITING_FAIL_INTERFACE; + } + + delete wrapper; + ticket->data = nullptr; + wrapper = nullptr; + } + break; + case autoProxyTask::closeConnection: + // nothing to do here + break; + default: + RS_WARN("unkown task", ticket->task); + } + + // clean up! + delete ticket; + ticket = nullptr; +} + +bool pqissli2psam3::setupSocket() +{ + /* + * This function contains the generis part from pqissl::Initiate_Connection() + */ + int err; + int osock = mConn->fd; + + err = unix_fcntl_nonblock(osock); + if (err < 0) + { + RS_ERR("Cannot make socket NON-Blocking:", err); + + waiting = WAITING_FAIL_INTERFACE; + net_internal_close(osock); + return false; + } + +#ifdef WINDOWS_SYS + /* Set TCP buffer size for Windows systems */ + + int sockbufsize = 0; + int size = sizeof(int); + + err = getsockopt(osock, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, &size); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: Current TCP receive buffer size " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error getting TCP receive buffer size. Error " << err << std::endl; + } +#endif + + sockbufsize = 0; + + err = getsockopt(osock, SOL_SOCKET, SO_SNDBUF, (char *)&sockbufsize, &size); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: Current TCP send buffer size " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error getting TCP send buffer size. Error " << err << std::endl; + } +#endif + + sockbufsize = WINDOWS_TCP_BUFFER_SIZE; + + err = setsockopt(osock, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, sizeof(sockbufsize)); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: TCP receive buffer size set to " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error setting TCP receive buffer size. Error " << err << std::endl; + } +#endif + + err = setsockopt(osock, SOL_SOCKET, SO_SNDBUF, (char *)&sockbufsize, sizeof(sockbufsize)); +#ifdef PQISSL_DEBUG + if (err == 0) { + std::cerr << "pqissl::Initiate_Connection: TCP send buffer size set to " << sockbufsize << std::endl; + } else { + std::cerr << "pqissl::Initiate_Connection: Error setting TCP send buffer size. Error " << err << std::endl; + } +#endif +#endif // WINDOWS_SYS + + + mTimeoutTS = time(NULL) + mConnectTimeout; + //std::cerr << "Setting Connect Timeout " << mConnectTimeout << " Seconds into Future " << std::endl; + + waiting = WAITING_SOCK_CONNECT; + sockfd = osock; + + return true; +} diff --git a/libretroshare/src/pqi/pqissli2psam3.h b/libretroshare/src/pqi/pqissli2psam3.h new file mode 100644 index 000000000..90fdff6e0 --- /dev/null +++ b/libretroshare/src/pqi/pqissli2psam3.h @@ -0,0 +1,47 @@ +#ifndef PQISSLI2PSAM3_H +#define PQISSLI2PSAM3_H + +#include "pqi/pqissl.h" +#include "services/autoproxy/rsautoproxymonitor.h" +#include "services/autoproxy/p3i2psam3.h" + +// Use a state machine as the whole pqi code is designed around them and some operation (like lookup) might be blocking +enum class pqisslSam3State : uint8_t { + NONE = 0, + DO_LOOKUP, + WAIT_LOOKUP, + DO_CONNECT, + WAIT_CONNECT, + DONE +}; + +class pqissli2psam3 : public pqissl, public autoProxyCallback +{ +public: + pqissli2psam3(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm); + + // NetInterface interface +public: + bool connect_parameter(uint32_t type, const std::string &value); + + // pqissl interface +protected: + int Initiate_Connection(); + int net_internal_close(int fd); + + // autoProxyCallback interface +public: + void taskFinished(taskTicket *&ticket); + +private: + bool setupSocket(); + +private: + pqisslSam3State mState; + std::string mI2pAddrB32; + std::string mI2pAddrLong; + + Sam3Connection *mConn; +}; + +#endif // PQISSLI2PSAM3_H diff --git a/libretroshare/src/pqi/pqissllistener.h b/libretroshare/src/pqi/pqissllistener.h index 85af2d869..800ca876b 100644 --- a/libretroshare/src/pqi/pqissllistener.h +++ b/libretroshare/src/pqi/pqissllistener.h @@ -186,8 +186,8 @@ public: virtual int finaliseConnection(int fd, SSL *ssl, const RsPeerId& peerId, const sockaddr_storage &raddr); + RS_SET_CONTEXT_DEBUG_LEVEL(2) + private: std::map listenaddr; - - RS_SET_CONTEXT_DEBUG_LEVEL(2) }; diff --git a/libretroshare/src/pqi/pqisslpersongrp.cc b/libretroshare/src/pqi/pqisslpersongrp.cc index 7016a2fa6..16112c744 100644 --- a/libretroshare/src/pqi/pqisslpersongrp.cc +++ b/libretroshare/src/pqi/pqisslpersongrp.cc @@ -46,7 +46,7 @@ static struct RsLog::logInfo pqipersongrpzoneInfo = {RsLog::Default, "pqipersong #endif #include "pqi/pqisslproxy.h" -#include "pqi/pqissli2pbob.h" +#include "pqi/pqissli2psam3.h" pqilistener * pqisslpersongrp::locked_createListener(const struct sockaddr_storage &laddr) { @@ -74,26 +74,26 @@ pqiperson * pqisslpersongrp::locked_createPerson(const RsPeerId& id, pqilistener std::cerr << std::endl; #endif - // Use pqicI2PBOB for I2P - pqiconnect *pqicSOCKSProxy, *pqicI2PBOB; + // Use pqicI2P for I2P + pqiconnect *pqicSOCKSProxy, *pqicI2P; { pqisslproxy *pqis = new pqisslproxy((pqissllistener *) listener, pqip, mLinkMgr); RsSerialiser *rss = new RsSerialiser(); rss->addSerialType(new RsRawSerialiser()); pqicSOCKSProxy = new pqiconnect(pqip, rss, pqis); } - if (rsAutoProxyMonitor::instance()->isEnabled(autoProxyType::I2PBOB)) + + if (rsAutoProxyMonitor::instance()->isEnabled(autoProxyType::I2PSAM3)) { - pqissli2pbob *pqis = new pqissli2pbob((pqissllistener *) listener, pqip, mLinkMgr); + pqissli2psam3 *pqis = new pqissli2psam3((pqissllistener *) listener, pqip, mLinkMgr); RsSerialiser *rss = new RsSerialiser(); rss->addSerialType(new RsRawSerialiser()); - pqicI2PBOB = new pqiconnect(pqip, rss, pqis); + pqicI2P = new pqiconnect(pqip, rss, pqis); } else { - pqicI2PBOB = pqicSOCKSProxy; + pqicI2P = pqicSOCKSProxy; } - /* first select type based on peer */ uint32_t typePeer = mPeerMgr->getHiddenType(id); switch (typePeer) { @@ -101,7 +101,7 @@ pqiperson * pqisslpersongrp::locked_createPerson(const RsPeerId& id, pqilistener pqip -> addChildInterface(PQI_CONNECT_HIDDEN_TOR_TCP, pqicSOCKSProxy); break; case RS_HIDDEN_TYPE_I2P: - pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2PBOB); + pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2P); break; default: /* peer is not a hidden one but we are */ @@ -109,7 +109,7 @@ pqiperson * pqisslpersongrp::locked_createPerson(const RsPeerId& id, pqilistener uint32_t typeOwn = mPeerMgr->getHiddenType(AuthSSL::getAuthSSL()->OwnId()); switch (typeOwn) { case RS_HIDDEN_TYPE_I2P: - pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2PBOB); + pqip -> addChildInterface(PQI_CONNECT_HIDDEN_I2P_TCP, pqicI2P); break; default: /* this case shouldn't happen! */ diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index 73fea2684..d26e7ac05 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -195,7 +195,7 @@ public: /* * Setup Hidden Location; */ - static void SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useBob); + static void SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useI2p); static bool LoadPassword(const std::string& passwd) ; diff --git a/libretroshare/src/rsserver/p3face.h b/libretroshare/src/rsserver/p3face.h index 661cb244f..648e39123 100644 --- a/libretroshare/src/rsserver/p3face.h +++ b/libretroshare/src/rsserver/p3face.h @@ -43,7 +43,7 @@ class p3heartbeat; class p3discovery2; -class p3I2pBob; +class p3I2pSam3; /* GXS Classes - just declare the classes. so we don't have to totally recompile to switch */ @@ -161,8 +161,8 @@ public: p3ChatService *chatSrv; p3StatusService *mStatusSrv; p3GxsTunnelService *mGxsTunnels; -#ifdef RS_USE_I2P_BOB - p3I2pBob *mI2pBob; +#ifdef RS_USE_I2P_SAM3 + p3I2pSam3 *mI2pSam3; #endif // This list contains all threaded services. It will be used to shut them down properly. diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 8449a9e3e..ad39f46a1 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -170,7 +170,7 @@ struct RsInitConfig std::string hiddenNodeAddress; uint16_t hiddenNodePort; - bool hiddenNodeI2PBOB; + bool hiddenNodeI2P; /* Logging */ bool haveLogFile; @@ -664,13 +664,13 @@ void RsInit::setAutoLogin(bool autoLogin){ } /* Setup Hidden Location; */ -void RsInit::SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useBob) +void RsInit::SetHiddenLocation(const std::string& hiddenaddress, uint16_t port, bool useI2p) { /* parse the bugger (todo) */ rsInitConfig->hiddenNodeSet = true; rsInitConfig->hiddenNodeAddress = hiddenaddress; rsInitConfig->hiddenNodePort = port; - rsInitConfig->hiddenNodeI2PBOB = useBob; + rsInitConfig->hiddenNodeI2P = useI2p; } @@ -717,7 +717,7 @@ RsGRouter *rsGRouter = NULL ; # include "rs_upnp/upnphandler_miniupnp.h" #endif // def RS_USE_LIBUPNP -#include "services/autoproxy/p3i2pbob.h" +#include "services/autoproxy/p3i2psam3.h" #include "services/autoproxy/rsautoproxymonitor.h" #include "services/p3gxsreputation.h" @@ -923,9 +923,9 @@ int RsServer::StartupRetroShare() mNetMgr->setManagers(mPeerMgr, mLinkMgr); rsAutoProxyMonitor *autoProxy = rsAutoProxyMonitor::instance(); -#ifdef RS_USE_I2P_BOB - mI2pBob = new p3I2pBob(mPeerMgr); - autoProxy->addProxy(autoProxyType::I2PBOB, mI2pBob); +#ifdef RS_USE_I2P_SAM3 + mI2pSam3 = new p3I2pSam3(mPeerMgr); + autoProxy->addProxy(autoProxyType::I2PSAM3, mI2pSam3); #endif //load all the SSL certs as friends @@ -1655,8 +1655,9 @@ int RsServer::StartupRetroShare() mConfigMgr->addConfiguration("wire.cfg", wire_ns); #endif #endif //RS_ENABLE_GXS -#ifdef RS_USE_I2P_BOB - mConfigMgr->addConfiguration("I2PBOB.cfg", mI2pBob); +#ifdef RS_USE_I2P_SAM3 + // to make migration easiert, SAM will use BOBs configuration, as they are compatible / the same. + mConfigMgr->addConfiguration("I2PBOB.cfg", mI2pSam3); #endif mPluginsManager->addConfigurations(mConfigMgr) ; @@ -1709,34 +1710,33 @@ int RsServer::StartupRetroShare() { std::cout << "RsServer::StartupRetroShare setting up hidden locations" << std::endl; - if (rsInitConfig->hiddenNodeI2PBOB) { - std::cout << "RsServer::StartupRetroShare setting up BOB" << std::endl; + if (rsInitConfig->hiddenNodeI2P) { + std::cout << "RsServer::StartupRetroShare setting up SAMv3" << std::endl; // we need a local port! mNetMgr->checkNetAddress(); // add i2p proxy - // bob will use this address sockaddr_storage i2pInstance; sockaddr_storage_ipv4_aton(i2pInstance, rsInitConfig->hiddenNodeAddress.c_str()); mPeerMgr->setProxyServerAddress(RS_HIDDEN_TYPE_I2P, i2pInstance); std::string addr; // will be set by auto proxy service - uint16_t port = rsInitConfig->hiddenNodePort; // unused by bob + uint16_t port; // unused by SAM - bool r = autoProxy->initialSetup(autoProxyType::I2PBOB, addr, port); + bool r = autoProxy->initialSetup(autoProxyType::I2PSAM3, addr, port); if (r && !addr.empty()) { mPeerMgr->setupHiddenNode(addr, port); - // now enable bob - bobSettings bs; - autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::getSettings, &bs); - bs.enable = true; - autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &bs); + // now enable SAM + samSettings ss; + autoProxy->taskSync(autoProxyType::I2PSAM3, autoProxyTask::getSettings, &ss); + ss.enable = true; + autoProxy->taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &ss); } else { std::cerr << "RsServer::StartupRetroShare failed to receive keys" << std::endl; - /// TODO add notify for failed bob setup + /// TODO add notify for failed i2p setup } } else { mPeerMgr->setupHiddenNode(rsInitConfig->hiddenNodeAddress, rsInitConfig->hiddenNodePort); @@ -1758,19 +1758,17 @@ int RsServer::StartupRetroShare() if (rsInitConfig->hiddenNodeSet) { // newly created location // mNetMgr->checkNetAddress() will setup ports for us + +#if 0 // this was used for BOB but is not requires for SAMv3 // trigger updates for auto proxy services std::vector types; - - // i2p bob need to rebuild its command map - types.push_back(autoProxyType::I2PBOB); - rsAutoProxyMonitor::taskSync(types, autoProxyTask::reloadConfig); +#endif } /**************************************************************************/ /* startup (stuff dependent on Ids/peers is after this point) */ /**************************************************************************/ - autoProxy->startAll(); pqih->init_listener(); @@ -1803,8 +1801,8 @@ int RsServer::StartupRetroShare() /**************************************************************************/ // auto proxy threads -#ifdef RS_USE_I2P_BOB - startServiceThread(mI2pBob, "I2P-BOB"); +#ifdef RS_USE_I2P_SAM3 + startServiceThread(mI2pSam3, "I2P-SAM3"); #endif #ifdef RS_ENABLE_GXS diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc deleted file mode 100644 index f9eb3d9b3..000000000 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ /dev/null @@ -1,1117 +0,0 @@ -/******************************************************************************* - * libretroshare/src/services/autoproxy: p3i2pbob.cc * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#include -#include /* for usleep() */ - -#include "p3i2pbob.h" - -#include "pqi/p3peermgr.h" -#include "rsitems/rsconfigitems.h" -#include "util/radix32.h" -#include "util/radix64.h" -#include "util/rsdebug.h" -#include "util/rstime.h" -#include "util/rsprint.h" -#include "util/rsrandom.h" - -static const std::string kConfigKeyBOBEnable = "BOB_ENABLE"; -static const std::string kConfigKeyBOBKey = "BOB_KEY"; -static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; -static const std::string kConfigKeyInLength = "IN_LENGTH"; -static const std::string kConfigKeyInQuantity = "IN_QUANTITY"; -static const std::string kConfigKeyInVariance = "IN_VARIANCE"; -static const std::string kConfigKeyOutLength = "OUT_LENGTH"; -static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY"; -static const std::string kConfigKeyOutVariance = "OUT_VARIANCE"; - -/// Sleep duration for receiving loop in error/no-data case -static const useconds_t sleepTimeRecv = 250; // times 1000 = 250ms -/// Sleep duration for everything else -static const useconds_t sleepTimeWait = 50; // times 1000 = 50ms or 0.05s -static const int sleepFactorDefault = 10; // 0.5s -static const int sleepFactorFast = 1; // 0.05s -static const int sleepFactorSlow = 20; // 1s - -static const rstime_t selfCheckPeroid = 30; - -void doSleep(useconds_t timeToSleepMS) { - rstime::rs_usleep((useconds_t) (timeToSleepMS * 1000)); -} - -p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr) - : RsTickingThread(), p3Config(), - mState(csIdel), mTask(ctIdle), - mStateOld(csIdel), mTaskOld(ctIdle), - mBOBState(bsCleared), mPeerMgr(peerMgr), - mConfigLoaded(false), mSocket(0), - mLastProxyCheck(time(NULL)), - mProcessing(NULL), mLock("I2P-BOB") -{ - // set defaults - mSetting.initDefault(); - - mCommands.clear(); -} - -bool p3I2pBob::isEnabled() -{ - RS_STACK_MUTEX(mLock); - return mSetting.enable; -} - -bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) -{ - RS_DBG(""); - - // update config - { - RS_STACK_MUTEX(mLock); - if (!mConfigLoaded) { - finalizeSettings_locked(); - mConfigLoaded = true; - } else { - updateSettings_locked(); - } - } - - RS_DBG("config updated"); - - // request keys - // p3I2pBob::stateMachineBOB expects mProcessing to be set therefore - // we create this fake ticket without a callback or data - // ticket gets deleted later by this service - taskTicket *fakeTicket = rsAutoProxyMonitor::getTicket(); - fakeTicket->task = autoProxyTask::receiveKey; - processTaskAsync(fakeTicket); - - RS_DBG("fakeTicket requested"); - - // now start thread - start("I2P-BOB gen key"); - - RS_DBG("thread started"); - - int counter = 0; - // wait for keys - for(;;) { - doSleep(sleepTimeWait * sleepFactorDefault); - - RS_STACK_MUTEX(mLock); - - // wait for tast change - if (mTask != ctRunGetKeys) - break; - - if (++counter > 30) { - RS_DBG4("timeout!"); - return false; - } - } - - RS_DBG("got keys"); - - // stop thread - fullstop(); - - RS_DBG("thread stopped"); - - { - RS_STACK_MUTEX(mLock); - addr = mSetting.address.base32; - } - - RS_DBG4("addr ", addr); - - return true; -} - -void p3I2pBob::processTaskAsync(taskTicket *ticket) -{ - switch (ticket->task) { - case autoProxyTask::start: - case autoProxyTask::stop: - case autoProxyTask::receiveKey: - case autoProxyTask::proxyStatusCheck: - { - RS_STACK_MUTEX(mLock); - mPending.push(ticket); - } - break; - default: - RS_DBG("unknown task"); - rsAutoProxyMonitor::taskError(ticket); - break; - } -} - -void p3I2pBob::processTaskSync(taskTicket *ticket) -{ - bool data = !!ticket->data; - - // check wether we can process the task immediately or have to queue it - switch (ticket->task) { - case autoProxyTask::status: - // check if everything needed is set - if (!data) { - RS_DBG("autoProxyTask::status data is missing"); - rsAutoProxyMonitor::taskError(ticket); - break; - } - - // get states - getStates((struct bobStates*)ticket->data); - - // finish task - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::getSettings: - // check if everything needed is set - if (!data) { - RS_DBG("autoProxyTask::getSettings data is missing"); - rsAutoProxyMonitor::taskError(ticket); - break; - } - - // get settings - getBOBSettings((struct bobSettings *)ticket->data); - - // finish task - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::setSettings: - // check if everything needed is set - if (!data) { - RS_DBG("autoProxyTask::setSettings data is missing"); - rsAutoProxyMonitor::taskError(ticket); - break; - } - - // set settings - setBOBSettings((struct bobSettings *)ticket->data); - - // finish task - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::reloadConfig: - { - RS_STACK_MUTEX(mLock); - updateSettings_locked(); - } - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - break; - case autoProxyTask::getErrorInfo: - if (!data) { - RS_DBG("autoProxyTask::getErrorInfo data is missing"); - rsAutoProxyMonitor::taskError(ticket); - } else { - RS_STACK_MUTEX(mLock); - *(std::string *)ticket->data = mErrorMsg; - rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); - } - break; - default: - RS_DBG("unknown task"); - rsAutoProxyMonitor::taskError(ticket); - break; - } -} - -bool inline isAnswerOk(const std::string &answer) { - return (answer.compare(0, 2, "OK") == 0); -} - -bool inline isTunnelActiveError(const std::string &answer) { - return answer.compare(0, 22, "ERROR tunnel is active") == 0; -} - -void p3I2pBob::threadTick() -{ - int sleepTime = 0; - { - RS_STACK_MUTEX(mLock); - RS_DBG4("data_tick mState: ", mState, " mTask: ", mTask, " mBOBState: ", mBOBState, " mPending: ", mPending.size()); - } - - sleepTime += stateMachineController(); - sleepTime += stateMachineBOB(); - - sleepTime >>= 1; - - // sleep outisde of lock! - doSleep(sleepTime * sleepTimeWait); -} - -int p3I2pBob::stateMachineBOB() -{ - std::string answer; - bobStateInfo currentState; - - { - RS_STACK_MUTEX(mLock); - if (mBOBState == bsCleared || !mConfigLoaded) { - // we don't have work to do - sleep longer - return sleepFactorSlow; - } - - // get next command - currentState = mCommands[mBOBState]; - } - - // this call can take a while - // do NOT hold the lock - answer = executeCommand(currentState.command); - - // can hold the lock for the rest of the function - RS_STACK_MUTEX(mLock); - - // special state first - if (mBOBState == bsList) { - int counter = 0; - while (answer.find("OK Listing done") == std::string::npos) { - RS_DBG3("stateMachineBOB status check: read loop, counter: ", counter); - answer += recv(); - counter++; - } - - if (answer.find(mTunnelName) == std::string::npos) { - RS_DBG("status check: tunnel down!"); - // signal error - *((bool *)mProcessing->data) = true; - } - - mBOBState = currentState.nextState; - } else if (isAnswerOk(answer)) { - // check for other special states - std::string key; - switch (mBOBState) { - case bsNewkeysN: - key = answer.substr(3, answer.length()-3); - mSetting.address.base32 = i2p::keyToBase32Addr(key); - IndicateConfigChanged(); - break; - case bsGetkeys: - key = answer.substr(3, answer.length()-3); - mSetting.address.privateKey = key; - IndicateConfigChanged(); - break; - default: - break; - } - - // goto next command - mBOBState = currentState.nextState; - } else { - return stateMachineBOB_locked_failure(answer, currentState); - } - return sleepFactorFast; -} - -int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bobStateInfo ¤tState) -{ - // wait in case of active tunnel - // happens when trying to clear a stopping tunnel - if (isTunnelActiveError(answer)) { - return sleepFactorDefault; - } - - RS_DBG("FAILED to run command: ", currentState.command); - RS_DBG("answer: ", answer); - - mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n'); - mErrorMsg.append("reason '" + answer + "'" + '\n'); - - // this error handling needs testing! - mStateOld = mState; - mState = csError; - switch (mBOBState) { - case bsGetnick: - // failed getting nick - // tunnel is probably non existing - case bsClear: - // tunnel is cleared - mBOBState = bsQuit; - break; - case bsStop: - // failed stopping - // tunnel us probably not running - // continue to clearing - mBOBState = bsClear; - break; - case bsQuit: - // this can happen when the - // connection is somehow broken - // just try to disconnect - disconnectI2P(); - mBOBState = bsCleared; - break; - default: - // try to recover - mBOBState = bsGetnick; - break; - } - - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController() -{ - RS_STACK_MUTEX(mLock); - - switch (mState) { - case csIdel: - return stateMachineController_locked_idle(); - case csDoConnect: - if (!connectI2P()) { - RS_DBG("doConnect: unable to connect"); - mStateOld = mState; - mState = csError; - mErrorMsg = "unable to connect to BOB port"; - return sleepFactorSlow; - } - - RS_DBG4("doConnect: connected"); - mState = csConnected; - break; - case csConnected: - return stateMachineController_locked_connected(); - case csWaitForBob: - // check connection problems - if (mSocket == 0) { - RS_DBG("waitForBob: conection lost"); - mStateOld = mState; - mState = csError; - mErrorMsg = "connection lost to BOB"; - return sleepFactorDefault; - } - - // check for finished BOB protocol - if (mBOBState == bsCleared) { - // done - RS_DBG4("waitForBob: mBOBState == bsCleared"); - mState = csDoDisconnect; - } - break; - case csDoDisconnect: - if (!disconnectI2P() || mSocket != 0) { - // just in case - RS_DBG("doDisconnect: can't disconnect"); - mStateOld = mState; - mState = csError; - mErrorMsg = "unable to disconnect from BOB"; - return sleepFactorDefault; - } - - RS_DBG4("doDisconnect: disconnected"); - mState = csDisconnected; - break; - case csDisconnected: - return stateMachineController_locked_disconnected(); - case csError: - return stateMachineController_locked_error(); - } - - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController_locked_idle() -{ - // do some sanity checks - // use asserts becasue these things indicate wrong/broken state machines that need to be fixed ASAP! - assert(mBOBState == bsCleared); - assert(mSocket == 0); - assert(mState == csIdel || mState == csDisconnected); - - controllerTask oldTask = mTask; - // check for new task - if (mProcessing == NULL && !mPending.empty()) { - mProcessing = mPending.front(); - mPending.pop(); - - if (!mSetting.enable && ( - mProcessing->task == autoProxyTask::start || - mProcessing->task == autoProxyTask::stop || - mProcessing->task == autoProxyTask::proxyStatusCheck)) { - // skip since we are not enabled - RS_DBG1("disabled -> skipping ticket"); - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled); - mProcessing = NULL; - } else { - // set states - switch (mProcessing->task) { - case autoProxyTask::start: - mLastProxyCheck = time(NULL); - mTask = ctRunSetUp; - break; - case autoProxyTask::stop: - mTask = ctRunShutDown; - break; - case autoProxyTask::receiveKey: - mTaskOld = mTask; - mTask = ctRunGetKeys; - break; - case autoProxyTask::proxyStatusCheck: - mTaskOld = mTask; - mTask = ctRunCheck; - break; - default: - RS_DBG1("unknown async task"); - rsAutoProxyMonitor::taskError(mProcessing); - mProcessing = NULL; - break; - } - } - - mErrorMsg.clear(); - } - - // periodically check - if (mTask == ctRunSetUp && mLastProxyCheck < time(NULL) - selfCheckPeroid) { - taskTicket *tt = rsAutoProxyMonitor::getTicket(); - tt->task = autoProxyTask::proxyStatusCheck; - tt->data = (void *) new bool; - - *((bool *)tt->data) = false; - - mPending.push(tt); - - mLastProxyCheck = time(NULL); - } - - // wait for new task - if (!!mProcessing) { - // check if task was changed - if (mTask != oldTask) { - mState = csDoConnect; - } else { - // A ticket shall be processed but the state didn't change. - // This means that what ever is requested in the ticket - // was requested before already. - // -> set mState to csDisconnected to answer the ticket - mState = csDisconnected; - } - return sleepFactorFast; - } - - return sleepFactorSlow; -} - -int p3I2pBob::stateMachineController_locked_connected() -{ - // set proper bob state - switch (mTask) { - case ctRunSetUp: - // when we have a key use it for server tunnel! - if(mSetting.address.privateKey.empty()) { - RS_DBG4("setting mBOBState = setnickC"); - mBOBState = bsSetnickC; - } else { - RS_DBG4("setting mBOBState = setnickS"); - mBOBState = bsSetnickS; - } - break; - case ctRunShutDown: - // shut down existing tunnel - RS_DBG4("setting mBOBState = getnick"); - mBOBState = bsGetnick; - break; - case ctRunCheck: - RS_DBG4("setting mBOBState = list"); - mBOBState = bsList; - break; - case ctRunGetKeys: - RS_DBG4("setting mBOBState = setnickN"); - mBOBState = bsSetnickN; - break; - case ctIdle: - RS_DBG("task is idle. This should not happen!"); - break; - } - - mState = csWaitForBob; - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController_locked_disconnected() -{ - // check if we had an error - bool errorHappened = (mStateOld == csError); - - if(errorHappened) { - // reset old state - mStateOld = csIdel; - RS_DBG("error during process!"); - } - - // answer ticket - controllerState newState = csIdel; - switch (mTask) { - case ctRunSetUp: - if (errorHappened) { - rsAutoProxyMonitor::taskError(mProcessing); - // switch to error - newState = csError; - } else { - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::online); - } - break; - case ctRunShutDown: - // don't care about error here - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::offline); - break; - case ctRunCheck: - // get result and delete dummy ticket - errorHappened |= *((bool *)mProcessing->data); - delete (bool *)mProcessing->data; - delete mProcessing; - - // restore old task - mTask = mTaskOld; - - if (!errorHappened) { - RS_DBG4("run check result: ok"); - break; - } - // switch to error - newState = csError; - RS_DBG("run check result: error"); - mErrorMsg = "Connection check failed. Will try to restart tunnel."; - - break; - case ctRunGetKeys: - if (!errorHappened) { - // rebuild commands - updateSettings_locked(); - - if (mProcessing->data) - *((struct bobSettings *)mProcessing->data) = mSetting; - - rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::ok); - } else { - rsAutoProxyMonitor::taskError(mProcessing); - // switch to error - newState = csError; - } - - // restore old task - mTask = mTaskOld; - break; - case ctIdle: - RS_DBG("task is idle. This should not happen!"); - rsAutoProxyMonitor::taskError(mProcessing); - } - mProcessing = NULL; - mState = newState; - - if (newState == csError) - mLastProxyCheck = time(NULL); - - return sleepFactorFast; -} - -int p3I2pBob::stateMachineController_locked_error() -{ - // wait for bob protocoll - if (mBOBState != bsCleared) { - RS_DBG4("waiting for BOB"); - return sleepFactorFast; - } - -#if 0 - RS_DBG4("stateMachineController_locked_error: mProcessing: ", (mProcessing ? "not null" : "null")); -#endif - - // try to finish ticket - if (mProcessing) { - switch (mTask) { - case ctRunCheck: - // connection check failed at some point - RS_DBG("failed to check proxy status (it's likely dead)!"); - *((bool *)mProcessing->data) = true; - mState = csDoDisconnect; - mStateOld = csIdel; - // keep the error message - break; - case ctRunShutDown: - // not a big deal though - RS_DBG("failed to shut down tunnel (it's likely dead though)!"); - mState = csDoDisconnect; - mStateOld = csIdel; - mErrorMsg.clear(); - break; - case ctIdle: - // should not happen but we need to deal with it - // this will produce some error messages in the log and finish the task (marked as failed) - RS_DBG("task is idle. This should not happen!"); - mState = csDoDisconnect; - mStateOld = csIdel; - mErrorMsg.clear(); - break; - case ctRunGetKeys: - case ctRunSetUp: - RS_DBG("failed to receive key / start up"); - mStateOld = csError; - mState = csDoDisconnect; - // keep the error message - break; - } - return sleepFactorFast; - } - - // periodically retry - if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) { - RS_DBG("retrying"); - - mLastProxyCheck = time(NULL); - mErrorMsg.clear(); - - // create fake ticket - taskTicket *tt = rsAutoProxyMonitor::getTicket(); - tt->task = autoProxyTask::start; - mPending.push(tt); - } - - // check for new tickets - if (!mPending.empty()) { - RS_DBG4("processing new ticket"); - - // reset and try new task - mTask = ctIdle; - mState = csIdel; - return sleepFactorFast; - } - - return sleepFactorDefault; -} - -RsSerialiser *p3I2pBob::setupSerialiser() -{ - RsSerialiser* rsSerialiser = new RsSerialiser(); - rsSerialiser->addSerialType(new RsGeneralConfigSerialiser()); - - return rsSerialiser; -} - -#define addKVS(_vitem, _kv, _key, _value) \ - _kv.key = _key;\ - _kv.value = _value;\ - _vitem->tlvkvs.pairs.push_back(_kv); - -#define addKVSInt(_vitem, _kv, _key, _value) \ - _kv.key = _key;\ - rs_sprintf(_kv.value, "%d", _value);\ - _vitem->tlvkvs.pairs.push_back(_kv); - -bool p3I2pBob::saveList(bool &cleanup, std::list &lst) -{ - RS_DBG4(""); - - cleanup = true; - RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; - RsTlvKeyValue kv; - - RS_STACK_MUTEX(mLock); - addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enable ? "TRUE" : "FALSE") - addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.address.privateKey) - addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.address.base32) - addKVSInt(vitem, kv, kConfigKeyInLength, mSetting.inLength) - addKVSInt(vitem, kv, kConfigKeyInQuantity, mSetting.inQuantity) - addKVSInt(vitem, kv, kConfigKeyInVariance, mSetting.inVariance) - addKVSInt(vitem, kv, kConfigKeyOutLength, mSetting.outLength) - addKVSInt(vitem, kv, kConfigKeyOutQuantity, mSetting.outQuantity) - addKVSInt(vitem, kv, kConfigKeyOutVariance, mSetting.outVariance) - - lst.push_back(vitem); - - return true; -} - -#undef addKVS -#undef addKVSUInt - -#define getKVSUInt(_kit, _key, _value) \ - else if (_kit->key == _key) {\ - std::istringstream is(_kit->value);\ - int tmp;\ - is >> tmp;\ - _value = (int8_t)tmp;\ - } - -bool p3I2pBob::loadList(std::list &load) -{ - RS_DBG4(""); - - for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { - RsConfigKeyValueSet *vitem = dynamic_cast(*it); - if(vitem != NULL) { - RS_STACK_MUTEX(mLock); - for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { - if (kit->key == kConfigKeyBOBEnable) - mSetting.enable = kit->value == "TRUE"; - else if (kit->key == kConfigKeyBOBKey) - mSetting.address.privateKey = kit->value; - else if (kit->key == kConfigKeyBOBAddr) - mSetting.address.base32 = kit->value; - getKVSUInt(kit, kConfigKeyInLength, mSetting.inLength) - getKVSUInt(kit, kConfigKeyInQuantity, mSetting.inQuantity) - getKVSUInt(kit, kConfigKeyInVariance, mSetting.inVariance) - getKVSUInt(kit, kConfigKeyOutLength, mSetting.outLength) - getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) - getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) - else - RS_DBG("unknown key: ", kit->key); - } - } - delete vitem; - } - - RS_STACK_MUTEX(mLock); - finalizeSettings_locked(); - mConfigLoaded = true; - - return true; -} - -#undef getKVSUInt - -void p3I2pBob::getBOBSettings(bobSettings *settings) -{ - if (settings == NULL) - return; - - RS_STACK_MUTEX(mLock); - *settings = mSetting; - -} - -void p3I2pBob::setBOBSettings(const bobSettings *settings) -{ - if (settings == NULL) - return; - - RS_STACK_MUTEX(mLock); - mSetting = *settings; - - IndicateConfigChanged(); - - // Note: - // We don't take care of updating a running BOB session here - // This can be done manually by stoping and restarting the session - - // Note2: - // In case there is no config yet to load - // finalize settings here instead - if (!mConfigLoaded) { - finalizeSettings_locked(); - mConfigLoaded = true; - } else { - updateSettings_locked(); - } -} - -void p3I2pBob::getStates(bobStates *bs) -{ - if (bs == NULL) - return; - - RS_STACK_MUTEX(mLock); - bs->cs = mState; - bs->ct = mTask; - bs->bs = mBOBState; - bs->tunnelName = mTunnelName; -} - -std::string p3I2pBob::executeCommand(const std::string &command) -{ - RS_DBG4("running: ", command); - - std::string copy = command; - copy.push_back('\n'); - - // send command - // there is only one thread that touches mSocket - no need for a lock - ::send(mSocket, copy.c_str(), copy.size(), 0); - - // receive answer (trailing new line is already removed!) - std::string ans = recv(); - - RS_DBG4("answer: ", ans); - - return ans; -} - -bool p3I2pBob::connectI2P() -{ - // there is only one thread that touches mSocket - no need for a lock - - if (mSocket != 0) { - RS_DBG("mSocket != 0"); - return false; - } - - // create socket - mSocket = unix_socket(PF_INET, SOCK_STREAM, 0); - if (mSocket < 0) - { - RS_DBG("Failed to open socket! Socket Error: ", socket_errorType(errno)); - return false; - } - - // connect - int err = unix_connect(mSocket, mI2PProxyAddr); - if (err != 0) { - RS_DBG("Failed to connect to BOB! Socket Error: ", socket_errorType(errno)); - return false; - } - - // receive hello msg - recv(); - - RS_DBG4("done"); - return true; -} - -bool p3I2pBob::disconnectI2P() -{ - // there is only one thread that touches mSocket - no need for a lock - - if (mSocket == 0) { - RS_DBG("mSocket == 0"); - return true; - } - - int err = unix_close(mSocket); - if (err != 0) { - RS_DBG("Failed to close socket! Socket Error: ", socket_errorType(errno)); - return false; - } - - RS_DBG4("done"); - mSocket = 0; - return true; -} - -std::string toString(const std::string &a, const int b) { - std::ostringstream oss; - oss << b; - return a + oss.str();; -} - -std::string toString(const std::string &a, const uint16_t b) { - return toString(a, (int)b); -} - -std::string toString(const std::string &a, const int8_t b) { - return toString(a, (int)b); -} - -void p3I2pBob::finalizeSettings_locked() -{ - RS_DBG4(""); - - sockaddr_storage_clear(mI2PProxyAddr); - // get i2p proxy addr - sockaddr_storage proxy; - mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); - - // overwrite port to bob port - sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); - sockaddr_storage_setport(mI2PProxyAddr, 2827); - - RS_DBG4("using ", mI2PProxyAddr); - RS_DBG4("using ", mSetting.address.base32); - - peerState ps; - mPeerMgr->getOwnNetStatus(ps); - - // setup commands - // new lines are appended later! - - // generate 8 characater long random suffix for name - constexpr size_t len = 8; - const std::string location = RsRandom::alphaNumeric(len); - RS_DBG4("using suffix ", location); - mTunnelName = "RetroShare-" + location; - - const std::string setnick = "setnick RetroShare-" + location; - const std::string getnick = "getnick RetroShare-" + location; - const std::string newkeys = "newkeys"; - const std::string getkeys = "getkeys"; - const std::string setkeys = "setkeys " + mSetting.address.privateKey; - const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); - const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); - const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); - const std::string outport = toString("outport ", sockaddr_storage_port(ps.localaddr)); - // length - const std::string inlength = toString("option inbound.length=", mSetting.inLength); - const std::string outlength = toString("option outbound.length=", mSetting.outLength); - // variance - const std::string invariance = toString("option inbound.lengthVariance=", mSetting.inVariance); - const std::string outvariance= toString("option outbound.lengthVariance=", mSetting.outVariance); - // quantity - const std::string inquantity = toString("option inbound.quantity=", mSetting.inQuantity); - const std::string outquantity= toString("option outbound.quantity=", mSetting.outQuantity); - const std::string quiet = "quiet true"; - const std::string start = "start"; - const std::string stop = "stop"; - const std::string clear = "clear"; - const std::string list = "list"; - const std::string quit = "quit"; - - // setup state machine - - // start chain - // -> A: server and client tunnel - mCommands[bsSetnickS] = {setnick, bsSetkeys}; - mCommands[bsSetkeys] = {setkeys, bsOuthost}; - mCommands[bsOuthost] = {outhost, bsOutport}; - mCommands[bsOutport] = {outport, bsInhost}; - // -> B: only client tunnel - mCommands[bsSetnickC] = {setnick, bsNewkeysC}; - mCommands[bsNewkeysC] = {newkeys, bsInhost}; - // -> both - mCommands[bsInhost] = {inhost, bsInport}; - mCommands[bsInport] = {inport, bsInlength}; - mCommands[bsInlength] = {inlength, bsOutlength}; - mCommands[bsOutlength] = {outlength, bsInvariance}; - mCommands[bsInvariance] = {invariance, bsOutvariance}; - mCommands[bsOutvariance]= {outvariance,bsInquantity}; - mCommands[bsInquantity] = {inquantity, bsOutquantity}; - mCommands[bsOutquantity]= {outquantity,bsQuiet}; - mCommands[bsQuiet] = {quiet, bsStart}; - mCommands[bsStart] = {start, bsQuit}; - mCommands[bsQuit] = {quit, bsCleared}; - - // stop chain - mCommands[bsGetnick] = {getnick, bsStop}; - mCommands[bsStop] = {stop, bsClear}; - mCommands[bsClear] = {clear, bsQuit}; - - // getkeys chain - mCommands[bsSetnickN] = {setnick, bsNewkeysN}; - mCommands[bsNewkeysN] = {newkeys, bsGetkeys}; - mCommands[bsGetkeys] = {getkeys, bsClear}; - - // list chain - mCommands[bsList] = {list, bsQuit}; -} - -void p3I2pBob::updateSettings_locked() -{ - RS_DBG4(""); - - sockaddr_storage proxy; - mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); - - peerState ps; - mPeerMgr->getOwnNetStatus(ps); - - const std::string setkeys = "setkeys " + mSetting.address.privateKey; - const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); - const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); - const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); - const std::string outport = toString("outport ", sockaddr_storage_port(ps.localaddr)); - - // length - const std::string inlength = toString("option inbound.length=", mSetting.inLength); - const std::string outlength = toString("option outbound.length=", mSetting.outLength); - // variance - const std::string invariance = toString("option inbound.lengthVariance=", mSetting.inVariance); - const std::string outvariance= toString("option outbound.lengthVariance=", mSetting.outVariance); - // quantity - const std::string inquantity = toString("option inbound.quantity=", mSetting.inQuantity); - const std::string outquantity= toString("option outbound.quantity=", mSetting.outQuantity); - - mCommands[bsSetkeys] = {setkeys, bsOuthost}; - mCommands[bsOuthost] = {outhost, bsOutport}; - mCommands[bsOutport] = {outport, bsInhost}; - mCommands[bsInhost] = {inhost, bsInport}; - mCommands[bsInport] = {inport, bsInlength}; - - mCommands[bsInlength] = {inlength, bsOutlength}; - mCommands[bsOutlength] = {outlength, bsInvariance}; - mCommands[bsInvariance] = {invariance, bsOutvariance}; - mCommands[bsOutvariance]= {outvariance,bsInquantity}; - mCommands[bsInquantity] = {inquantity, bsOutquantity}; - mCommands[bsOutquantity]= {outquantity,bsQuiet}; -} - -std::string p3I2pBob::recv() -{ - // BOB works line based - // -> \n indicates and of the line - - constexpr uint16_t bufferSize = 128; - char buffer[bufferSize]; - - std::string ans; - uint16_t retry = 10; - - do { - memset(buffer, 0, bufferSize); - - // peek at data - auto length = ::recv(mSocket, buffer, bufferSize, MSG_PEEK); - if (length <= 0) { - if (length < 0) { - // error - perror(__PRETTY_FUNCTION__); - } - retry--; - doSleep(sleepTimeRecv); - continue; - } - - // at least one byte was read - - // search for new line - auto bufferStr = std::string(buffer); - size_t pos = bufferStr.find('\n'); - - if (pos == std::string::npos) { - // no new line found -> more to read - - // sanity check - if (length != bufferSize) { - // expectation: a full buffer was peeked) - RS_DBG1("peeked less than bufferSize but also didn't found a new line character"); - } - // this should never happen - assert(length <= bufferSize); - } else { - // new line found -> end of message - - // calculate how much there is to read, read the \n, too! - length = pos + 1; - - // end loop - retry = 0; - } - - // now read for real - memset(buffer, 0, bufferSize); - length = ::recv(mSocket, buffer, length, 0); - bufferStr = std::string(buffer); - ans.append(bufferStr); - } while(retry > 0); - - return ans; -} diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.h b/libretroshare/src/services/autoproxy/p3i2pbob.h deleted file mode 100644 index 29bcbcb61..000000000 --- a/libretroshare/src/services/autoproxy/p3i2pbob.h +++ /dev/null @@ -1,256 +0,0 @@ -/******************************************************************************* - * libretroshare/src/services/autoproxy: p3i2pbob.h * - * * - * libretroshare: retroshare core library * - * * - * Copyright 2016 by Sehraf * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ -#ifndef P3I2PBOB_H -#define P3I2PBOB_H - -#include -#include -#include -#include "util/rstime.h" -#ifndef WINDOWS_SYS - #include -#endif - -#include "pqi/p3cfgmgr.h" -#include "services/autoproxy/rsautoproxymonitor.h" -#include "util/rsthreads.h" -#include "util/i2pcommon.h" - -/* - * This class implements I2P BOB (BASIC OPEN BRIDGE) communication to allow RS - * to automatically remote control I2P to setup the needed tunnel. - * BOB is a simple text-based interface: https://geti2p.net/en/docs/api/bob - * - * Note 1: - * One tunnel is enough even for hidden locations since it can be used - * bidirectional. (In contrast to what RS I2P users had to set up manually.) - * - * Note 2: - * BOB tunnels are no SOCKS tunnel. Therefore pqissli2pbob implements a simplified - * proxy specially for BOB tunnels. - * - * Note 3: - * BOB needs a unique name as an ID for each tunnel. - * We use 'RetroShare-' + 8 random base32 characters. - * - * Design: - * The service uses three state machines to manage its task: - * int stateMachineBOB(); - * mBOBState - * int stateMachineController(); - * mState - * mTask - * - * stateMachineBOB: - * This state machine manages the low level communication with BOB. It basically has a linked - * list (currently a implemented as a std::map) that contains a command and the next - * state. - * Each high level operation (start up / shut down / get keys) is represented by a - * chain of states. E.g. the chain to retrieve new keys: - * mCommands[bobState::setnickN] = {setnick, bobState::newkeysN}; - * mCommands[bobState::newkeysN] = {newkeys, bobState::getkeys}; - * mCommands[bobState::getkeys] = {getkeys, bobState::clear}; - * mCommands[bobState::clear] = {clear, bobState::quit}; - * mCommands[bobState::quit] = {quit, bobState::cleared}; - * - * stateMachineController: - * This state machine manages the high level tasks. - * It is controlled by mState and mTask. - * - * mTast: - * Tracks the high level operation (like start up). - * It will keep its value even when a task is done to track - * the requested BOB state. - * When other operations are performed like a conection check - * the last task gets backed up and is later restored again - * - * mState: - * This state lives only for one operation an manages the communication - * with the BOB instance. This is basically connecting, starting BOB - * protocol and disconnecting - * - * How a task looks like: - * 1) RS sets task using the ticket system - * 2) stateMachineController connects to BOBs control port, sets mBobState to a lists head - * 3) stateMachineBOB processes command chain - * 4) stateMachineBOB is done and sets mBobState to cleared signaling that the connection - * is cleared and can be closed - * 5) stateMachineController disconnects from BOBs control port and updates mState - */ - -/// -/// \brief The controllerState enum -/// States for the controller to keep track of what he is currently doing -enum controllerState { - csIdel, - csDoConnect, - csConnected, - csWaitForBob, - csDoDisconnect, - csDisconnected, - csError -}; - -/// -/// \brief The controllerTask enum -/// This state tracks the controllers tast (e.g. setup a BOB tunnel or shut down -/// an existing one). -enum controllerTask { - ctIdle, - ctRunSetUp, - ctRunShutDown, - ctRunGetKeys, - ctRunCheck -}; - -/// -/// \brief The bobState enum -/// One state for each message -/// -enum bobState { - bsCleared, - bsSetnickC, // chain head for only client tunnel - bsSetnickN, // chain head for getting new (server) keys - bsSetnickS, // chain head for client and server tunnel - bsGetnick, - bsNewkeysC, // part of chain for only client tunnel - bsNewkeysN, // part of chain for getting new (server) keys - bsGetkeys, - bsSetkeys, - bsInhost, - bsOuthost, - bsInport, - bsOutport, - bsInlength, - bsOutlength, - bsInvariance, - bsOutvariance, - bsInquantity, - bsOutquantity, - bsQuiet, - bsStart, - bsStop, - bsClear, - bsList, // chain head for 'list' command - bsQuit -}; - -/// -/// \brief The bobStateInfo struct -/// State machine with commands -/// \todo This could be replaced by a linked list instead of a map -struct bobStateInfo { - std::string command; - bobState nextState; -}; - -struct bobSettings : i2p::settings {}; - -/// -/// \brief The bobStates struct -/// This container struct is used to pass all states. -/// Additionally, the tunnel name is included to to show it in the GUI. -/// The advantage of a struct is that it can be forward declared. -struct bobStates { - bobState bs; - controllerState cs; - controllerTask ct; - - std::string tunnelName; -}; - -class p3PeerMgr; - -class p3I2pBob : public RsTickingThread, public p3Config, public autoProxyService -{ -public: - explicit p3I2pBob(p3PeerMgr *peerMgr); - - // autoProxyService interface -public: - bool isEnabled(); - bool initialSetup(std::string &addr, uint16_t &); - void processTaskAsync(taskTicket *ticket); - void processTaskSync(taskTicket *ticket); - - void threadTick() override; /// @see RsTickingThread - -private: - int stateMachineBOB(); - int stateMachineBOB_locked_failure(const std::string &answer, const bobStateInfo ¤tState); - - int stateMachineController(); - int stateMachineController_locked_idle(); - int stateMachineController_locked_connected(); - int stateMachineController_locked_disconnected(); - int stateMachineController_locked_error(); - - // p3Config interface -protected: - RsSerialiser *setupSerialiser(); - bool saveList(bool &cleanup, std::list &lst); - bool loadList(std::list &load); - -private: - // helpers - void getBOBSettings(bobSettings *settings); - void setBOBSettings(const bobSettings *settings); - void getStates(bobStates *bs); - - std::string executeCommand(const std::string &command); - bool connectI2P(); - bool disconnectI2P(); - - void finalizeSettings_locked(); - void updateSettings_locked(); - - std::string recv(); - - // states for state machines - controllerState mState; - controllerTask mTask; - // used to store old state when in error state - // mStateOld is also used as a flag when an error occured in BOB protocol - controllerState mStateOld; - // mTaskOld is used to keep the previous task (start up / shut down) when requesting keys or checking the connection - controllerTask mTaskOld; - bobSettings mSetting; - bobState mBOBState; - - // used variables - p3PeerMgr *mPeerMgr; - bool mConfigLoaded; - int mSocket; - rstime_t mLastProxyCheck; - sockaddr_storage mI2PProxyAddr; - std::map mCommands; - std::string mErrorMsg; - std::string mTunnelName; - - std::queue mPending; - taskTicket *mProcessing; - - // mutex - RsMutex mLock; -}; - -#endif // P3I2PBOB_H diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.cpp b/libretroshare/src/services/autoproxy/p3i2psam3.cpp new file mode 100644 index 000000000..c9e77273e --- /dev/null +++ b/libretroshare/src/services/autoproxy/p3i2psam3.cpp @@ -0,0 +1,798 @@ +#include "p3i2psam3.h" + +#include + +#include "pqi/p3peermgr.h" +#include "rsitems/rsconfigitems.h" + + +static const std::string kConfigKeySAM3Enable = "SAM3_ENABLE"; + +static const std::string kConfigKeyDestPriv = "DEST_PRIV"; + +static const std::string kConfigKeyInLength = "IN_LENGTH"; +static const std::string kConfigKeyInQuantity = "IN_QUANTITY"; +static const std::string kConfigKeyInVariance = "IN_VARIANCE"; +static const std::string kConfigKeyInBackupQuantity = "IN_BACKUPQUANTITY"; + +static const std::string kConfigKeyOutLength = "OUT_LENGTH"; +static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY"; +static const std::string kConfigKeyOutVariance = "OUT_VARIANCE"; +static const std::string kConfigKeyOutBackupQuantity = "OUT_BACKUPQUANTITY"; + +#ifdef RS_I2P_SAM3_BOB_COMPAT +// used for migration from BOB to SAM +static const std::string kConfigKeyBOBEnable = "BOB_ENABLE"; +static const std::string kConfigKeyBOBKey = "BOB_KEY"; +static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; +#endif + +static constexpr bool kDefaultSAM3Enable = false; + +RS_SET_CONTEXT_DEBUG_LEVEL(2) + +static void inline doSleep(std::chrono::duration> timeToSleepMS) { + std::this_thread::sleep_for(timeToSleepMS); +} + +p3I2pSam3::p3I2pSam3(p3PeerMgr *peerMgr) : + mConfigLoaded(false), mPeerMgr(peerMgr), mPending(), mLock("p3i2p-sam3") +#ifdef RS_USE_I2P_SAM3_LIBSAM3 + , mLockSam3Access("p3i2p-sam3-access") +#endif +{ + RS_DBG4(); + + // set defaults + mSetting.initDefault(); + mSetting.enable = kDefaultSAM3Enable; + mSetting.session = nullptr; + + libsam3_debug = 0; +} + +bool p3I2pSam3::isEnabled() +{ + RS_STACK_MUTEX(mLock); + return mSetting.enable; +} + +bool p3I2pSam3::initialSetup(std::string &addr, uint16_t &/*port*/) +{ + RS_DBG4(); + + RS_STACK_MUTEX(mLock); + + if (!mSetting.address.publicKey.empty() || !mSetting.address.privateKey.empty()) + RS_WARN("overwriting keys!"); + + bool success = generateKey(mSetting.address.publicKey, mSetting.address.privateKey); + + if (!success) { + RS_WARN("failed to retrieve keys"); + return false; + } else { + std::string s, c; + i2p::getKeyTypes(mSetting.address.publicKey, s, c); + RS_INFO("received key ", s, " ", c); + RS_INFO("public key: ", mSetting.address.publicKey); + RS_INFO("private key: ", mSetting.address.privateKey); + RS_INFO("address: ", i2p::keyToBase32Addr(mSetting.address.publicKey)); + + // sanity check + auto pub = i2p::publicKeyFromPrivate(mSetting.address.privateKey); + RS_INFO("pub key derived: ", pub); + RS_INFO("address: ", i2p::keyToBase32Addr(pub)); + if (pub != mSetting.address.publicKey) { + RS_WARN("public key does not match private key! fixing ..."); + mSetting.address.publicKey = pub; + } + + mSetting.address.base32 = i2p::keyToBase32Addr(mSetting.address.publicKey); + + IndicateConfigChanged(); + } + + addr = mSetting.address.base32; + return true; +} + +void p3I2pSam3::processTaskAsync(taskTicket *ticket) +{ + RS_DBG4(); + + switch (ticket->task) { + case autoProxyTask::stop: [[fallthrough]]; + case autoProxyTask::start: [[fallthrough]]; + case autoProxyTask::receiveKey: [[fallthrough]]; + case autoProxyTask::lookupKey: [[fallthrough]]; + case autoProxyTask::proxyStatusCheck: [[fallthrough]]; + case autoProxyTask::establishConnection: [[fallthrough]]; + case autoProxyTask::closeConnection: + { + RS_STACK_MUTEX(mLock); + mPending.push(ticket); + } + break; + case autoProxyTask::status: [[fallthrough]]; + case autoProxyTask::getSettings: [[fallthrough]]; + case autoProxyTask::setSettings: [[fallthrough]]; + case autoProxyTask::getErrorInfo: [[fallthrough]]; + case autoProxyTask::reloadConfig: + // These are supposed to be sync! + RS_DBG("unknown task or sync one!"); + rsAutoProxyMonitor::taskError(ticket); + break; + } +} + +void p3I2pSam3::processTaskSync(taskTicket *ticket) +{ +// RS_DBG4(); + + const bool data = !!ticket->data; + + switch (ticket->task) { + case autoProxyTask::status: + { + samStatus *ss = static_cast(ticket->data); + RS_STACK_MUTEX(mLock); + ss->state = mState; + if (mSetting.session) + ss->sessionName = mSetting.session->channel; + else + ss->sessionName = "none"; + } + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + + break; + case autoProxyTask::getSettings: + // check if everything needed is set + if (!data) { + RS_DBG("autoProxyTask::getSettings data is missing"); + rsAutoProxyMonitor::taskError(ticket); + break; + } + + // get settings + { + RS_STACK_MUTEX(mLock); + *static_cast(ticket->data) = mSetting; + } + + // finish task + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + break; + case autoProxyTask::setSettings: + // check if everything needed is set + if (!data) { + RS_DBG("autoProxyTask::setSettings data is missing"); + rsAutoProxyMonitor::taskError(ticket); + break; + } + + // set settings + { + RS_STACK_MUTEX(mLock); + mSetting = *static_cast(ticket->data); + updateSettings_locked(); + } + + // finish task + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + break; + case autoProxyTask::getErrorInfo: + *static_cast(ticket->data) = mSetting.session->error; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + break; + case autoProxyTask::reloadConfig: + { + RS_STACK_MUTEX(mLock); + updateSettings_locked(); + } + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + break; + case autoProxyTask::stop: +#if 0 // doesn't seem to work, socket stays "CLOSE_WAIT" + // there can be a case where libsam3 will block forever because for some reason it does not detect that the socket it has is dead + // as a workaroung kill it from here + if (mState == samStatus::samState::connectSession || mState == samStatus::samState::connectForward) { + // lock should be held by the main thread + if (!mTmpSession) { + // now it's getting weird + RS_WARN("session is nullptr but mState says it is connecting."); + // no break! just ignore for now ... + } else { + // just close it from here, libsam3 is not thread safe. + // a bit of a hack but should do the trick +// sam3CloseSession(mSetting.session); + sam3tcpDisconnect(mTmpSession->fd); + // no break! continue as usual to keep everything in line + } + } +#endif + [[fallthrough]]; + case autoProxyTask::start: [[fallthrough]]; + case autoProxyTask::receiveKey: [[fallthrough]]; + case autoProxyTask::lookupKey: [[fallthrough]]; + case autoProxyTask::proxyStatusCheck: [[fallthrough]]; + case autoProxyTask::establishConnection: [[fallthrough]]; + case autoProxyTask::closeConnection: + // These are supposed to be async! + RS_WARN("unknown task or async one!"); + rsAutoProxyMonitor::taskError(ticket); + break; + } +} + +void p3I2pSam3::threadTick() +{ +// { +// RS_STACK_MUTEX(mLock); +// Dbg4() << __PRETTY_FUNCTION__ << " mPending: " << mPending.size() << std::endl; +// } + + if(mPending.empty()) { + // sleep outisde of lock! + doSleep(std::chrono::milliseconds(250)); + return; + } + + // get task + taskTicket *tt = nullptr; + { + RS_STACK_MUTEX(mLock); + tt = mPending.front(); + mPending.pop(); + } + + switch (tt->task) { + case autoProxyTask::stop: + mState = samStatus::samState::offline; + stopForwarding(); + stopSession(); + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::offline); + break; + + case autoProxyTask::start: + { + if (!mSetting.enable) { + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::disabled); + break; + } + + // create main session + mState = samStatus::samState::connectSession; + bool ret = startSession(); + if (!ret) { + mState = samStatus::samState::offline; + + rsAutoProxyMonitor::taskError(tt); + break; + } + + // start forwarding + mState = samStatus::samState::connectForward; + ret = startForwarding(); + + // finish ticket + if (ret) { + mState = samStatus::samState::online; + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::online); + } else { + mState = samStatus::samState::offline; + rsAutoProxyMonitor::taskError(tt); + } + } + break; + + case autoProxyTask::receiveKey: + { + i2p::address *addr = static_cast(tt->data); + if (generateKey(addr->publicKey, addr->privateKey)) { + addr->base32 = i2p::keyToBase32Addr(addr->publicKey); + rsAutoProxyMonitor::taskDone(tt, autoProxyStatus::ok); + } else { + rsAutoProxyMonitor::taskError(tt); + } + } + break; + + case autoProxyTask::lookupKey: + lookupKey(tt); + break; + + case autoProxyTask::proxyStatusCheck: + { + // TODO better detection of status + bool ok; + ok = !!mSetting.session->fd; + ok &= !!mSetting.session->fwd_fd; + *static_cast(tt->data) = ok; + rsAutoProxyMonitor::taskDone(tt, ok ? autoProxyStatus::ok : autoProxyStatus::error); + } + break; + + case autoProxyTask::establishConnection: + establishConnection(tt); + break; + case autoProxyTask::closeConnection: + closeConnection(tt); + break; + case autoProxyTask::status: [[fallthrough]]; + case autoProxyTask::getSettings: [[fallthrough]]; + case autoProxyTask::setSettings: [[fallthrough]]; + case autoProxyTask::getErrorInfo: [[fallthrough]]; + case autoProxyTask::reloadConfig: + RS_ERR("unable to handle! This is a bug! task:", tt->task); + rsAutoProxyMonitor::taskError(tt); + break; + } + tt = nullptr; + + // give i2p backend some time + doSleep(std::chrono::milliseconds(100)); +} + +RsSerialiser *p3I2pSam3::setupSerialiser() +{ + RsSerialiser* rsSerialiser = new RsSerialiser(); + rsSerialiser->addSerialType(new RsGeneralConfigSerialiser()); + + return rsSerialiser; +} + +#define addKVS(_key, _value) \ + kv.key = _key;\ + kv.value = _value;\ + vitem->tlvkvs.pairs.push_back(kv); + +#define addKVSInt(_key, _value) \ + kv.key = _key;\ + rs_sprintf(kv.value, "%d", _value);\ + vitem->tlvkvs.pairs.push_back(kv); + +bool p3I2pSam3::saveList(bool &cleanup, std::list &lst) +{ + RS_DBG4(); + + cleanup = true; + RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; + RsTlvKeyValue kv; + + RS_STACK_MUTEX(mLock); + addKVS(kConfigKeySAM3Enable, mSetting.enable ? "TRUE" : "FALSE") + addKVS(kConfigKeyDestPriv, mSetting.address.privateKey); + + addKVSInt(kConfigKeyInLength, mSetting.inLength) + addKVSInt(kConfigKeyInQuantity, mSetting.inQuantity) + addKVSInt(kConfigKeyInVariance, mSetting.inVariance) + addKVSInt(kConfigKeyInBackupQuantity, mSetting.inBackupQuantity) + + addKVSInt(kConfigKeyOutLength, mSetting.outLength) + addKVSInt(kConfigKeyOutQuantity, mSetting.outQuantity) + addKVSInt(kConfigKeyOutVariance, mSetting.outVariance) + addKVSInt(kConfigKeyOutBackupQuantity, mSetting.outBackupQuantity) + +#ifdef RS_I2P_SAM3_BOB_COMPAT + // these allow SAMv3 users to switch back to BOB + // remove after some time + addKVS(kConfigKeyBOBEnable, mSetting.enable ? "TRUE" : "FALSE") + addKVS(kConfigKeyBOBKey, mSetting.address.privateKey) + addKVS(kConfigKeyBOBAddr, mSetting.address.base32) +#endif + lst.push_back(vitem); + return true; +} + +#undef addKVS +#undef addKVSUInt + +#define getKVSUInt(_kit, _key, _value) \ + else if (_kit->key == _key) {\ + std::istringstream is(_kit->value);\ + int tmp;\ + is >> tmp;\ + _value = (int8_t)tmp;\ + } + +bool p3I2pSam3::loadList(std::list &load) +{ + RS_DBG4(); + + std::string priv; + priv.clear(); + + for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { + RsConfigKeyValueSet *vitem = dynamic_cast(*it); + if(vitem != NULL) { + RS_STACK_MUTEX(mLock); + for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { + if (kit->key == kConfigKeySAM3Enable) + mSetting.enable = kit->value == "TRUE"; + else if (kit->key == kConfigKeyDestPriv) + priv = kit->value; + getKVSUInt(kit, kConfigKeyInLength, mSetting.inLength) + getKVSUInt(kit, kConfigKeyInQuantity, mSetting.inQuantity) + getKVSUInt(kit, kConfigKeyInVariance, mSetting.inVariance) + getKVSUInt(kit, kConfigKeyInBackupQuantity, mSetting.inBackupQuantity) + + getKVSUInt(kit, kConfigKeyOutLength, mSetting.outLength) + getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) + getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) + getKVSUInt(kit, kConfigKeyOutBackupQuantity, mSetting.outBackupQuantity) + +#ifdef RS_I2P_SAM3_BOB_COMPAT + // import BOB settings + else if (kit->key == kConfigKeyBOBEnable) + mSetting.enable = kit->value == "TRUE"; + else if (kit->key == kConfigKeyBOBKey) { + // don't overwirte, just import when not set already! + if (priv.empty()) + priv = kit->value; + } +#endif + else + RS_INFO("unknown key:", kit->key); + } + } + delete vitem; + } + + // get the pub key + std::string pub = i2p::publicKeyFromPrivate(priv); + if (pub.empty() || priv.empty()) + RS_DBG("no destination to load"); + else { + RS_STACK_MUTEX(mLock); + + mSetting.address.publicKey = pub; + mSetting.address.privateKey = priv; + mSetting.address.base32 = i2p::keyToBase32Addr(pub); + } + + RS_STACK_MUTEX(mLock); + mConfigLoaded = true; + + return true; +} + +#undef getKVSUInt + +bool p3I2pSam3::startSession() +{ + RS_DBG4(); + + constexpr size_t len = 8; + const std::string location = RsRandom::alphaNumeric(len); + const std::string nick = "RetroShare-" + location; + + std::vector params; + { + RS_STACK_MUTEX(mLock); + + // length + params.push_back(i2p::makeOption("inbound.length", mSetting.inLength)); + params.push_back(i2p::makeOption("outbound.length", mSetting.outLength)); + // variance + params.push_back(i2p::makeOption("inbound.lengthVariance", + mSetting.inVariance)); + params.push_back(i2p::makeOption("outbound.lengthVariance", + mSetting.outVariance)); + // quantity + params.push_back(i2p::makeOption("inbound.quantity", + mSetting.inQuantity)); + params.push_back(i2p::makeOption("outbound.quantity", + mSetting.outQuantity)); + // backup quantity + params.push_back(i2p::makeOption("inbound.backupQuantity", + mSetting.inBackupQuantity)); + params.push_back(i2p::makeOption("outbound.backupQuantity", + mSetting.outBackupQuantity)); + } + + std::string paramsStr; + for (auto &&p : params) + paramsStr.append(p + " "); + // keep trailing space for easier extending when necessary + + int ret; + + if (mSetting.session) { + stopSession(); + } + + auto session = (Sam3Session*)rs_malloc(sizeof (Sam3Session)); + + // add nick + paramsStr.append("inbound.nickname=" + nick); // leading space is already there + + { + RS_STACK_MUTEX(mLockSam3Access); + + if(!mSetting.address.privateKey.empty()) { + RS_DBG3("with destination"); + ret = sam3CreateSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, mSetting.address.privateKey.c_str(), Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); + } else { + RS_DBG("without destination"); + ret = sam3CreateSession(session, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, SAM3_DESTINATION_TRANSIENT, Sam3SessionType::SAM3_SESSION_STREAM, Sam3SigType::EdDSA_SHA512_Ed25519, paramsStr.c_str()); + } + } + + if (ret != 0) { + delete session; + session = nullptr; + return false; + } + +#if 0 // this check is useless. For non i2p hidden locations the public key is temporal anyway and for i2p hidden ones, it is part of the (fixed) private key. + if (!mSetting.address.publicKey.empty() && mSetting.address.publicKey != session->pubkey) + // This should be ok for non hidden locations. This should be a problem for hidden i2p locations... + RS_DBG("public key changed! Yet unsure if this is ok or a problem. Should be fine for non i2p hidden locations or clear net."); +#endif + /* + * Note: sam3CreateSession will issue a name looup of "ME" to receive its public key, thus it is always correct. + * No need to use i2p::publicKeyFromPrivate() + */ + RS_STACK_MUTEX(mLock); + mSetting.session = session; + mSetting.address.publicKey = session->pubkey; + mSetting.address.base32 = i2p::keyToBase32Addr(session->pubkey); + // do not overwrite the private key, if any!! + + RS_DBG1("nick: ", nick, " address: ", mSetting.address.base32); + RS_DBG2(" myDestination.pub ", mSetting.address.publicKey); + RS_DBG2(" myDestination.priv ", mSetting.address.privateKey); + return true; +} + +bool p3I2pSam3::startForwarding() +{ + RS_DBG4(); + + if(mSetting.address.privateKey.empty()) { + RS_DBG3("no private key set"); + // IMPORANT: return true here! + // since there is no forward session for non hidden nodes, this funtion is successfull by doing nothing + return true; + } + + if (!mSetting.session) { + RS_WARN("no session found!"); + return false; + } + + peerState ps; + mPeerMgr->getOwnNetStatus(ps); + + RS_STACK_MUTEX(mLockSam3Access); + + mSetting.session->silent = true; + int ret = sam3StreamForward(mSetting.session, sockaddr_storage_iptostring(ps.localaddr).c_str(), sockaddr_storage_port(ps.localaddr)); + if (ret < 0) { + RS_DBG("forward failed, due to", mSetting.session->error); + return false; + } + + return true; +} + +void p3I2pSam3::stopSession() +{ + RS_DBG4(); + + { + RS_STACK_MUTEX(mLock); + if (!mSetting.session) + return; + + // swap connections + mInvalidConnections = mValidConnections; + mValidConnections.clear(); + + RS_STACK_MUTEX(mLockSam3Access); + sam3CloseSession(mSetting.session); + free(mSetting.session); + + mSetting.session = nullptr; + mState = samStatus::samState::offline; + } + + // At least i2pd doesn't like to instantaniously stop and (re)start a session, wait here just a little bit. + // Not ideal but does the trick. + // (This happens when using the "restart" button in the settings.) + doSleep(std::chrono::seconds(10)); +} + +void p3I2pSam3::stopForwarding() +{ + // nothing to do here, forwarding is stop when closing the seassion +} + +bool p3I2pSam3::generateKey(std::string &pub, std::string &priv) +{ + RS_DBG4(); + + pub.clear(); + priv.clear(); + + // The session is only usef for transporting the data + Sam3Session ss; + + if (0 > sam3GenerateKeys(&ss, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, Sam3SigType::EdDSA_SHA512_Ed25519)) { + RS_DBG("got error: ", ss.error); + return false; + } + + pub = std::string(ss.pubkey); + priv = std::string(ss.privkey); + + // sanity check + auto p = i2p::publicKeyFromPrivate(priv); + if (p != pub) { + RS_WARN("public key does not match private key! fixing ..."); + pub = p; + } + + RS_DBG2("publuc key / address ", pub); + RS_DBG2("private key ", priv); + + return true; +} + +void p3I2pSam3::lookupKey(taskTicket *ticket) +{ + // this can be called independend of the main SAM session! + + auto addr = static_cast(ticket->data); + if (addr->base32.empty()) { + RS_ERR("lookupKey: called with empty address"); + rsAutoProxyMonitor::taskError(ticket); + return; + } + + RsThread::async([ticket]() + { + auto addr = static_cast(ticket->data); + + // The session is only usef for transporting the data + Sam3Session ss; + int ret = sam3NameLookup(&ss, SAM3_HOST_DEFAULT, SAM3_PORT_DEFAULT, addr->base32.c_str()); + if (ret < 0) { + // get error + RS_DBG("key: ", addr->base32); + RS_DBG("got error: ", ss.error); + rsAutoProxyMonitor::taskError(ticket); + } else { + addr->publicKey = ss.destkey; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + RS_DBG1("success"); + } + }); +} + +void p3I2pSam3::establishConnection(taskTicket *ticket) +{ + if (mState != samStatus::samState::online || !mSetting.session) { + RS_WARN("no session found!"); + rsAutoProxyMonitor::taskError(ticket); + return; + } + + samEstablishConnectionWrapper *wrapper = static_cast(ticket->data); + if (wrapper->address.publicKey.empty()) { + RS_ERR("no public key given"); + rsAutoProxyMonitor::taskError(ticket); + return; + } + + RsThread::async([ticket, this]() { + auto wrapper = static_cast(ticket->data); + + struct Sam3Connection *connection; + { + auto l = this->mLockSam3Access; + RS_STACK_MUTEX(l); + mSetting.session->silent = false; + connection = sam3StreamConnect(this->mSetting.session, wrapper->address.publicKey.c_str()); + } + + if (!connection) { + // get error + RS_DBG("got error:", this->mSetting.session->error); + rsAutoProxyMonitor::taskError(ticket); + } else { + wrapper->connection = connection; + { + auto l = this->mLockSam3Access; + RS_STACK_MUTEX(l); + this->mValidConnections.push_back(connection); + } + RS_DBG1("success"); + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + } + }); +} + +void p3I2pSam3::closeConnection(taskTicket *ticket) +{ + Sam3Connection *conn = static_cast(ticket->data); + + if (mState == samStatus::samState::offline || !mSetting.session) { + // no session found, sam was likely stopped + RS_DBG2("no session found"); + + auto it = std::find(mInvalidConnections.begin(), mInvalidConnections.end(), conn); + if (it != mInvalidConnections.end()) { + // this is the expected case + mInvalidConnections.erase(it); + } else { + // this is unexpected but not a big deal, just warn + RS_WARN("cannot find connection in mInvalidConnections"); + + it = std::find(mValidConnections.begin(), mValidConnections.end(), conn); + if (it != mValidConnections.end()) { + mValidConnections.erase(it); + + // now it is getting even weirder, still not a big deal, just warn + RS_WARN("found connection in mValidConnections"); + } + } + + // when libsam3 has already handled closing of the connection - which should be the case here - the memory has been freed already (-> pointer is invalid) + conn = nullptr; + } else { + RS_STACK_MUTEX(mLock); + + bool callClose = true; + // search in current connections + auto it = std::find(mValidConnections.begin(), mValidConnections.end(), conn); + if (it != mValidConnections.end()) { + RS_DBG2("found valid connection"); + mValidConnections.erase(it); + } else { + // search in old connections + it = std::find(mInvalidConnections.begin(), mInvalidConnections.end(), conn); + if (it != mInvalidConnections.end()) { + // old connection, just ignore. *should* be freed already + mInvalidConnections.erase(it); + + RS_DBG2("found old (invalid) connection"); + + callClose = false; + conn = nullptr; + } else { + // weird + RS_WARN("could'n find connection!"); + + // best thing we can do here + callClose = false; + conn = nullptr; + } + } + + if (callClose) { + RS_DBG2("closing connection"); + + RS_STACK_MUTEX(mLockSam3Access); + sam3CloseConnection(conn); + conn = nullptr; // freed by above call + } + } + + if (conn) { + free(conn); + conn = nullptr; + } + + ticket->data = nullptr; + rsAutoProxyMonitor::taskDone(ticket, autoProxyStatus::ok); + return; +} + +void p3I2pSam3::updateSettings_locked() +{ + RS_DBG4(); + IndicateConfigChanged(); + +#if 0 // TODO recreat session when active, can we just recreat it? + if (mSs) { + stopSession(); + startSession(); + } +#endif +} diff --git a/libretroshare/src/services/autoproxy/p3i2psam3.h b/libretroshare/src/services/autoproxy/p3i2psam3.h new file mode 100644 index 000000000..c6b57615e --- /dev/null +++ b/libretroshare/src/services/autoproxy/p3i2psam3.h @@ -0,0 +1,110 @@ +#ifndef P3I2PSAM3_H +#define P3I2PSAM3_H + +#include +#include + +#include "services/autoproxy/rsautoproxymonitor.h" +#include "pqi/p3cfgmgr.h" +#include "util/i2pcommon.h" +#include "util/rsthreads.h" + +/* + * This class implements I2P SAMv3 (Simple Anonymous Messaging) to allow RS + * to automatically setup tunnel to and from I2P. + * SAMv3 is a simple text-based interface: https://geti2p.net/de/docs/api/samv3 + * + * For the actual SAM commands / low level stuff libsam3 (https://github.com/i2p/libsam3) + * is used with some minor adjustments, for exmaple, the FORWARD session is always silent. + * + * SAM in a nutshell works like this: + * 1) setup main/control session which configures everything (destination ID, tunnel number, hops number, and so on) + * 2) setup a forward session, so that I2P will establish a connection to RS for each incoming connection to our i2p destination + * 3a) query/lookup the destination (public key) for a given i2p address + * 3b) connect to the given destination + * + * An established connection (both incoming or outgoing) are then handed over to RS. + * The lifetime of a session (and its subordinates connections) is bound to their tcp socket. When the socket closes, the session is closed, too. + * + */ + +class p3PeerMgr; + +class Sam3Session; +class Sam3Connection; + +typedef Sam3Session samSession; + +struct samSettings : i2p::settings { + samSession *session; +}; + +struct samEstablishConnectionWrapper { + i2p::address address; + Sam3Connection *connection; +}; + +struct samStatus { + std::string sessionName; + enum samState { + offline, + connectSession, + connectForward, + online + } state; // the name is kinda redundant ... +}; + +class p3I2pSam3 : public RsTickingThread, public p3Config, public autoProxyService +{ +public: + p3I2pSam3(p3PeerMgr *peerMgr); + + // autoProxyService interface +public: + bool isEnabled(); + bool initialSetup(std::string &addr, uint16_t &port); + void processTaskAsync(taskTicket *ticket); + void processTaskSync(taskTicket *ticket); + + // RsTickingThread interface +public: + void threadTick(); /// @see RsTickingThread + + // p3Config interface +protected: + RsSerialiser *setupSerialiser(); + bool saveList(bool &cleanup, std::list &); + bool loadList(std::list &load); + +private: + bool startSession(); + bool startForwarding(); + void stopSession(); + void stopForwarding(); + + bool generateKey(std::string &pub, std::string &priv); + void lookupKey(taskTicket *ticket); + void establishConnection(taskTicket *ticket); + void closeConnection(taskTicket *ticket); + void updateSettings_locked(); + + bool mConfigLoaded; + + samSettings mSetting; + p3PeerMgr *mPeerMgr; + std::queue mPending; + + // Used to report the state to the gui + // (Since the create session call/will can block and there is no easy way from outside the main thread to see + // what is going on, it is easier to store the current state in an extra variable independen from the main thread) + samStatus::samState mState; + + // used to keep track of connections, libsam3 does it internally but it can be unreliable since pointers are shared + std::list mValidConnections, mInvalidConnections; + + // mutex + RsMutex mLock; + RsMutex mLockSam3Access; // libsam3 is not thread safe! (except for key lookup) +}; + +#endif // P3I2PSAM3_H diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index d58c871e3..b2d71571e 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -329,14 +329,22 @@ autoProxyService *rsAutoProxyMonitor::lookUpService(autoProxyType::autoProxyType bool rsAutoProxyMonitor::isAsyncTask(autoProxyTask::autoProxyTask_enum t) { + // Explicit list all values, so that missing ones will be detected by the compiler. switch (t) { - case autoProxyTask::start: - case autoProxyTask::stop: - case autoProxyTask::receiveKey: + case autoProxyTask::start: [[fallthrough]]; + case autoProxyTask::stop: [[fallthrough]]; + case autoProxyTask::receiveKey: [[fallthrough]]; + case autoProxyTask::lookupKey: [[fallthrough]]; + case autoProxyTask::establishConnection: [[fallthrough]]; + case autoProxyTask::closeConnection: return true; - break; - default: - break; + case autoProxyTask::status: [[fallthrough]]; + case autoProxyTask::getSettings: [[fallthrough]]; + case autoProxyTask::setSettings: [[fallthrough]]; + case autoProxyTask::getErrorInfo: [[fallthrough]]; + case autoProxyTask::reloadConfig: [[fallthrough]]; + case autoProxyTask::proxyStatusCheck: + return false; } return false; } diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.h b/libretroshare/src/services/autoproxy/rsautoproxymonitor.h index d9a4c16aa..e08692947 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.h +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.h @@ -31,23 +31,27 @@ class autoProxyCallback; namespace autoProxyType { enum autoProxyType_enum { - I2PBOB +// I2PBOB, + I2PSAM3 }; } namespace autoProxyTask { enum autoProxyTask_enum { /* async tasks */ - start, ///< start up proxy - stop, ///< shut down proxy - receiveKey, ///< renew proxy key (if any) - proxyStatusCheck, ///< use to check if the proxy is still running + start, ///< start up proxy + stop, ///< shut down proxy + receiveKey, ///< renew proxy key (if any) + lookupKey, ///< look up a base32 addr + proxyStatusCheck, ///< use to check if the proxy is still running + establishConnection, ///< create a connection to a given public key or base32 address + closeConnection, ///< closes a connection /* sync tasks */ - status, ///< get status from auto proxy - getSettings, ///< get setting from auto proxy - setSettings, ///< set setting of auto proxy - reloadConfig, ///< signal config reload/rebuild - getErrorInfo ///< get error information from auto proxy + status, ///< get status from auto proxy + getSettings, ///< get setting from auto proxy + setSettings, ///< set setting of auto proxy + reloadConfig, ///< signal config reload/rebuild + getErrorInfo ///< get error information from auto proxy }; } diff --git a/libretroshare/src/use_libretroshare.pri b/libretroshare/src/use_libretroshare.pri index ee8872104..0cca29150 100644 --- a/libretroshare/src/use_libretroshare.pri +++ b/libretroshare/src/use_libretroshare.pri @@ -90,6 +90,15 @@ rs_broadcast_discovery { win32-g++|win32-clang-g++:dLibs *= wsock32 } +rs_sam3_libsam3 { + LIBSAM3_SRC_PATH=$$clean_path($${RS_SRC_PATH}/supportlibs/libsam3/) + LIBSAM3_BUILD_PATH=$$clean_path($${RS_BUILD_PATH}/supportlibs/libsam3/) + INCLUDEPATH *= $$clean_path($${LIBSAM3_SRC_PATH}/src/libsam3/) + DEPENDPATH *= $$clean_path($${LIBSAM3_BUILD_PATH}) + QMAKE_LIBDIR *= $$clean_path($${LIBSAM3_BUILD_PATH}) + LIBS *= -L$$clean_path($${LIBSAM3_BUILD_PATH}) -lsam3 +} + static { sLibs *= $$mLibs } else { diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index ec2ebfd6b..524219eb3 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -50,8 +50,10 @@ std::string publicKeyFromPrivate(std::string const &priv) * https://geti2p.net/spec/common-structures#keysandcert * https://geti2p.net/spec/common-structures#certificate */ - if (priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys) + if (priv.length() < privKeyMinLenth_b64) { + RS_WARN("key to short!"); return std::string(); + } // creat a copy to work on, need to convert it to standard base64 auto priv_copy(priv); @@ -70,13 +72,14 @@ std::string publicKeyFromPrivate(std::string const &priv) uint8_t certType = 0; uint16_t len = 0; uint16_t signingKeyType = 0; - uint16_t cryptKey = 0; + uint16_t cryptKeyType = 0; // only used for easy break do { try { // jump to certificate p += publicKeyLen; + // try to read type and length certType = *p++; len = readTwoBytesBE(p); @@ -87,7 +90,7 @@ std::string publicKeyFromPrivate(std::string const &priv) /* * CertType.Null * type null is followed by 0x00 0x00 - * so has to be 0! + * so len has to be 0! */ RS_DBG("cert is CertType.Null"); publicKeyLen += 3; // add 0x00 0x00 0x00 @@ -119,7 +122,7 @@ std::string publicKeyFromPrivate(std::string const &priv) // likely 7 signingKeyType = readTwoBytesBE(p); - RS_DBG("signing pubkey type ", certType); + RS_DBG("signing pubkey type ", signingKeyType); if (signingKeyType >= 3 && signingKeyType <= 6) { RS_DBG("signing pubkey type ", certType, " has oversize"); // calculate oversize @@ -137,18 +140,18 @@ std::string publicKeyFromPrivate(std::string const &priv) return std::string(); } - publicKeyLen += values.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted + publicKeyLen += values.first - 128; // 128 = default DSA key length = the space that can be used before the key must be splitted } // Crypto Public Key // likely 0 - cryptKey = readTwoBytesBE(p); - RS_DBG("crypto pubkey type ", cryptKey); + cryptKeyType = readTwoBytesBE(p); + RS_DBG("crypto pubkey type ", cryptKeyType); // info: these are all smaller than the default 256 bytes, so no oversize calculation is needed break; } catch (const std::out_of_range &e) { - RS_DBG("hit exception! ", e.what()); + RS_DBG("hit an exception! ", e.what()); return std::string(); } } while(false); @@ -160,4 +163,107 @@ std::string publicKeyFromPrivate(std::string const &priv) return pub; } +bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &cryptoKey) +{ + if (key.length() < pubKeyMinLenth_b64) { + RS_WARN("key to short!"); + return false; + } + + // creat a copy to work on, need to convert it to standard base64 + auto key_copy(key); + std::replace(key_copy.begin(), key_copy.end(), '~', '/'); + // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too + // std::replace(copy.begin(), copy.end(), '-', '+'); + + // get raw data + std::vector data; + RsBase64::decode(key_copy, data); + + auto p = data.cbegin(); + + constexpr size_t publicKeyLen = 256 + 128; // default length (bytes) + uint8_t certType = 0; + uint16_t signingKeyType = 0; + uint16_t cryptKeyType = 0; + + // try to read types + try { + // jump to certificate + p += publicKeyLen; + + // try to read type and skip length + certType = *p++; + p += 2; + + // only 0 and 5 are used / valid at this point + // check for == 0 + if (certType == static_cast::type>(CertType::Null)) { + RS_DBG("cert is CertType.Null"); + + signingKey = "DSA_SHA1"; + cryptoKey = "ElGamal"; + return true; + } + + // check for != 5 + if (certType != static_cast::type>(CertType::Key)) { + // unsupported + RS_DBG("cert type ", certType, " is unsupported"); + return false; + } + + RS_DBG("cert is CertType.Key"); + + // Signing Public Key + // likely 7 + signingKeyType = readTwoBytesBE(p); + RS_DBG("signing pubkey type ", signingKeyType); + + // Crypto Public Key + // likely 0 + cryptKeyType = readTwoBytesBE(p); + RS_DBG("crypto pubkey type ", cryptKeyType); + } catch (const std::out_of_range &e) { + RS_DBG("hit an exception! ", e.what()); + return false; + } + + // now convert to string (this would be easier with c++17) +#define HELPER(a, b, c) \ + case static_cast::type>(a::c): \ + b = #c; \ + break; + + switch (signingKeyType) { + HELPER(SigningKeyType, signingKey, DSA_SHA1) + HELPER(SigningKeyType, signingKey, ECDSA_SHA256_P256) + HELPER(SigningKeyType, signingKey, ECDSA_SHA384_P384) + HELPER(SigningKeyType, signingKey, ECDSA_SHA512_P521) + HELPER(SigningKeyType, signingKey, RSA_SHA256_2048) + HELPER(SigningKeyType, signingKey, RSA_SHA384_3072) + HELPER(SigningKeyType, signingKey, RSA_SHA512_4096) + HELPER(SigningKeyType, signingKey, EdDSA_SHA512_Ed25519) + HELPER(SigningKeyType, signingKey, EdDSA_SHA512_Ed25519ph) + HELPER(SigningKeyType, signingKey, RedDSA_SHA512_Ed25519) + default: + RsWarn("unkown signing key type:", signingKeyType); + return false; + } + + switch (cryptKeyType) { + HELPER(CryptoKeyType, cryptoKey, ElGamal) + HELPER(CryptoKeyType, cryptoKey, P256) + HELPER(CryptoKeyType, cryptoKey, P384) + HELPER(CryptoKeyType, cryptoKey, P521) + HELPER(CryptoKeyType, cryptoKey, X25519) + default: + RsWarn("unkown crypto key type:", cryptKeyType); + return false; + } +#undef HELPER + + return true; +} + } // namespace i2p diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h index 1fd152079..f0da0322b 100644 --- a/libretroshare/src/util/i2pcommon.h +++ b/libretroshare/src/util/i2pcommon.h @@ -186,6 +186,39 @@ static const std::array, 12> signingKeyLengths { /*SigningKeyType::RedDSA_SHA512_Ed25519 */ std::make_pair( 32, 32), }; +/* + * Key length infos: + * + * BOB private key + * len b64: 884 + * len pln: 663 + * + * BOB public key / destination + * len b64: 516 + * len pln: 387 + * + * SAMv3 private key + * len b64: 908 + * len pln: 679 + * + * SAMv3 public key + * len b64: 516 + * len pln: 387 + * + * Example: + * in bytes, public key only + * 384 (Key) + 3 (Null certificate) = 387 bytes + * 384 (Key) + 7 (key certificate) = 391 bytes + * + * in bytes public + private key + * 384 (Key) + 3 (Null certificate) + 256 (ElGamal) + 20 (DSA_SHA1) = 663 bytes + * 384 (Key) + 7 (key certificate) + 256 (ElGamal) + 32 (EdDSA_SHA512_Ed25519) = 679 bytes + */ +constexpr size_t pubKeyMinLenth_b64 = 516; +constexpr size_t pubKeyMinLenth_bin = 387; +constexpr size_t privKeyMinLenth_b64 = 884; +constexpr size_t privKeyMinLenth_bin = 663; + /** * @brief makeOption Creates the string "lhs=rhs" used by BOB and SAM. Converts rhs * @param lhs option to set @@ -208,6 +241,15 @@ std::string keyToBase32Addr(const std::string &key); */ std::string publicKeyFromPrivate(const std::string &priv); +/** + * @brief getKeyTypes returns the name of the utilized algorithms used by the key + * @param key public key + * @param signingKey name of the signing key, e.g. DSA_SHA1 + * @param cryptoKey name of the crpyto key, e.g. ElGamal + * @return true on success, false otherwise + */ +bool getKeyTypes(const std::string &key, std::string &signingKey, std::string &cryptoKey); + } // namespace i2p #endif // I2PCOMMON_H diff --git a/plugins/plugins.pro b/plugins/plugins.pro index 209b82898..7ecbc32c7 100644 --- a/plugins/plugins.pro +++ b/plugins/plugins.pro @@ -19,5 +19,5 @@ TEMPLATE = subdirs SUBDIRS += \ - VOIP \ + VOIP \ FeedReader diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index e77095526..c0646f5e9 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -150,7 +150,7 @@ GenCertDialog::GenCertDialog(bool onlyGenerateIdentity, QWidget *parent) connect(ui.node_input, SIGNAL(textChanged(QString)), this, SLOT(updateCheckLabels())); connect(ui.reuse_existing_node_CB, SIGNAL(toggled(bool)), this, SLOT(updateCheckLabels())); - connect(ui.cbUseBob, SIGNAL(clicked(bool)), this, SLOT(useBobChecked(bool)));; + connect(ui.cbUseBob, SIGNAL(clicked(bool)), this, SLOT(useI2pChecked(bool)));; entropy_timer = new QTimer ; entropy_timer->start(20) ; @@ -341,9 +341,9 @@ void GenCertDialog::setupState() ui.hiddenport_spinBox->setVisible(hidden_state && !tor_auto); ui.cbUseBob->setVisible(hidden_state && !tor_auto); -#ifndef RS_USE_I2P_BOB +#ifndef RS_USE_I2P_SAM3 ui.cbUseBob->setDisabled(true); - ui.cbUseBob->setToolTip(tr("BOB support is not available")); + ui.cbUseBob->setToolTip(tr("SAMv3 support is not available")); #endif if(!mAllFieldsOk) @@ -440,10 +440,10 @@ void GenCertDialog::updateCheckLabels() setupState(); } -void GenCertDialog::useBobChecked(bool checked) +void GenCertDialog::useI2pChecked(bool checked) { if (checked) { - ui.hiddenaddr_input->setPlaceholderText(tr("I2P instance address with BOB enabled")); + ui.hiddenaddr_input->setPlaceholderText(tr("I2P instance address with SAMv3 enabled")); ui.hiddenaddr_label->setText(tr("I2P instance address")); ui.hiddenport_spinBox->setEnabled(false); @@ -539,12 +539,12 @@ void GenCertDialog::genPerson() std::string hl = ui.hiddenaddr_input->text().toStdString(); uint16_t port = ui.hiddenport_spinBox->value(); - bool useBob = ui.cbUseBob->isChecked(); + bool useI2p = ui.cbUseBob->isChecked(); - if (useBob && hl.empty()) + if (useI2p && hl.empty()) hl = "127.0.0.1"; - RsInit::SetHiddenLocation(hl, port, useBob); /* parses it */ + RsInit::SetHiddenLocation(hl, port, useI2p); /* parses it */ } diff --git a/retroshare-gui/src/gui/GenCertDialog.h b/retroshare-gui/src/gui/GenCertDialog.h index afd82e5f0..5bef44485 100644 --- a/retroshare-gui/src/gui/GenCertDialog.h +++ b/retroshare-gui/src/gui/GenCertDialog.h @@ -44,7 +44,7 @@ private slots: void switchReuseExistingNode(); void grabMouse(); void updateCheckLabels(); - void useBobChecked(bool checked); + void useI2pChecked(bool checked); private: void initKeyList(); diff --git a/retroshare-gui/src/gui/GenCertDialog.ui b/retroshare-gui/src/gui/GenCertDialog.ui index 96f09cae2..482150beb 100644 --- a/retroshare-gui/src/gui/GenCertDialog.ui +++ b/retroshare-gui/src/gui/GenCertDialog.ui @@ -496,7 +496,7 @@ - Use BOB + Use I2P @@ -764,8 +764,8 @@ importIdentity_PB - + diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index e17fc9919..cdbe5c3e3 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -47,6 +47,8 @@ #include #include +#include + #define ICON_STATUS_UNKNOWN ":/images/ledoff1.png" #define ICON_STATUS_WORKING ":/images/yellowled.png" #define ICON_STATUS_OK ":/images/ledon1.png" @@ -66,7 +68,7 @@ // Tabs numbers *after* non relevant tabs are removed. So do not use them to add/remove tabs!! const static uint32_t TAB_HIDDEN_SERVICE_OUTGOING = 0; const static uint32_t TAB_HIDDEN_SERVICE_INCOMING = 1; -const static uint32_t TAB_HIDDEN_SERVICE_I2P_BOB = 2; +const static uint32_t TAB_HIDDEN_SERVICE_I2P = 2; const static uint32_t TAB_NETWORK = 0; const static uint32_t TAB_HIDDEN_SERVICE = 1; @@ -79,14 +81,14 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) : ConfigPage(parent, flags) , manager(NULL), mOngoingConnectivityCheck(-1) , mIsHiddenNode(false), mHiddenType(RS_HIDDEN_TYPE_NONE) - , mBobAccessible(false) + , mSamAccessible(false) , mEventHandlerId(0) { /* Invoke the Qt Designer generated object setup routine */ ui.setupUi(this); -#ifndef RS_USE_I2P_BOB - ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important. +#ifndef RS_USE_I2P_SAM3 + ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P); // warning: the order of operation here is very important. #endif if(RsAccounts::isHiddenNode()) @@ -111,7 +113,7 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) ui.hiddenpage_outHeader->setText(tr("Tor has been automatically configured by Retroshare. You shouldn't need to change anything here.")) ; ui.hiddenpage_inHeader->setText(tr("Tor has been automatically configured by Retroshare. You shouldn't need to change anything here.")) ; - ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important. + ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P); // warning: the order of operation here is very important. } } else @@ -166,14 +168,14 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) QObject::connect(ui.filteredIpsTable,SIGNAL(currentCellChanged(int,int,int,int)),this,SLOT(updateSelectedBlackListIP(int,int,int,int))); QObject::connect(ui.whiteListIpsTable,SIGNAL(currentCellChanged(int,int,int,int)),this,SLOT(updateSelectedWhiteListIP(int,int,int,int))); - QObject::connect(ui.pbBobStart, SIGNAL(clicked()), this, SLOT(startBOB())); - QObject::connect(ui.pbBobRestart, SIGNAL(clicked()), this, SLOT(restartBOB())); - QObject::connect(ui.pbBobStop, SIGNAL(clicked()), this, SLOT(stopBOB())); + QObject::connect(ui.pbBobStart, SIGNAL(clicked()), this, SLOT(startSam())); + QObject::connect(ui.pbBobRestart, SIGNAL(clicked()), this, SLOT(restartSam())); + QObject::connect(ui.pbBobStop, SIGNAL(clicked()), this, SLOT(stopSam())); QObject::connect(ui.pbBobGenAddr, SIGNAL(clicked()), this, SLOT(getNewKey())); QObject::connect(ui.pbBobLoadKey, SIGNAL(clicked()), this, SLOT(loadKey())); - QObject::connect(ui.cb_enableBob, SIGNAL(toggled(bool)), this, SLOT(enableBob(bool))); + QObject::connect(ui.cb_enableBob, SIGNAL(toggled(bool)), this, SLOT(enableSam(bool))); - QObject::connect(ui.cbBobAdvanced, SIGNAL(toggled(bool)), this, SLOT(toggleBobAdvancedSettings(bool))); + QObject::connect(ui.cbBobAdvanced, SIGNAL(toggled(bool)), this, SLOT(toggleSamAdvancedSettings(bool))); QObject::connect(ui.sbBobLengthIn, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); QObject::connect(ui.sbBobLengthOut, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); @@ -182,13 +184,9 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) QObject::connect(ui.sbBobVarianceIn, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); QObject::connect(ui.sbBobVarianceOut, SIGNAL(valueChanged(int)), this, SLOT(tunnelSettingsChanged(int))); - // These two spin boxes are used for the same thing - keep them in sync! - QObject::connect(ui.hiddenpage_proxyPort_i2p, SIGNAL(valueChanged(int)), this, SLOT(syncI2PProxyPortNormal(int))); - QObject::connect(ui.hiddenpage_proxyPort_i2p_2, SIGNAL(valueChanged(int)), this, SLOT(syncI2PProxyPortBob(int))); - // These two line edits are used for the same thing - keep them in sync! QObject::connect(ui.hiddenpage_proxyAddress_i2p, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrNormal(QString))); - QObject::connect(ui.hiddenpage_proxyAddress_i2p_2, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrBob(QString))); + QObject::connect(ui.hiddenpage_proxyAddress_i2p_2, SIGNAL(textChanged(QString)), this, SLOT(syncI2PProxyAddrSam(QString))); connect(NotifyQt::getInstance(), SIGNAL(connectionWithoutCert()), this, SLOT(connectionWithoutCert())); @@ -373,7 +371,7 @@ void ServerPage::load() } mIsHiddenNode = (detail.netMode == RS_NETMODE_HIDDEN); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::getSettings, &mBobSettings); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::getSettings, &mSamSettings); loadCommon(); updateStatus(); @@ -870,9 +868,9 @@ void ServerPage::updateStatus() loadFilteredIps() ; - updateStatusBob(); + updateStatusSam(); - // this is used by BOB + // this is used by SAM if (mOngoingConnectivityCheck > 0) { mOngoingConnectivityCheck--; @@ -1349,32 +1347,33 @@ void ServerPage::updateOutProxyIndicator() ui.iconlabel_tor_outgoing->setToolTip(tr("Tor proxy is not enabled")) ; } - // I2P - socket.connectToHost(ui.hiddenpage_proxyAddress_i2p->text(),ui.hiddenpage_proxyPort_i2p->text().toInt()); - if(socket.waitForConnected(500)) - { - socket.disconnectFromHost(); - ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; - ui.iconlabel_i2p_outgoing->setToolTip(tr("Proxy seems to work.")) ; - } - else - { - ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; - ui.iconlabel_i2p_outgoing->setToolTip(tr("I2P proxy is not enabled")) ; - } + // I2P - SAM + // Note: there is only "the SAM port", there is no additional proxy port! + samStatus ss; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::status, &ss); + if(ss.state == samStatus::samState::online) + { + socket.disconnectFromHost(); + ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; + ui.iconlabel_i2p_outgoing->setToolTip(tr("Proxy seems to work.")) ; + } + else + { + ui.iconlabel_i2p_outgoing->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; + ui.iconlabel_i2p_outgoing->setToolTip(tr("I2P proxy is not enabled")) ; + } - // I2P - BOB - socket.connectToHost(ui.hiddenpage_proxyAddress_i2p_2->text(), 2827); - if(true == (mBobAccessible = socket.waitForConnected(500))) + socket.connectToHost(ui.hiddenpage_proxyAddress_i2p_2->text(), 7656); + if(true == (mSamAccessible = socket.waitForConnected(1000))) { socket.disconnectFromHost(); ui.iconlabel_i2p_outgoing_2->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)) ; - ui.iconlabel_i2p_outgoing_2->setToolTip(tr("BOB is running and accessible")) ; + ui.iconlabel_i2p_outgoing_2->setToolTip(tr("SAMv3 is running and accessible")) ; } else { ui.iconlabel_i2p_outgoing_2->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; - ui.iconlabel_i2p_outgoing_2->setToolTip(tr("BOB is not accessible! Is it running?")) ; + ui.iconlabel_i2p_outgoing_2->setToolTip(tr("SAMv3 is not accessible! Is i2p running and SAM enabled?")) ; } } @@ -1385,28 +1384,18 @@ void ServerPage::updateInProxyIndicator() if(!mIsHiddenNode) return ; - //ui.iconlabel_tor_incoming->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; + //ui.iconlabel_tor_incoming->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)) ; //ui.testIncomingTor_PB->setIcon(FilesDefs::getIconFromQtResourcePath(":/loader/circleball-16.gif")) ; QMovie *movie = new QMovie(":/images/loader/circleball-16.gif"); ui.iconlabel_service_incoming->setMovie(movie); movie->start(); - if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enable) { - - QTcpSocket tcpSocket; - - const QString host = ui.hiddenpage_proxyAddress_i2p->text(); - qint16 port = ui.hiddenpage_proxyPort_i2p->text().toInt(); - QByteArray addr = ui.leBobB32Addr->text().toUtf8(); - addr.push_back('\n'); - - mOngoingConnectivityCheck = 5; // timeout in sec - - tcpSocket.connectToHost(host, port); - tcpSocket.write(addr); // write addr - tcpSocket.write(addr); // trigger connection error since RS expects a tls connection - tcpSocket.close(); - tcpSocket.waitForDisconnected(5 * 1000); + if (mHiddenType == RS_HIDDEN_TYPE_I2P && mSamSettings.enable) { + // there is no inproxy for SAMv3, since every connection goes through sam itself + auto secw = new samEstablishConnectionWrapper(); + secw->address = mSamSettings.address; + secw->connection = nullptr; + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::establishConnection, this, secw); return; } @@ -1419,18 +1408,10 @@ void ServerPage::updateInProxyIndicator() QNetworkProxy proxy ; proxy.setType(QNetworkProxy::Socks5Proxy); - switch (mHiddenType) { - case RS_HIDDEN_TYPE_I2P: - proxy.setHostName(ui.hiddenpage_proxyAddress_i2p->text()); - proxy.setPort(ui.hiddenpage_proxyPort_i2p->text().toInt()); - break; - case RS_HIDDEN_TYPE_TOR: - proxy.setHostName(ui.hiddenpage_proxyAddress_tor->text()); - proxy.setPort(ui.hiddenpage_proxyPort_tor->text().toInt()); - break; - default: - return; - } + + proxy.setHostName(ui.hiddenpage_proxyAddress_tor->text()); + proxy.setPort(ui.hiddenpage_proxyPort_tor->text().toInt()); + proxy.setCapabilities(QNetworkProxy::HostNameLookupCapability | proxy.capabilities()) ; QNetworkProxy::setApplicationProxy(proxy) ; @@ -1445,53 +1426,58 @@ void ServerPage::updateInProxyIndicator() QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy) ; } -void ServerPage::startBOB() +void ServerPage::startSam() { - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::start); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::start); updateStatus(); } -void ServerPage::restartBOB() +void ServerPage::restartSam() { - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::stop); - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::start); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::stop); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::start); updateStatus(); } -void ServerPage::stopBOB() +void ServerPage::stopSam() { - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::stop); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::stop); updateStatus(); } void ServerPage::getNewKey() { - bobSettings *bs = new bobSettings(); - - rsAutoProxyMonitor::taskAsync(autoProxyType::I2PBOB, autoProxyTask::receiveKey, this, bs); - - updateStatus(); + i2p::address *addr = new i2p::address(); + rsAutoProxyMonitor::taskAsync(autoProxyType::I2PSAM3, autoProxyTask::receiveKey, this, addr); } void ServerPage::loadKey() { - mBobSettings.address.privateKey = ui.pteBobServerKey->toPlainText().toStdString(); - mBobSettings.address.publicKey = i2p::publicKeyFromPrivate(mBobSettings.address.privateKey); - mBobSettings.address.base32 = i2p::keyToBase32Addr(mBobSettings.address.publicKey); + auto priv = ui.pteBobServerKey->toPlainText().toStdString(); + auto pub = i2p::publicKeyFromPrivate(priv); + if (pub.empty()) { + // something went wrong! + ui.pteBobServerKey->setPlainText("FAILED! Something went wrong while parsing the key!"); + return; + } - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); + mSamSettings.address.privateKey = priv; + mSamSettings.address.publicKey = pub; + mSamSettings.address.base32 = i2p::keyToBase32Addr(mSamSettings.address.publicKey); + + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); } -void ServerPage::enableBob(bool checked) +void ServerPage::enableSam(bool checked) { - mBobSettings.enable = checked; + mSamSettings.enable = checked; - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); - setUpBobElements(); + setUpSamElements(); } int8_t fitRange(int i, int min, int max) { @@ -1513,21 +1499,21 @@ void ServerPage::tunnelSettingsChanged(int) vi = ui.sbBobVarianceIn->value(); vo = ui.sbBobVarianceOut->value(); - mBobSettings.inLength = fitRange(li, 0, 7); - mBobSettings.outLength = fitRange(lo, 0, 7); - mBobSettings.inQuantity = fitRange(qi, 1, 16); - mBobSettings.outQuantity = fitRange(qo, 1, 16); - mBobSettings.inVariance = fitRange(vi, -1, 2); - mBobSettings.outVariance = fitRange(vo, -1, 2); + mSamSettings.inLength = fitRange(li, 0, 7); + mSamSettings.outLength = fitRange(lo, 0, 7); + mSamSettings.inQuantity = fitRange(qi, 1, 16); + mSamSettings.outQuantity = fitRange(qo, 1, 16); + mSamSettings.inVariance = fitRange(vi, -1, 2); + mSamSettings.outVariance = fitRange(vo, -1, 2); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); } -void ServerPage::toggleBobAdvancedSettings(bool checked) +void ServerPage::toggleSamAdvancedSettings(bool checked) { ui.swBobAdvanced->setCurrentIndex(checked ? 1 : 0); - if (!mBobSettings.address.privateKey.empty()) { + if (!mSamSettings.address.privateKey.empty()) { if (checked) { ui.pbBobGenAddr->show(); } else { @@ -1536,57 +1522,89 @@ void ServerPage::toggleBobAdvancedSettings(bool checked) } } -void ServerPage::syncI2PProxyPortNormal(int i) -{ - ui.hiddenpage_proxyPort_i2p_2->setValue(i); -} - -void ServerPage::syncI2PProxyPortBob(int i) -{ - ui.hiddenpage_proxyPort_i2p->setValue(i); - - // update port - saveBob(); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::reloadConfig); -} - void ServerPage::syncI2PProxyAddrNormal(QString t) { ui.hiddenpage_proxyAddress_i2p_2->setText(t); } -void ServerPage::syncI2PProxyAddrBob(QString t) +void ServerPage::syncI2PProxyAddrSam(QString t) { ui.hiddenpage_proxyAddress_i2p->setText(t); // update addr - saveBob(); - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::reloadConfig); + saveSam(); + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::reloadConfig); } void ServerPage::taskFinished(taskTicket *&ticket) { - if (ticket->task == autoProxyTask::receiveKey) { - bobSettings *s = NULL; - switch (ticket->types.front()) { - case autoProxyType::I2PBOB: - // update settings - s = (struct bobSettings *)ticket->data; - mBobSettings = *s; - delete s; - s = NULL; - ticket->data = NULL; - break; - default: - break; - } - } + switch (ticket->task) { + case autoProxyTask::receiveKey: + { + i2p::address *addr = nullptr; + addr = static_cast(ticket->data); + + if (ticket->types.front() != autoProxyType::I2PSAM3) + RS_WARN("auto proxy task finished but not for SMA, not exptected! Also not a serious problem."); + else { + // update settings + auto copy = *addr; + RsQThreadUtils::postToObject( + [this, copy]() + { + mSamSettings.address = copy; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::setSettings, &mSamSettings); + updateStatusSam(); + + }); + } + + delete addr; + addr = nullptr; + ticket->data = nullptr; + + + break; + } + case autoProxyTask::establishConnection: + { + samEstablishConnectionWrapper *secw = nullptr; + secw = static_cast(ticket->data); + + if (ticket->types.front() != autoProxyType::I2PSAM3) + RS_WARN("auto proxy task finished but not for SMA, not exptected! Also not a serious problem."); + else { + bool res = ticket->result == autoProxyStatus::ok && !!secw->connection->ses; + // update settings + if (res) { + sam3CloseConnection(secw->connection); + secw->connection = nullptr; // freed by above call + } + + RsQThreadUtils::postToObject( + [this, res]() + { + updateInProxyIndicatorResult(res); + + }); + } + + if (secw->connection) + delete secw->connection; + delete secw; + secw = nullptr; + ticket->data = nullptr; + break; + } + default: + RS_DBG("unsupported task!", ticket->task); + } if (ticket->data) std::cerr << "(WW) ServerPage::taskFinished data set. This should NOT happen - check the code!" << std::endl; delete ticket; - ticket = NULL; + ticket = nullptr; } void ServerPage::connectionWithoutCert() @@ -1613,14 +1631,13 @@ void ServerPage::loadCommon() // I2P rsPeers->getProxyServer(RS_HIDDEN_TYPE_I2P, proxyaddr, proxyport, status); whileBlocking(ui.hiddenpage_proxyAddress_i2p) -> setText(QString::fromStdString(proxyaddr)); - whileBlocking(ui.hiddenpage_proxyAddress_i2p_2)->setText(QString::fromStdString(proxyaddr)); // this one is for bob tab + whileBlocking(ui.hiddenpage_proxyAddress_i2p_2)->setText(QString::fromStdString(proxyaddr)); // this one is for sam tab whileBlocking(ui.hiddenpage_proxyPort_i2p) -> setValue(proxyport); - whileBlocking(ui.hiddenpage_proxyPort_i2p_2)->setValue(proxyport); // this one is for bob tab // don't use whileBlocking here - ui.cb_enableBob->setChecked(mBobSettings.enable); + ui.cb_enableBob->setChecked(mSamSettings.enable); - if (!mBobSettings.address.privateKey.empty()) { + if (!mSamSettings.address.privateKey.empty()) { ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); } @@ -1642,10 +1659,10 @@ void ServerPage::saveCommon() rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, new_proxyaddr, new_proxyport); } - saveBob(); + saveSam(); } -void ServerPage::saveBob() +void ServerPage::saveSam() { std::string orig_proxyaddr, new_proxyaddr; uint16_t orig_proxyport, new_proxyport; @@ -1656,20 +1673,29 @@ void ServerPage::saveBob() new_proxyaddr = ui.hiddenpage_proxyAddress_i2p -> text().toStdString(); new_proxyport = ui.hiddenpage_proxyPort_i2p -> value(); - if ((new_proxyaddr != orig_proxyaddr) || (new_proxyport != orig_proxyport)) { + // SAMv3 has no proxy port, everything goes through the SAM port. + if ((new_proxyaddr != orig_proxyaddr) /* || (new_proxyport != orig_proxyport) */) { rsPeers->setProxyServer(RS_HIDDEN_TYPE_I2P, new_proxyaddr, new_proxyport); } } -void ServerPage::updateStatusBob() +void ServerPage::updateStatusSam() { - QString addr = QString::fromStdString(mBobSettings.address.base32); + QString addr = QString::fromStdString(mSamSettings.address.base32); if (ui.leBobB32Addr->text() != addr) { ui.leBobB32Addr->setText(addr); ui.hiddenpage_serviceAddress->setText(addr); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mSamSettings.address.privateKey)); - if (!mBobSettings.address.privateKey.empty()) { + std::string signingKeyType, cryptoKeyType; + if (i2p::getKeyTypes(mSamSettings.address.publicKey, signingKeyType, cryptoKeyType)) + ui.samKeyInfo->setText(tr("Your key uses the following algorithms: %1 and %2"). + arg(QString::fromStdString(signingKeyType)). + arg(QString::fromStdString(cryptoKeyType))); + else + ui.samKeyInfo->setText(tr("unkown key type")); + + if (!mSamSettings.address.privateKey.empty()) { // we have an addr -> show fields ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); @@ -1686,128 +1712,82 @@ void ServerPage::updateStatusBob() ui.pbBobGenAddr->hide(); } - saveAddresses(); + saveAddresses(); } - bobStates bs; - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::status, &bs); + samStatus ss; + rsAutoProxyMonitor::taskSync(autoProxyType::I2PSAM3, autoProxyTask::status, &ss); QString bobSimpleText = QString(); - bobSimpleText.append(tr("RetroShare uses BOB to set up a %1 tunnel at %2:%3 (named %4)\n\n" - "When changing options (e.g. port) use the buttons at the bottom to restart BOB.\n\n"). - arg(mBobSettings.address.privateKey.empty() ? tr("client") : tr("server"), + bobSimpleText.append(tr("RetroShare uses SAMv3 to set up a %1 tunnel at %2:%3\n(id: %4)\n\n" + "When changing options use the buttons at the bottom to restart SAMv3.\n\n"). + arg(mSamSettings.address.privateKey.empty() ? tr("client") : tr("server"), ui.hiddenpage_proxyAddress_i2p_2->text(), - ui.hiddenpage_proxyPort_i2p_2->text(), - bs.tunnelName.empty() ? tr("unknown") : - QString::fromStdString(bs.tunnelName))); + "7656", + ss.sessionName.empty() ? tr("unknown") : + QString::fromStdString(ss.sessionName))); - // update BOB UI based on state - std::string errorString; - switch (bs.cs) { - case csDoConnect: - case csConnected: - case csDoDisconnect: - case csWaitForBob: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); + // update SAM UI based on state + QString s; + QString icon; + switch (ss.state) { + case samStatus::samState::offline: + enableSamElements(false); - enableBobElements(false); + ui.pbBobStart->setEnabled(true); + ui.pbBobRestart->setEnabled(false); + ui.pbBobStop->setEnabled(false); - { - QString s; - switch (bs.ct) { - case ctRunCheck: - s = tr("connectivity check"); - break; - case ctRunGetKeys: - s = tr("generating key"); - break; - case ctRunSetUp: - s = tr("starting up"); - break; - case ctRunShutDown: - s = tr("shuting down"); - default: - break; - } - bobSimpleText.append(tr("BOB is processing a request: %1").arg(s)); - } + s = tr("Offline, no SAM session is established yet.\n"); + icon = ICON_STATUS_ERROR; + break; + case samStatus::samState::connectSession: + enableSamElements(false); - ui.pbBobStart->setEnabled(false); - ui.pbBobRestart->setEnabled(false); - ui.pbBobStop->setEnabled(false); - break; - case csError: - // get error msg from bob - rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::getErrorInfo, &errorString); - - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_ERROR)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB is broken\n") + QString::fromStdString(errorString)); - - enableBobElements(false); - - bobSimpleText.append(tr("BOB encountered an error:\n")); - bobSimpleText.append(QString::fromStdString(errorString)); - - ui.pbBobStart->setEnabled(true); - ui.pbBobRestart->setEnabled(false); + ui.pbBobStart->setEnabled(false); + ui.pbBobRestart->setEnabled(false); ui.pbBobStop->setEnabled(true); - break; - case csDisconnected: - case csIdel: - switch (bs.ct) { - case ctRunSetUp: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_OK)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is running")); - enableBobElements(false); + s = tr("SAM is trying to establish a session ... this can take some time.\n"); + icon = ICON_STATUS_WORKING; + break; + case samStatus::samState::connectForward: + enableSamElements(false); - bobSimpleText.append(tr("BOB is working fine: tunnel established")); + ui.pbBobStart->setEnabled(false); + ui.pbBobRestart->setEnabled(false); + ui.pbBobStop->setEnabled(true); - ui.pbBobStart->setEnabled(false); - ui.pbBobRestart->setEnabled(true); - ui.pbBobStop->setEnabled(true); - break; - case ctRunCheck: - case ctRunGetKeys: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_WORKING)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB is processing a request")); + s = tr("SAM session established! Now setting up a forward session ...\n"); + icon = ICON_STATUS_WORKING; + break; + case samStatus::samState::online: + enableSamElements(true); - enableBobElements(false); + ui.pbBobStart->setEnabled(false); + ui.pbBobRestart->setEnabled(true); + ui.pbBobStop->setEnabled(true); - bobSimpleText.append(tr("BOB is processing a request")); + s = tr("Online, SAM is working as exptected\n"); + icon = ICON_STATUS_OK; + break; + } + ui.iconlabel_i2p_bob->setPixmap(icon); + ui.iconlabel_i2p_bob->setToolTip(s); + bobSimpleText.append(s); - ui.pbBobStart->setEnabled(false); - ui.pbBobRestart->setEnabled(false); - ui.pbBobStop->setEnabled(false); - break; - case ctRunShutDown: - case ctIdle: - ui.iconlabel_i2p_bob->setPixmap(FilesDefs::getPixmapFromQtResourcePath(ICON_STATUS_UNKNOWN)); - ui.iconlabel_i2p_bob->setToolTip(tr("BOB tunnel is not running")); - enableBobElements(true); + ui.pteBobSimple->setPlainText(bobSimpleText); - bobSimpleText.append(tr("BOB is inactive: tunnel closed")); - - ui.pbBobStart->setEnabled(true); - ui.pbBobRestart->setEnabled(false); - ui.pbBobStop->setEnabled(false); - break; - } - break; - - } - ui.pteBobSimple->setPlainText(bobSimpleText); // disable elements when BOB is not accessible - if (!mBobAccessible) { + if (!mSamAccessible) { ui.pbBobStart->setEnabled(false); - ui.pbBobStart->setToolTip("BOB is not accessible"); + ui.pbBobStart->setToolTip("SAMv3 is not accessible"); ui.pbBobRestart->setEnabled(false); - ui.pbBobRestart->setToolTip("BOB is not accessible"); - // don't disable the stop button! (in case bob is running you are otherwise unable to stop and disable it) - ui.pbBobStop->setToolTip("BOB is not accessible"); + ui.pbBobRestart->setToolTip("SAMv3 is not accessible"); + // don't disable the stop button! (in case SAM is running you are otherwise unable to stop and disable it) + ui.pbBobStop->setToolTip("SAMv3 is not accessible"); } else { ui.pbBobStart->setToolTip(""); ui.pbBobRestart->setToolTip(""); @@ -1815,26 +1795,34 @@ void ServerPage::updateStatusBob() } } -void ServerPage::setUpBobElements() +void ServerPage::setUpSamElements() { - ui.gbBob->setEnabled(mBobSettings.enable); - if (mBobSettings.enable) { + ui.gbBob->setEnabled(mSamSettings.enable); + if (mSamSettings.enable) { ui.hiddenpage_proxyAddress_i2p->setEnabled(false); - ui.hiddenpage_proxyAddress_i2p->setToolTip("Use I2P/BOB settings to change this value"); + ui.hiddenpage_proxyAddress_i2p->setToolTip("Use I2P settings to change this value"); ui.hiddenpage_proxyPort_i2p->setEnabled(false); - ui.hiddenpage_proxyPort_i2p->setToolTip("Use I2P/BOB settings to change this value"); + ui.hiddenpage_proxyPort_i2p->setToolTip("Use I2P settings to change this value"); - ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.address.base32)); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); + ui.leBobB32Addr->setText(QString::fromStdString(mSamSettings.address.base32)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mSamSettings.address.privateKey)); + + std::string signingKeyType, cryptoKeyType; + if (i2p::getKeyTypes(mSamSettings.address.publicKey, signingKeyType, cryptoKeyType)) + ui.samKeyInfo->setText(tr("You key uses %1 for signing and %2 for crypto"). + arg(QString::fromStdString(signingKeyType)). + arg(QString::fromStdString(cryptoKeyType))); + else + ui.samKeyInfo->setText(tr("unkown key type")); // cast to int to avoid problems int li, lo, qi, qo, vi, vo; - li = mBobSettings.inLength; - lo = mBobSettings.outLength; - qi = mBobSettings.inQuantity; - qo = mBobSettings.outQuantity; - vi = mBobSettings.inVariance; - vo = mBobSettings.outVariance; + li = mSamSettings.inLength; + lo = mSamSettings.outLength; + qi = mSamSettings.inQuantity; + qo = mSamSettings.outQuantity; + vi = mSamSettings.inVariance; + vo = mSamSettings.outVariance; ui.sbBobLengthIn ->setValue(li); ui.sbBobLengthOut ->setValue(lo); @@ -1850,7 +1838,7 @@ void ServerPage::setUpBobElements() } } -void ServerPage::enableBobElements(bool enable) +void ServerPage::enableSamElements(bool enable) { if (enable) { ui.pbBobGenAddr->setEnabled(true); @@ -1863,13 +1851,13 @@ void ServerPage::enableBobElements(bool enable) ui.cb_enableBob->setToolTip(tr("")); } else { ui.pbBobGenAddr->setEnabled(false); - ui.pbBobGenAddr->setToolTip(tr("stop BOB tunnel first to generate a new key")); + ui.pbBobGenAddr->setToolTip(tr("stop SAM tunnel first to generate a new key")); ui.pbBobLoadKey->setEnabled(false); - ui.pbBobLoadKey->setToolTip(tr("stop BOB tunnel first to load a key")); + ui.pbBobLoadKey->setToolTip(tr("stop SAM tunnel first to load a key")); ui.cb_enableBob->setEnabled(false); - ui.cb_enableBob->setToolTip(tr("stop BOB tunnel first to disable BOB")); + ui.cb_enableBob->setToolTip(tr("stop SAM tunnel first to disable SAM")); } } @@ -1896,6 +1884,7 @@ void ServerPage::handleNetworkReply(QNetworkReply *reply) { int error = reply->error() ; + RS_INFO("error:", error); if(reply->isOpen() && error == QNetworkReply::SslHandshakeFailedError) updateInProxyIndicatorResult(true); else diff --git a/retroshare-gui/src/gui/settings/ServerPage.h b/retroshare-gui/src/gui/settings/ServerPage.h index de971881b..7d82f2190 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.h +++ b/retroshare-gui/src/gui/settings/ServerPage.h @@ -34,7 +34,7 @@ #endif #include -#include +#include #include "retroshare-gui/configpage.h" #include "retroshare-gui/RsAutoUpdatePage.h" @@ -97,22 +97,19 @@ private slots: void handleNetworkReply(QNetworkReply *reply); void updateInProxyIndicator(); - // i2p bob - void startBOB(); - void restartBOB(); - void stopBOB(); + // i2p SAMv3 + void startSam(); + void restartSam(); + void stopSam(); void getNewKey(); void loadKey(); - void enableBob(bool checked); + void enableSam(bool checked); void tunnelSettingsChanged(int); - void toggleBobAdvancedSettings(bool checked); - - void syncI2PProxyPortNormal(int i); - void syncI2PProxyPortBob(int i); + void toggleSamAdvancedSettings(bool checked); void syncI2PProxyAddrNormal(QString); - void syncI2PProxyAddrBob(QString); + void syncI2PProxyAddrSam(QString); void connectionWithoutCert(); @@ -133,11 +130,11 @@ public: private: void loadCommon(); void saveCommon(); - void saveBob(); - void updateStatusBob(); + void saveSam(); + void updateStatusSam(); - void setUpBobElements(); - void enableBobElements(bool enable); + void setUpSamElements(); + void enableSamElements(bool enable); void updateInProxyIndicatorResult(bool success); @@ -160,8 +157,8 @@ private: bool mIsHiddenNode; uint32_t mHiddenType; - bobSettings mBobSettings; - bool mBobAccessible; // keeps track wether bob is accessable or not to en/disable the corresponding buttons + samSettings mSamSettings; + bool mSamAccessible; // keeps track wether SAM is accessable or not to en/disable the corresponding buttons RsEventsHandlerId_t mEventHandlerId; void handleEvent(std::shared_ptr event); diff --git a/retroshare-gui/src/gui/settings/ServerPage.ui b/retroshare-gui/src/gui/settings/ServerPage.ui index 5ef7308b1..6fad339c1 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.ui +++ b/retroshare-gui/src/gui/settings/ServerPage.ui @@ -6,8 +6,8 @@ 0 0 - 712 - 502 + 726 + 579 @@ -908,9 +908,9 @@ If you have issues connecting over Tor check the Tor logs too. - + - Automatic I2P/BOB + Automatic I2P @@ -918,7 +918,7 @@ If you have issues connecting over Tor check the Tor logs too. - Enable I2P BOB - changing this requires a restart to fully take effect + Enable I2P SAMv3 - changing this requires a restart to fully take effect @@ -950,7 +950,7 @@ If you have issues connecting over Tor check the Tor logs too. - I2P Basic Open Bridge + I2P Simple Anonymous Messaging @@ -982,21 +982,17 @@ If you have issues connecting over Tor check the Tor logs too. - - - I2P proxy port + + + Qt::Horizontal - - - - - - 10 + + + 40 + 20 + - - 65535 - - + @@ -1016,7 +1012,7 @@ If you have issues connecting over Tor check the Tor logs too. <html><head/><body><p>This led is green when the port listen on the left is active on your computer. It does not</p><p>mean that your Retroshare traffic transits though I2P. It will do so only if </p><p>you connect to Hidden nodes, or if you are running a Hidden node yourself.</p></body></html> - BOB accessible + SAM accessible @@ -1304,6 +1300,13 @@ If you have issues connecting over Tor check the Tor logs too. + + + + <key info> + + + @@ -1359,7 +1362,7 @@ If you have issues connecting over Tor check the Tor logs too. - BOB status + SAM status diff --git a/retroshare.pri b/retroshare.pri index 8e59df32d..7661f2bf8 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -140,11 +140,6 @@ rs_macos10.15:CONFIG -= rs_macos10.11 CONFIG *= no_rs_jsonapi rs_jsonapi:CONFIG -= no_rs_jsonapi -# Disable i2p BOB support for automatically setting up an i2p tunnel for RS -# "CONFIG+=no_rs_bob" -CONFIG *= rs_bob -no_rs_bob:CONFIG -= rs_bob - # To enable channel indexing append the following assignation to qmake command # line "CONFIG+=rs_deep_channels_index" CONFIG *= no_rs_deep_channels_index @@ -209,6 +204,9 @@ no_rs_dh_init_check:CONFIG -= rs_dh_init_check # many exported symbols. retroshare_plugins:win32:CONFIG *= libretroshare_shared +CONFIG+=rs_sam3 +CONFIG+=rs_sam3_libsam3 + # Specify host precompiled jsonapi-generator path, appending the following # assignation to qmake command line # 'JSONAPI_GENERATOR_EXE=/myBuildDir/jsonapi-generator'. Required for JSON API @@ -559,10 +557,6 @@ rs_webui { DEFINES *= RS_WEBUI } -rs_bob { - DEFINES *= RS_USE_I2P_BOB -} - rs_deep_channels_index:DEFINES *= RS_DEEP_CHANNEL_INDEX rs_deep_files_index:DEFINES *= RS_DEEP_FILES_INDEX @@ -576,6 +570,13 @@ rs_broadcast_discovery:DEFINES *= RS_BROADCAST_DISCOVERY no_rs_dh_init_check:DEFINES *= RS_DISABLE_DIFFIE_HELLMAN_INIT_CHECK +rs_sam3: { + DEFINES *= RS_USE_I2P_SAM3 + # this allows a downgrade from a SAMv3 build to a BOB build, can be removed in the future + DEFINES *= RS_I2P_SAM3_BOB_COMPAT +} +rs_sam3_libsam3: DEFINES *= RS_USE_I2P_SAM3_LIBSAM3 + debug { rs_mutex_debug:DEFINES *= RS_MUTEX_DEBUG diff --git a/supportlibs/libsam3 b/supportlibs/libsam3 new file mode 160000 index 000000000..8623304b6 --- /dev/null +++ b/supportlibs/libsam3 @@ -0,0 +1 @@ +Subproject commit 8623304b62294dafbe477573f321a464fef721dd