From 3e982c0a39d1d1833afaf558fc7b0f7aeffd2d64 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 16 Jul 2015 10:11:09 +0200 Subject: Fix S5B proxy connection management for multiple hosts per JID A recent commit introduced resolving of S5B proxy domain names to their IPv4 and IPv6 addresses. With that a proxy identified by a JID can have more than one host and we try them in parallel until the first succeeds. The old code just handled one host per proxy JID and a failed IPv6 attempt would override the succeeded connection. The code uses shared pointers and the succeeded connecting is deallocated and disconnected when it is replaced with the failing IPv6 connection. The result is the proxy server complaining that we are not connected as we try to activate the proxy stream. This commit changes the the proxy management to handle multiple connections per proxy JID. Failing connections are removed from the proxy sessions data structure. With the first succeeding connections, others are stopped and also removed. Test-Information: Tested on Linux (Elementary OS 0.2) with "Swiften/QA/FileTransferTest/FileTransferTest 4 4", which forces the use of SOCKS5 bytestream proxy. Change-Id: If3071c3d058e1040556bb72702bf83f4f5f25334 diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.cpp b/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.cpp index 2956ff7..1036e12 100644 --- a/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.cpp +++ b/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.cpp @@ -35,6 +35,13 @@ SOCKS5BytestreamProxiesManager::~SOCKS5BytestreamProxiesManager() { if (proxyFinder_) { proxyFinder_->stop(); } + + foreach (const ProxySessionsMap::value_type& sessionsForID, proxySessions_) { + foreach (const ProxyJIDClientSessionVector::value_type& session, sessionsForID.second) { + session.second->onSessionReady.disconnect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionReady, this,sessionsForID.first, session.first, session.second, _1)); + session.second->onFinished.disconnect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionFinished, this, sessionsForID.first, session.first, session.second, _1)); + } + } } void SOCKS5BytestreamProxiesManager::addS5BProxy(S5BProxyRequest::ref proxy) { @@ -56,7 +63,7 @@ const boost::optional >& SOCKS5BytestreamProxi void SOCKS5BytestreamProxiesManager::connectToProxies(const std::string& sessionID) { SWIFT_LOG(debug) << "session ID: " << sessionID << std::endl; - ProxyJIDClientSessionMap clientSessions; + ProxyJIDClientSessionVector clientSessions; if (localS5BProxies_) { foreach(S5BProxyRequest::ref proxy, localS5BProxies_.get()) { @@ -65,7 +72,10 @@ void SOCKS5BytestreamProxiesManager::connectToProxies(const std::string& session HostAddressPort addressPort = HostAddressPort(proxy->getStreamHost().get().host, proxy->getStreamHost().get().port); SWIFT_LOG_ASSERT(addressPort.isValid(), warning) << std::endl; boost::shared_ptr session = boost::make_shared(conn, addressPort, sessionID, timerFactory_); - clientSessions[proxy->getStreamHost().get().jid] = session; + JID proxyJid = proxy->getStreamHost().get().jid; + clientSessions.push_back(std::pair >(proxyJid, session)); + session->onSessionReady.connect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionReady, this,sessionID, proxyJid, session, _1)); + session->onFinished.connect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionFinished, this, sessionID, proxyJid, session, _1)); session->start(); } } @@ -78,17 +88,18 @@ boost::shared_ptr SOCKS5BytestreamProxiesManager: if (proxySessions_.find(sessionID) == proxySessions_.end()) { return boost::shared_ptr(); } - if (proxySessions_[sessionID].find(proxyJID) == proxySessions_[sessionID].end()) { - return boost::shared_ptr(); - } // get active session - boost::shared_ptr activeSession = proxySessions_[sessionID][proxyJID]; - proxySessions_[sessionID].erase(proxyJID); - - // close other sessions - foreach(const ProxyJIDClientSessionMap::value_type& myPair, proxySessions_[sessionID]) { - myPair.second->stop(); + boost::shared_ptr activeSession; + for (ProxyJIDClientSessionVector::iterator i = proxySessions_[sessionID].begin(); i != proxySessions_[sessionID].end(); i++) { + i->second->onSessionReady.disconnect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionReady, this,sessionID, proxyJID, i->second, _1)); + i->second->onFinished.disconnect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionFinished, this, sessionID, proxyJID, i->second, _1)); + if (i->first == proxyJID && !activeSession) { + activeSession = i->second; + } + else { + i->second->stop(); + } } proxySessions_.erase(sessionID); @@ -150,4 +161,41 @@ void SOCKS5BytestreamProxiesManager::queryForProxies() { proxyFinder_->start(); } +void SOCKS5BytestreamProxiesManager::handleProxySessionReady(const std::string& sessionID, const JID& jid, boost::shared_ptr session, bool error) { + session->onSessionReady.disconnect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionFinished, this, boost::cref(sessionID), boost::cref(jid), session, _1)); + if (!error) { + // The SOCKS5 bytestream session to the proxy succeeded; stop and remove other sessions. + if (proxySessions_.find(sessionID) != proxySessions_.end()) { + for (ProxyJIDClientSessionVector::iterator i = proxySessions_[sessionID].begin(); i != proxySessions_[sessionID].end();) { + if ((i->first == jid) && (i->second != session)) { + i->second->stop(); + i = proxySessions_[sessionID].erase(i); + } + else { + i++; + } + } + } + } +} + +void SOCKS5BytestreamProxiesManager::handleProxySessionFinished(const std::string& sessionID, const JID& jid, boost::shared_ptr session, boost::optional error) { + session->onFinished.disconnect(boost::bind(&SOCKS5BytestreamProxiesManager::handleProxySessionFinished, this, sessionID, jid, session, _1)); + if (error.is_initialized()) { + // The SOCKS5 bytestream session to the proxy failed; remove it. + if (proxySessions_.find(sessionID) != proxySessions_.end()) { + for (ProxyJIDClientSessionVector::iterator i = proxySessions_[sessionID].begin(); i != proxySessions_[sessionID].end();) { + if ((i->first == jid) && (i->second == session)) { + i->second->stop(); + i = proxySessions_[sessionID].erase(i); + break; + } + else { + i++; + } + } + } + } +} + } diff --git a/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h b/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h index 06db76c..e4bf1d9 100644 --- a/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h +++ b/Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h @@ -13,14 +13,16 @@ #pragma once +#include #include +#include #include #include #include #include -#include #include +#include namespace Swift { class TimerFactory; @@ -59,6 +61,9 @@ namespace Swift { void queryForProxies(); + void handleProxySessionReady(const std::string& sessionID, const JID& jid, boost::shared_ptr session, bool error); + void handleProxySessionFinished(const std::string& sessionID, const JID& jid, boost::shared_ptr session, boost::optional error); + private: ConnectionFactory* connectionFactory_; TimerFactory* timerFactory_; @@ -66,8 +71,9 @@ namespace Swift { IQRouter* iqRouter_; JID serviceRoot_; - typedef std::map > ProxyJIDClientSessionMap; - std::map proxySessions_; + typedef std::vector > > ProxyJIDClientSessionVector; + typedef std::map ProxySessionsMap; + ProxySessionsMap proxySessions_; boost::shared_ptr proxyFinder_; -- cgit v0.10.2-6-g49f6