From 7b38c1c75abf64e5744d97cb4453ddfb8d69be01 Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Fri, 12 Apr 2019 02:53:39 +0200 Subject: [PATCH] Initial work on Broadcast Domain Discovery Discover peers on the same broadcast domain/LAN Implements own node announcement Implements others announcement listening Implements API to get notifiend when other nodes get discovered Implements API to get discovered nodes Add discovered peer locator to rsPeers if it is already friend Yet a bunch of things to do to take full advantage of this new implementation --- .gitmodules | 4 + .../Android/prepare-toolchain-clang.sh | 23 +- jsonapi-generator/src/jsonapi-generator.cpp | 36 +-- libretroshare/src/jsonapi/jsonapi.cpp | 17 +- libretroshare/src/jsonapi/jsonapi.h | 28 ++- libretroshare/src/jsonapi/jsonapiitems.h | 15 +- libretroshare/src/libretroshare.pro | 53 +++- .../src/retroshare/rsbroadcastdiscovery.h | 87 +++++++ libretroshare/src/rsserver/rsinit.cc | 19 +- .../src/services/broadcastdiscoveryservice.cc | 237 ++++++++++++++++++ .../src/services/broadcastdiscoveryservice.h | 83 ++++++ libretroshare/src/use_libretroshare.pri | 28 ++- libretroshare/src/util/rstime.h | 13 +- retroshare.pri | 9 +- supportlibs/udp-discovery-cpp | 1 + 15 files changed, 583 insertions(+), 70 deletions(-) create mode 100644 libretroshare/src/retroshare/rsbroadcastdiscovery.h create mode 100644 libretroshare/src/services/broadcastdiscoveryservice.cc create mode 100644 libretroshare/src/services/broadcastdiscoveryservice.h create mode 160000 supportlibs/udp-discovery-cpp diff --git a/.gitmodules b/.gitmodules index 650d2867a..ca1dc1b81 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,7 @@ [submodule "build_scripts/OBS"] path = build_scripts/OBS url = https://github.com/RetroShare/OBS.git +[submodule "supportlibs/udp-discovery-cpp"] + path = supportlibs/udp-discovery-cpp + url = https://github.com/truvorskameikin/udp-discovery-cpp.git + branch = develop diff --git a/build_scripts/Android/prepare-toolchain-clang.sh b/build_scripts/Android/prepare-toolchain-clang.sh index 81f7a398d..175b3406c 100755 --- a/build_scripts/Android/prepare-toolchain-clang.sh +++ b/build_scripts/Android/prepare-toolchain-clang.sh @@ -40,6 +40,9 @@ define_default_value QT_ANDROID_INSTALLER_SHA256 a214084e2295c9a9f8727e8a0131c37 define_default_value RESTBED_SOURCE_VERSION "4.6" +define_default_value UDP_DISCOVERY_CPP_SOURCE "https://github.com/truvorskameikin/udp-discovery-cpp.git" +define_default_value UDP_DISCOVERY_CPP_VERSION "develop" + define_default_value XAPIAN_SOURCE_VERSION "1.4.7" define_default_value XAPIAN_SOURCE_SHA256 13f08a0b649c7afa804fa0e85678d693fd6069dd394c9b9e7d41973d74a3b5d3 @@ -203,7 +206,7 @@ build_bzlib() rm -rf $B_dir verified_download $B_dir.tar.gz $BZIP2_SOURCE_SHA256 \ - http://trumpetti.atm.tut.fi/gentoo/distfiles/bzip2-${BZIP2_SOURCE_VERSION}.tar.gz + http://distfiles.gentoo.org/distfiles/bzip2-${BZIP2_SOURCE_VERSION}.tar.gz tar -xf $B_dir.tar.gz cd $B_dir @@ -342,6 +345,23 @@ build_restbed() cd .. } +build_udp-discovery-cpp() +{ + S_dir="udp-discovery-cpp" + [ -d $S_dir ] || git clone $UDP_DISCOVERY_CPP_SOURCE $S_dir + cd $S_dir + git checkout $UDP_DISCOVERY_CPP_VERSION + cd .. + + B_dir="udp-discovery-cpp-build" + rm -rf ${B_dir}; mkdir ${B_dir}; cd ${B_dir} + cmake -DCMAKE_INSTALL_PREFIX="${PREFIX}" -B. -H../udp-discovery-cpp + make -j${HOST_NUM_CPU} + cp libudp-discovery.a "${PREFIX}/lib/" + cp ../$S_dir/*.hpp "${PREFIX}/include/" + cd .. +} + build_xapian() { B_dir="xapian-core-${XAPIAN_SOURCE_VERSION}" @@ -372,6 +392,7 @@ build_sqlcipher build_libupnp build_rapidjson build_restbed +build_udp-discovery-cpp build_xapian delete_copied_includes diff --git a/jsonapi-generator/src/jsonapi-generator.cpp b/jsonapi-generator/src/jsonapi-generator.cpp index 90d44cbd9..310a5fd08 100644 --- a/jsonapi-generator/src/jsonapi-generator.cpp +++ b/jsonapi-generator/src/jsonapi-generator.cpp @@ -1,20 +1,22 @@ -/* - * RetroShare JSON API - * Copyright (C) 2018 Gioacchino Mazzurco - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ +/******************************************************************************* + * RetroShare JSON API * + * * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero 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 Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ #include #include diff --git a/libretroshare/src/jsonapi/jsonapi.cpp b/libretroshare/src/jsonapi/jsonapi.cpp index a6293d00f..6fe89508a 100644 --- a/libretroshare/src/jsonapi/jsonapi.cpp +++ b/libretroshare/src/jsonapi/jsonapi.cpp @@ -1,21 +1,20 @@ /******************************************************************************* - * libretroshare/src/gxs: jsonapi.cpp * - * * * RetroShare JSON API * - * Copyright (C) 2018 Gioacchino Mazzurco * + * * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * + * it under the terms of the GNU Affero 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. * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero 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 . * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * * * *******************************************************************************/ @@ -96,7 +95,7 @@ JsonApiServer::corsOptionsHeaders = /*static*/ bool JsonApiServer::checkRsServicePtrReady( - void* serviceInstance, const std::string& serviceName, + const void* serviceInstance, const std::string& serviceName, RsGenericSerializer::SerializeContext& ctx, const std::shared_ptr session) { diff --git a/libretroshare/src/jsonapi/jsonapi.h b/libretroshare/src/jsonapi/jsonapi.h index 1622bf49b..2ae8037f8 100644 --- a/libretroshare/src/jsonapi/jsonapi.h +++ b/libretroshare/src/jsonapi/jsonapi.h @@ -1,24 +1,22 @@ /******************************************************************************* - * libretroshare/src/gxs: jsonapi.h * - * * * RetroShare JSON API * - * Copyright (C) 2018 Gioacchino Mazzurco * + * * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * + * it under the terms of the GNU Affero 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. * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero 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 . * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * * * *******************************************************************************/ - #pragma once #include @@ -199,8 +197,18 @@ private: static void handleCorsOptions(const std::shared_ptr session); static bool checkRsServicePtrReady( - void* serviceInstance, const std::string& serviceName, + const void* serviceInstance, const std::string& serviceName, RsGenericSerializer::SerializeContext& ctx, const std::shared_ptr session ); + + static inline bool checkRsServicePtrReady( + const std::shared_ptr serviceInstance, + const std::string& serviceName, + RsGenericSerializer::SerializeContext& ctx, + const std::shared_ptr session ) + { + return checkRsServicePtrReady( + serviceInstance.get(), serviceName, ctx, session ); + } }; diff --git a/libretroshare/src/jsonapi/jsonapiitems.h b/libretroshare/src/jsonapi/jsonapiitems.h index 14173675d..19338d087 100644 --- a/libretroshare/src/jsonapi/jsonapiitems.h +++ b/libretroshare/src/jsonapi/jsonapiitems.h @@ -1,21 +1,20 @@ /******************************************************************************* - * libretroshare/src/gxs: jsonapiitems.h * - * * * RetroShare JSON API * - * Copyright (C) 2018 Gioacchino Mazzurco * + * * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * + * it under the terms of the GNU Affero 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. * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero 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 . * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * * * *******************************************************************************/ diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index 0d9d6fd76..b92e92c8d 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -865,21 +865,24 @@ rs_jsonapi { DUMMYRESTBEDINPUT = FORCE CMAKE_GENERATOR_OVERRIDE="" win32-g++:CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" - genrestbedlib.name = Generating libresbed. + genrestbedlib.name = Generating librestbed. genrestbedlib.input = DUMMYRESTBEDINPUT genrestbedlib.output = $$clean_path($${RESTBED_BUILD_PATH}/librestbed.a) genrestbedlib.CONFIG += target_predeps combine genrestbedlib.variable_out = PRE_TARGETDEPS genrestbedlib.commands = \ - cd $${RS_SRC_PATH} && \ - git submodule update --init --recommend-shallow supportlibs/restbed && \ - cd $${RESTBED_SRC_PATH} && \ - git submodule update --init --recommend-shallow dependency/asio && \ - git submodule update --init --recommend-shallow dependency/catch && \ - git submodule update --init --recommend-shallow dependency/kashmir && \ - mkdir -p $${RESTBED_BUILD_PATH}; cd $${RESTBED_BUILD_PATH} && \ - cmake -DCMAKE_CXX_COMPILER=$$QMAKE_CXX $${CMAKE_GENERATOR_OVERRIDE} -DBUILD_SSL=OFF \ - -DCMAKE_INSTALL_PREFIX=. -B. -H$$shell_path($${RESTBED_SRC_PATH}) && \ + cd $${RS_SRC_PATH} && ( \ + git submodule update --init --recommend-shallow supportlibs/restbed ; \ + cd $${RESTBED_SRC_PATH} ; \ + git submodule update --init --recommend-shallow dependency/asio ; \ + git submodule update --init --recommend-shallow dependency/catch ; \ + git submodule update --init --recommend-shallow dependency/kashmir ; \ + true ) && \ + mkdir -p $${RESTBED_BUILD_PATH} && cd $${RESTBED_BUILD_PATH} && \ + cmake -DCMAKE_CXX_COMPILER=$$QMAKE_CXX \ + $${CMAKE_GENERATOR_OVERRIDE} -DBUILD_SSL=OFF \ + -DCMAKE_INSTALL_PREFIX=. -B. \ + -H$$shell_path($${RESTBED_SRC_PATH}) && \ make QMAKE_EXTRA_COMPILERS += genrestbedlib @@ -925,6 +928,36 @@ rs_deep_search { HEADERS += deep_search/deep_search.h } +rs_broadcast_discovery { + HEADERS += retroshare/rsbroadcastdiscovery.h \ + services/broadcastdiscoveryservice.h + SOURCES += services/broadcastdiscoveryservice.cc + + no_rs_cross_compiling { + DUMMYQMAKECOMPILERINPUT = FORCE + CMAKE_GENERATOR_OVERRIDE="" + win32-g++:CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\"" + udpdiscoverycpplib.name = Generating libudp-discovery.a. + udpdiscoverycpplib.input = DUMMYQMAKECOMPILERINPUT + udpdiscoverycpplib.output = $$clean_path($${UDP_DISCOVERY_BUILD_PATH}/libudp-discovery.a) + udpdiscoverycpplib.CONFIG += target_predeps combine + udpdiscoverycpplib.variable_out = PRE_TARGETDEPS + udpdiscoverycpplib.commands = \ + cd $${RS_SRC_PATH} && ( \ + git submodule update --init --recommend-shallow supportlibs/udp-discovery-cpp || \ + true ) && \ + mkdir -p $${UDP_DISCOVERY_BUILD_PATH} && \ + cd $${UDP_DISCOVERY_BUILD_PATH} && \ + cmake -DCMAKE_CXX_COMPILER=$$QMAKE_CXX \ + $${CMAKE_GENERATOR_OVERRIDE} \ + -DBUILD_EXAMPLE=OFF -DBUILD_TOOL=OFF \ + -DCMAKE_INSTALL_PREFIX=. -B. \ + -H$$shell_path($${UDP_DISCOVERY_SRC_PATH}) && \ + make + QMAKE_EXTRA_COMPILERS += udpdiscoverycpplib + } +} + ########################################################################################################### # OLD CONFIG OPTIONS. # Not used much - but might be useful one day. diff --git a/libretroshare/src/retroshare/rsbroadcastdiscovery.h b/libretroshare/src/retroshare/rsbroadcastdiscovery.h new file mode 100644 index 000000000..ec8c327b7 --- /dev/null +++ b/libretroshare/src/retroshare/rsbroadcastdiscovery.h @@ -0,0 +1,87 @@ +/******************************************************************************* + * RetroShare Broadcast Domain Discovery * + * * + * Copyright (C) 2019 Gioacchino Mazzurco * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero 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 Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#include +#include +#include +#include + +#include "retroshare/rsids.h" +#include "retroshare/rstypes.h" +#include "serialiser/rsserializable.h" +#include "util/rstime.h" +#include "util/rsurl.h" +#include "util/rsmemory.h" + +class RsBroadcastDiscovery; + +/** + * Pointer to global instance of RsBroadcastDiscovery service implementation + * @jsonapi{development} + */ +extern std::shared_ptr rsBroadcastDiscovery; + +struct RsBroadcastDiscoveryResult : RsSerializable +{ + PGPFingerprintType mPgpFingerprint; + RsPeerId mSslId; + std::string mProfileName; + RsUrl locator; + + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx) override + { + RS_SERIAL_PROCESS(mPgpFingerprint); + RS_SERIAL_PROCESS(mSslId); + RS_SERIAL_PROCESS(mProfileName); + RS_SERIAL_PROCESS(locator); + } +}; + +class RsBroadcastDiscovery +{ +public: + virtual ~RsBroadcastDiscovery(); + + /** + * @brief Get potential peers that have been discovered up until now + * @jsonapi{development} + * @return vector containing discovered peers, may be empty. + */ + virtual std::vector getDiscoveredPeers() = 0; + + /** + * @brief registerPeersDiscoveredEventHandler + * @jsonapi{development} + * @param multiCallback function that will be called each time a potential + * peer is discovered + * @param[in] maxWait maximum wait time in seconds for discovery results, + * passing std::numeric_limits::max() means wait forever. + * @param[out] errorMessage Optional storage for error message, meaningful + * only on failure. + * @return false on error, true otherwise + */ + virtual bool registerPeersDiscoveredEventHandler( + const std::function& + multiCallback, + rstime_t maxWait = 300, + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0; +}; diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 86e26c55b..20520cd9a 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -34,7 +34,7 @@ #ifdef __ANDROID__ # include // To install bdboot.txt -# include // for String::fromStdString(...) +# include // for QString::fromStdString(...) #endif #include "util/argstream.h" @@ -98,6 +98,11 @@ RsDht *rsDht = NULL ; # include "jsonapi/jsonapi.h" #endif +#ifdef RS_BROADCAST_DISCOVERY +# include "retroshare/rsbroadcastdiscovery.h" +# include "services/broadcastdiscoveryservice.h" +#endif // def RS_BROADCAST_DISCOVERY + // #define GPG_DEBUG // #define AUTHSSL_DEBUG // #define FIM_DEBUG @@ -1462,6 +1467,12 @@ int RsServer::StartupRetroShare() mHistoryMgr, *mGxsTrans ); mStatusSrv = new p3StatusService(serviceCtrl); +#ifdef RS_BROADCAST_DISCOVERY + rsBroadcastDiscovery.reset(new BroadcastDiscoveryService(*rsPeers)); + BroadcastDiscoveryService& tBroadcastDiscoveryService = + static_cast(*rsBroadcastDiscovery); +#endif // def RS_BROADCAST_DISCOVERY + #ifdef ENABLE_GROUTER p3GRouter *gr = new p3GRouter(serviceCtrl,mGxsIdService) ; rsGRouter = gr ; @@ -1863,10 +1874,14 @@ int RsServer::StartupRetroShare() # ifdef RS_GXS_TRANS startServiceThread(mGxsTrans, "gxs trans"); startServiceThread(gxstrans_ns, "gxs trans ns"); -# endif // RS_GXS_TRANS +# endif // def RS_GXS_TRANS #endif // RS_ENABLE_GXS +#ifdef RS_BROADCAST_DISCOVERY + startServiceThread(&tBroadcastDiscoveryService, "Broadcast Discovery"); +#endif // def RS_BROADCAST_DISCOVERY + ftserver->StartupThreads(); ftserver->ResumeTransfers(); diff --git a/libretroshare/src/services/broadcastdiscoveryservice.cc b/libretroshare/src/services/broadcastdiscoveryservice.cc new file mode 100644 index 000000000..02218ceab --- /dev/null +++ b/libretroshare/src/services/broadcastdiscoveryservice.cc @@ -0,0 +1,237 @@ +/******************************************************************************* + * RetroShare Broadcast Domain Discovery * + * * + * Copyright (C) 2019 Gioacchino Mazzurco * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero 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 Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "services/broadcastdiscoveryservice.h" +#include "retroshare/rspeers.h" +#include "serialiser/rsserializable.h" +#include "serialiser/rsserializer.h" + +/*extern*/ std::shared_ptr rsBroadcastDiscovery(nullptr); +RsBroadcastDiscovery::~RsBroadcastDiscovery() { /* Beware of Rs prefix! */ } + +struct BroadcastDiscoveryPack : RsSerializable +{ + BroadcastDiscoveryPack() : mLocalPort(0) {} + + PGPFingerprintType mPgpFingerprint; + RsPeerId mSslId; + uint16_t mLocalPort; + std::string mProfileName; + + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) override + { + RS_SERIAL_PROCESS(mPgpFingerprint); + RS_SERIAL_PROCESS(mSslId); + RS_SERIAL_PROCESS(mLocalPort); + RS_SERIAL_PROCESS(mProfileName); + } + + static BroadcastDiscoveryPack fromPeerDetails(const RsPeerDetails& pd) + { + BroadcastDiscoveryPack bdp; + bdp.mPgpFingerprint = pd.fpr; + bdp.mSslId = pd.id; + bdp.mLocalPort = pd.localPort; + bdp.mProfileName = pd.name; + return bdp; + } + + static BroadcastDiscoveryPack fromSerializedString(const std::string& st) + { + RsGenericSerializer::SerializeContext ctx( + reinterpret_cast(const_cast(st.data())), + static_cast(st.size()) ); + BroadcastDiscoveryPack bdp; + bdp.serial_process(RsGenericSerializer::DESERIALIZE, ctx); + return bdp; + } + + std::string serializeToString() + { + /* After some experiments it seems very unlikely that UDP broadcast + * packets bigger then this could get trought a network */ + std::vector buffer(512, 0); + RsGenericSerializer::SerializeContext ctx( + buffer.data(), static_cast(buffer.size()) ); + serial_process(RsGenericSerializer::SERIALIZE, ctx); + return std::string(reinterpret_cast(buffer.data()), ctx.mOffset); + } +}; + + +BroadcastDiscoveryService::BroadcastDiscoveryService( + RsPeers& pRsPeers ) : + mDiscoveredDataMutex("BroadcastDiscoveryService discovered data mutex"), + mRsPeers(pRsPeers), + mPeersDiscoveredEventHandlersListMutex( + "BroadcastDiscoveryService event handlers mutex" ) +{ + if(mRsPeers.isHiddenNode(mRsPeers.getOwnId())) return; + + mUdcParameters.set_can_discover(true); + mUdcParameters.set_can_be_discovered(true); + mUdcParameters.set_port(port); + mUdcParameters.set_application_id(appId); + + mUdcEndpoint.Start(mUdcParameters, ""); + updatePublishedData(); +} + +BroadcastDiscoveryService::~BroadcastDiscoveryService() +{ mUdcEndpoint.Stop(true); } + +std::vector +BroadcastDiscoveryService::getDiscoveredPeers() +{ + std::vector ret; + + RS_STACK_MUTEX(mDiscoveredDataMutex); + for(auto&& pp: mDiscoveredData) + ret.push_back(createResult(pp.first, pp.second)); + + return ret; +} + +void BroadcastDiscoveryService::updatePublishedData() +{ + RsPeerDetails od; + mRsPeers.getPeerDetails(mRsPeers.getOwnId(), od); + mUdcEndpoint.SetUserData( + BroadcastDiscoveryPack::fromPeerDetails(od).serializeToString()); +} + +void BroadcastDiscoveryService::data_tick() +{ + auto nextRunAt = std::chrono::system_clock::now() + std::chrono::seconds(5); + + if( mUdcParameters.can_discover() && + !mRsPeers.isHiddenNode(mRsPeers.getOwnId()) ) + { + auto newEndpoints = mUdcEndpoint.ListDiscovered(); + std::set< std::pair > mChangedData; + + mDiscoveredDataMutex.lock(); + for(auto&& dEndpoint: newEndpoints) + { + auto findIt = mDiscoveredData.find(dEndpoint.ip_port()); + if( !dEndpoint.user_data().empty() && ( + findIt == mDiscoveredData.end() || + (*findIt).second != dEndpoint.user_data() ) ) + { + mDiscoveredData[dEndpoint.ip_port()] = dEndpoint.user_data(); + mChangedData.insert(std::make_pair( + dEndpoint.ip_port(), + dEndpoint.user_data() )); + } + } + mDiscoveredDataMutex.unlock(); + + cleanTimedOutEventHandlers(); + + mPeersDiscoveredEventHandlersListMutex.lock(); + if(!mChangedData.empty() && !mPeersDiscoveredEventHandlersList.empty()) + { + for (auto&& pp : mChangedData) + { + std::cout << __PRETTY_FUNCTION__ << " Got: >>>" << pp.second + << "<<< from: " << UDC::IpToString(pp.first.ip()) + << std::endl; + + RsBroadcastDiscoveryResult rbdr = + createResult(pp.first, pp.second); + + mRsPeers.addPeerLocator(rbdr.mSslId, rbdr.locator); + + for( const timedDiscHandlers_t& evtHandler : + mPeersDiscoveredEventHandlersList ) evtHandler.first(rbdr); + } + } + mPeersDiscoveredEventHandlersListMutex.unlock(); + } + + /* Probably this would be better if done only on actual change */ + if( mUdcParameters.can_be_discovered() && + !mRsPeers.isHiddenNode(mRsPeers.getOwnId()) ) updatePublishedData(); + + std::this_thread::sleep_until(nextRunAt); +} + +RsBroadcastDiscoveryResult BroadcastDiscoveryService::createResult( + const udpdiscovery::IpPort& ipp, const std::string& uData ) +{ + BroadcastDiscoveryPack bdp = + BroadcastDiscoveryPack::fromSerializedString(uData); + + RsBroadcastDiscoveryResult rbdr; + rbdr.mPgpFingerprint = bdp.mPgpFingerprint; + rbdr.mSslId = bdp.mSslId; + rbdr.mProfileName = bdp.mProfileName; + rbdr.locator. + setScheme("ipv4"). + setHost(UDC::IpToString(ipp.ip())). + setPort(static_cast(ipp.port()) ); + + return rbdr; +} + +void BroadcastDiscoveryService::cleanTimedOutEventHandlers() +{ + auto now = std::chrono::system_clock::now(); + + RS_STACK_MUTEX(mPeersDiscoveredEventHandlersListMutex); + mPeersDiscoveredEventHandlersList.remove_if( + [&](timedDiscHandlers_t h) { return h.second <= now; } ); +} + +bool BroadcastDiscoveryService::registerPeersDiscoveredEventHandler( + const std::function& + multiCallback, rstime_t maxWait, std::string& errorMessage ) +{ + auto now = std::chrono::system_clock::now(); + auto timeout = std::chrono::system_clock::time_point::max(); + if(maxWait != std::numeric_limits::max()) + timeout = now + std::chrono::seconds(maxWait); + + if(timeout <= now) + { + errorMessage = " Invalid maxWait value: " + std::to_string(maxWait) + + " either too big or too little, use: " + + "std::numeric_limits::max() == " + + std::to_string(std::numeric_limits::max()) + + " if you meant \"wait forever\""; + + std::cerr << __PRETTY_FUNCTION__ << errorMessage << std::endl; + return false; + } + + RS_STACK_MUTEX(mPeersDiscoveredEventHandlersListMutex); + mPeersDiscoveredEventHandlersList.push_front( + std::make_pair(multiCallback,timeout) ); + + return true; +} diff --git a/libretroshare/src/services/broadcastdiscoveryservice.h b/libretroshare/src/services/broadcastdiscoveryservice.h new file mode 100644 index 000000000..7710034b9 --- /dev/null +++ b/libretroshare/src/services/broadcastdiscoveryservice.h @@ -0,0 +1,83 @@ +/******************************************************************************* + * RetroShare Broadcast Domain Discovery * + * * + * Copyright (C) 2019 Gioacchino Mazzurco * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero 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 Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "retroshare/rsbroadcastdiscovery.h" +#include "util/rsthreads.h" + +namespace UDC = udpdiscovery; +class RsPeers; + +class BroadcastDiscoveryService : + public RsBroadcastDiscovery, public RsTickingThread +{ +public: + // TODO: std::shared_ptr mRsPeers; + BroadcastDiscoveryService(RsPeers& pRsPeers); + virtual ~BroadcastDiscoveryService() override; + + /// @see RsBroadcastDiscovery + std::vector getDiscoveredPeers() override; + + /// @see RsBroadcastDiscovery + bool registerPeersDiscoveredEventHandler( + const std::function& + multiCallback, + rstime_t maxWait = 300, + std::string& errorMessage = RS_DEFAULT_STORAGE_PARAM(std::string) + ) override; + + /// @see RsTickingThread + void data_tick() override; + +protected: + constexpr static uint16_t port = 36405; + constexpr static uint32_t appId = 904571; + + void updatePublishedData(); + + UDC::EndpointParameters mUdcParameters; + UDC::Endpoint mUdcEndpoint; + + std::map mDiscoveredData; + RsMutex mDiscoveredDataMutex; + + RsPeers& mRsPeers; // TODO: std::shared_ptr mRsPeers; + + typedef std::pair< + std::function, + std::chrono::system_clock::time_point > timedDiscHandlers_t; + /** Store peer discovered event handlers with timeout */ + std::forward_list mPeersDiscoveredEventHandlersList; + RsMutex mPeersDiscoveredEventHandlersListMutex; + + /// Cleanup mPeersDiscoveredEventHandlersList + void cleanTimedOutEventHandlers(); + + RsBroadcastDiscoveryResult createResult( + const UDC::IpPort& ipp, const std::string& uData ); +}; diff --git a/libretroshare/src/use_libretroshare.pri b/libretroshare/src/use_libretroshare.pri index d3dbb0e45..9a74d209c 100644 --- a/libretroshare/src/use_libretroshare.pri +++ b/libretroshare/src/use_libretroshare.pri @@ -15,13 +15,16 @@ # You should have received a copy of the GNU Lesser General Public License # # along with this program. If not, see . # ################################################################################ -DEPENDPATH *= $$clean_path($${PWD}/../../libretroshare/src/) -INCLUDEPATH *= $$clean_path($${PWD}/../../libretroshare/src) -LIBS *= -L$$clean_path($${OUT_PWD}/../../libretroshare/src/lib/) -lretroshare +RS_SRC_PATH=$$clean_path($${PWD}/../../) +RS_BUILD_PATH=$$clean_path($${OUT_PWD}/../../) + +DEPENDPATH *= $$clean_path($${RS_SRC_PATH}/libretroshare/src/) +INCLUDEPATH *= $$clean_path($${RS_SRC_PATH}/libretroshare/src) +LIBS *= -L$$clean_path($${RS_BUILD_PATH}/libretroshare/src/lib/) -lretroshare equals(TARGET, retroshare):equals(TEMPLATE, lib){ } else { - PRE_TARGETDEPS *= $$clean_path($$OUT_PWD/../../libretroshare/src/lib/libretroshare.a) + PRE_TARGETDEPS *= $$clean_path($${RS_BUILD_PATH}/libretroshare/src/lib/libretroshare.a) } !include("../../openpgpsdk/src/use_openpgpsdk.pri"):error("Including") @@ -48,9 +51,6 @@ mLibs = $$RS_SQL_LIB ssl crypto $$RS_THREAD_LIB $$RS_UPNP_LIB dLibs = rs_jsonapi { - RS_SRC_PATH=$$clean_path($${PWD}/../../) - RS_BUILD_PATH=$$clean_path($${OUT_PWD}/../../) - no_rs_cross_compiling { RESTBED_SRC_PATH=$$clean_path($${RS_SRC_PATH}/supportlibs/restbed) RESTBED_BUILD_PATH=$$clean_path($${RS_BUILD_PATH}/supportlibs/restbed) @@ -73,6 +73,20 @@ rs_deep_search { win32-g++:mLibs += rpcrt4 } +rs_broadcast_discovery { + no_rs_cross_compiling { + UDP_DISCOVERY_SRC_PATH=$$clean_path($${RS_SRC_PATH}/supportlibs/udp-discovery-cpp/) + UDP_DISCOVERY_BUILD_PATH=$$clean_path($${RS_BUILD_PATH}/supportlibs/udp-discovery-cpp/) + INCLUDEPATH *= $$clean_path($${UDP_DISCOVERY_SRC_PATH}) + DEPENDPATH *= $$clean_path($${UDP_DISCOVERY_BUILD_PATH}) + QMAKE_LIBDIR *= $$clean_path($${UDP_DISCOVERY_BUILD_PATH}) + # Using sLibs would fail as libudp-discovery.a is generated at compile-time + LIBS *= -L$$clean_path($${UDP_DISCOVERY_BUILD_PATH}) -ludp-discovery + } else:sLibs *= udp-discovery + + win32-g++:dLibs *= wsock32 +} + static { sLibs *= $$mLibs } else { diff --git a/libretroshare/src/util/rstime.h b/libretroshare/src/util/rstime.h index 397edfee1..6cc01e486 100644 --- a/libretroshare/src/util/rstime.h +++ b/libretroshare/src/util/rstime.h @@ -45,11 +45,14 @@ typedef int64_t rstime_t; // Do we really need this? Our names have rs prefix to avoid pollution already! namespace rstime { - /*! - * \brief This is a cross-system definition of usleep, which accepts any 32 bits number of micro-seconds. - */ - RS_DEPRECATED_FOR("std::this_thread::sleep_for") - int rs_usleep(uint32_t micro_seconds); +/** + * @deprecated { std::this_thread::sleep_for or + * std::this_thread::sleep_until instead } + * @brief This is a cross-system definition of usleep, which accepts any + * 32 bits number of micro-seconds. + */ +RS_DEPRECATED_FOR("std::this_thread::sleep_for") +int rs_usleep(uint32_t micro_seconds); /* Use this class to measure and display time duration of a given environment: diff --git a/retroshare.pri b/retroshare.pri index 7f687d766..dd97fc08a 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -167,10 +167,15 @@ CONFIG *= no_rs_deep_search rs_deep_search:CONFIG -= no_rs_deep_search # To enable native dialogs append the following assignation to qmake command -#line "CONFIG+=rs_use_native_dialogs" +# line "CONFIG+=rs_use_native_dialogs" CONFIG *= no_rs_use_native_dialogs rs_use_native_dialogs:CONFIG -= no_rs_use_native_dialogs +# To disable broadcast discovery happend the following assignation to qmake +# command line "CONFIG+=no_rs_broadcast_discovery" +CONFIG *= rs_broadcast_discovery +no_rs_broadcast_discovery:CONFIG -= rs_broadcast_discovery + # Specify host precompiled jsonapi-generator path, appending the following # assignation to qmake command line # 'JSONAPI_GENERATOR_EXE=/myBuildDir/jsonapi-generator'. Required for JSON API @@ -488,6 +493,8 @@ rs_deep_search { rs_use_native_dialogs:DEFINES *= RS_NATIVEDIALOGS +rs_broadcast_discovery:DEFINES *= RS_BROADCAST_DISCOVERY + debug { QMAKE_CXXFLAGS -= -O2 -fomit-frame-pointer QMAKE_CFLAGS -= -O2 -fomit-frame-pointer diff --git a/supportlibs/udp-discovery-cpp b/supportlibs/udp-discovery-cpp new file mode 160000 index 000000000..ab3f9745b --- /dev/null +++ b/supportlibs/udp-discovery-cpp @@ -0,0 +1 @@ +Subproject commit ab3f9745b1f2c6a91de38d9942dfe4b47c5c358c