diff options
-rw-r--r-- | Swiften/Client/CoreClient.cpp | 23 | ||||
-rw-r--r-- | Swiften/Network/BOSHConnectionPool.cpp | 10 | ||||
-rw-r--r-- | Swiften/Network/BOSHConnectionPool.h | 8 | ||||
-rw-r--r-- | Swiften/Network/HTTPConnectProxiedConnection.cpp | 31 | ||||
-rw-r--r-- | Swiften/Network/HTTPConnectProxiedConnection.h | 7 | ||||
-rw-r--r-- | Swiften/Network/HTTPTrafficFilter.h | 4 | ||||
-rw-r--r-- | Swiften/Network/ProxiedConnection.cpp | 14 | ||||
-rw-r--r-- | Swiften/Network/ProxiedConnection.h | 6 | ||||
-rw-r--r-- | Swiften/Network/UnitTest/BOSHConnectionTest.cpp | 22 | ||||
-rw-r--r-- | Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp | 129 | ||||
-rw-r--r-- | Swiften/SConscript | 2 | ||||
-rw-r--r-- | Swiften/Session/BOSHSessionStream.cpp | 26 | ||||
-rw-r--r-- | Swiften/Session/BOSHSessionStream.h | 17 |
13 files changed, 218 insertions, 81 deletions
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index b1a375b..fa9bd33 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -7,30 +7,30 @@ #include <Swiften/Client/CoreClient.h> #include <boost/bind.hpp> #include <boost/optional.hpp> #include <boost/smart_ptr/make_shared.hpp> +#include <Swiften/Base/Algorithm.h> #include <Swiften/Base/IDGenerator.h> #include <Swiften/Base/Log.h> #include <Swiften/Base/foreach.h> -#include <Swiften/Base/Algorithm.h> #include <Swiften/Client/ClientSession.h> -#include <Swiften/TLS/CertificateVerificationError.h> -#include <Swiften/TLS/TLSError.h> +#include <Swiften/Client/ClientSessionStanzaChannel.h> #include <Swiften/Network/ChainedConnector.h> +#include <Swiften/Network/DomainNameResolveError.h> +#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h> #include <Swiften/Network/NetworkFactories.h> #include <Swiften/Network/ProxyProvider.h> -#include <Swiften/Network/DomainNameResolveError.h> -#include <Swiften/TLS/PKCS12Certificate.h> -#include <Swiften/Session/BasicSessionStream.h> -#include <Swiften/Session/BOSHSessionStream.h> -#include <Swiften/Queries/IQRouter.h> -#include <Swiften/Client/ClientSessionStanzaChannel.h> #include <Swiften/Network/SOCKS5ProxiedConnectionFactory.h> -#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Session/BOSHSessionStream.h> +#include <Swiften/Session/BasicSessionStream.h> +#include <Swiften/TLS/CertificateVerificationError.h> +#include <Swiften/TLS/PKCS12Certificate.h> +#include <Swiften/TLS/TLSError.h> namespace Swift { CoreClient::CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), disconnectRequested_(false), certificateTrustChecker(NULL) { stanzaChannel_ = new ClientSessionStanzaChannel(); stanzaChannel_->onMessageReceived.connect(boost::bind(&CoreClient::handleMessageReceived, this, _1)); @@ -136,13 +136,14 @@ void CoreClient::connect(const ClientOptions& o) { networkFactories->getEventLoop(), networkFactories->getDomainNameResolver(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword, - options.tlsOptions)); + options.tlsOptions, + options.httpTrafficFilter)); sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); bindSessionToStream(); } } diff --git a/Swiften/Network/BOSHConnectionPool.cpp b/Swiften/Network/BOSHConnectionPool.cpp index c037b34..c23e2de 100644 --- a/Swiften/Network/BOSHConnectionPool.cpp +++ b/Swiften/Network/BOSHConnectionPool.cpp @@ -7,20 +7,20 @@ #include <climits> #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> -#include <Swiften/Base/foreach.h> #include <Swiften/Base/SafeString.h> -#include <Swiften/Network/TLSConnectionFactory.h> -#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h> +#include <Swiften/Base/foreach.h> #include <Swiften/Network/CachingDomainNameResolver.h> +#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h> +#include <Swiften/Network/TLSConnectionFactory.h> namespace Swift { -BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* realResolver, ConnectionFactory* connectionFactoryParameter, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, const TLSOptions& tlsOptions) : +BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* realResolver, ConnectionFactory* connectionFactoryParameter, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, const TLSOptions& tlsOptions, boost::shared_ptr<HTTPTrafficFilter> trafficFilter) : boshURL(boshURL), connectionFactory(connectionFactoryParameter), xmlParserFactory(parserFactory), timerFactory(timerFactory), rid(initialRID), pendingTerminate(false), @@ -31,13 +31,13 @@ BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* r if (!boshHTTPConnectProxyURL.isEmpty()) { if (boshHTTPConnectProxyURL.getScheme() == "https") { connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory, tlsOptions); myConnectionFactories.push_back(connectionFactory); } - connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, boshHTTPConnectProxyURL.getHost(), URL::getPortOrDefaultPort(boshHTTPConnectProxyURL), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword); + connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, boshHTTPConnectProxyURL.getHost(), URL::getPortOrDefaultPort(boshHTTPConnectProxyURL), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword, trafficFilter); } if (boshURL.getScheme() == "https") { connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory, tlsOptions); myConnectionFactories.push_back(connectionFactory); } resolver = new CachingDomainNameResolver(realResolver, eventLoop); diff --git a/Swiften/Network/BOSHConnectionPool.h b/Swiften/Network/BOSHConnectionPool.h index d845a3d..eaef56c 100644 --- a/Swiften/Network/BOSHConnectionPool.h +++ b/Swiften/Network/BOSHConnectionPool.h @@ -11,22 +11,22 @@ #include <Swiften/Base/API.h> #include <Swiften/Base/SafeString.h> #include <Swiften/Network/BOSHConnection.h> #include <Swiften/TLS/TLSOptions.h> - namespace Swift { - class HTTPConnectProxiedConnectionFactory; - class TLSConnectionFactory; class CachingDomainNameResolver; class EventLoop; + class HTTPConnectProxiedConnectionFactory; + class HTTPTrafficFilter; + class TLSConnectionFactory; class SWIFTEN_API BOSHConnectionPool : public boost::bsignals::trackable { public: - BOSHConnectionPool(const URL& boshURL, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, const TLSOptions& tlsOptions); + BOSHConnectionPool(const URL& boshURL, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, const TLSOptions& tlsOptions, boost::shared_ptr<HTTPTrafficFilter> trafficFilter = boost::shared_ptr<HTTPTrafficFilter>()); ~BOSHConnectionPool(); void write(const SafeByteArray& data); void writeFooter(); void close(); void restartStream(); diff --git a/Swiften/Network/HTTPConnectProxiedConnection.cpp b/Swiften/Network/HTTPConnectProxiedConnection.cpp index 942361e..3ed2ac5 100644 --- a/Swiften/Network/HTTPConnectProxiedConnection.cpp +++ b/Swiften/Network/HTTPConnectProxiedConnection.cpp @@ -13,24 +13,24 @@ #include <Swiften/Network/HTTPConnectProxiedConnection.h> #include <iostream> #include <utility> +#include <boost/algorithm/string.hpp> #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> -#include <boost/algorithm/string.hpp> -#include <Swiften/Base/foreach.h> #include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/ByteArray.h> #include <Swiften/Base/Log.h> #include <Swiften/Base/String.h> -#include <Swiften/Base/ByteArray.h> -#include <Swiften/Network/HostAddressPort.h> +#include <Swiften/Base/foreach.h> #include <Swiften/Network/ConnectionFactory.h> #include <Swiften/Network/HTTPTrafficFilter.h> +#include <Swiften/Network/HostAddressPort.h> #include <Swiften/StringCodecs/Base64.h> using namespace Swift; HTTPConnectProxiedConnection::HTTPConnectProxiedConnection( DomainNameResolver* resolver, @@ -42,28 +42,45 @@ HTTPConnectProxiedConnection::HTTPConnectProxiedConnection( const SafeString& authPassword) : ProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort), authID_(authID), authPassword_(authPassword) { } +HTTPConnectProxiedConnection::~HTTPConnectProxiedConnection() { + +} + void HTTPConnectProxiedConnection::setHTTPTrafficFilter(boost::shared_ptr<HTTPTrafficFilter> trafficFilter) { trafficFilter_ = trafficFilter; } void HTTPConnectProxiedConnection::initializeProxy() { + httpResponseBuffer_.clear(); + std::stringstream connect; connect << "CONNECT " << getServer().getAddress().toString() << ":" << getServer().getPort() << " HTTP/1.1\r\n"; SafeByteArray data = createSafeByteArray(connect.str()); if (!authID_.empty() && !authPassword_.empty()) { append(data, createSafeByteArray("Proxy-Authorization: Basic ")); SafeByteArray credentials = authID_; append(credentials, createSafeByteArray(":")); append(credentials, authPassword_); append(data, Base64::encode(credentials)); append(data, createSafeByteArray("\r\n")); } + else if (!nextHTTPRequestHeaders_.empty()) { + typedef std::pair<std::string, std::string> StringPair; + foreach(const StringPair& headerField, nextHTTPRequestHeaders_) { + append(data, createSafeByteArray(headerField.first)); + append(data, createSafeByteArray(": ")); + append(data, createSafeByteArray(headerField.second)); + append(data, createSafeByteArray("\r\n")); + } + + nextHTTPRequestHeaders_.clear(); + } append(data, createSafeByteArray("\r\n")); SWIFT_LOG(debug) << "HTTP Proxy send headers: " << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl; write(data); } void HTTPConnectProxiedConnection::parseHTTPHeader(const std::string& data, std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields) { @@ -111,17 +128,17 @@ void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr<S return; } parseHTTPHeader(httpResponseBuffer_.substr(0, headerEnd), statusLine, headerFields); if (trafficFilter_) { - std::vector<std::pair<std::string, std::string> > newHeaderFields = trafficFilter_->filterHTTPResponseHeader(headerFields); + std::vector<std::pair<std::string, std::string> > newHeaderFields = trafficFilter_->filterHTTPResponseHeader(statusLine, headerFields); if (!newHeaderFields.empty()) { std::stringstream statusLine; - statusLine << "CONNECT " << getServer().getAddress().toString() << ":" << getServer().getPort(); - sendHTTPRequest(statusLine.str(), newHeaderFields); + reconnect(); + nextHTTPRequestHeaders_ = newHeaderFields; return; } } std::vector<std::string> tmp = String::split(statusLine, ' '); if (tmp.size() > 1) { diff --git a/Swiften/Network/HTTPConnectProxiedConnection.h b/Swiften/Network/HTTPConnectProxiedConnection.h index 11431bf..24f36b9 100644 --- a/Swiften/Network/HTTPConnectProxiedConnection.h +++ b/Swiften/Network/HTTPConnectProxiedConnection.h @@ -16,22 +16,24 @@ #include <boost/shared_ptr.hpp> #include <Swiften/Base/API.h> #include <Swiften/Network/ProxiedConnection.h> namespace Swift { - class DomainNameResolver; class ConnectionFactory; + class DomainNameResolver; class EventLoop; - class TimerFactory; class HTTPTrafficFilter; + class TimerFactory; class SWIFTEN_API HTTPConnectProxiedConnection : public ProxiedConnection { public: typedef boost::shared_ptr<HTTPConnectProxiedConnection> ref; + virtual ~HTTPConnectProxiedConnection(); + static ref create(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) { return ref(new HTTPConnectProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, authID, authPassword)); } void setHTTPTrafficFilter(boost::shared_ptr<HTTPTrafficFilter> trafficFilter); @@ -46,8 +48,9 @@ namespace Swift { private: SafeByteArray authID_; SafeByteArray authPassword_; boost::shared_ptr<HTTPTrafficFilter> trafficFilter_; std::string httpResponseBuffer_; + std::vector<std::pair<std::string, std::string> > nextHTTPRequestHeaders_; }; } diff --git a/Swiften/Network/HTTPTrafficFilter.h b/Swiften/Network/HTTPTrafficFilter.h index da96c96..cc1af21 100644 --- a/Swiften/Network/HTTPTrafficFilter.h +++ b/Swiften/Network/HTTPTrafficFilter.h @@ -3,15 +3,15 @@ * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <vector> #include <string> #include <utility> +#include <vector> #include <boost/optional.hpp> #include <Swiften/Base/API.h> namespace Swift { @@ -22,10 +22,10 @@ class SWIFTEN_API HTTPTrafficFilter { /** * @brief This method is called by the HTTPConnectPRoxiedConnection on every incoming HTTP response. * It can be used to insert additional HTTP requests into the HTTP CONNECT proxy initalization process. * @return A vector of HTTP header fields to use in a new request. If an empty vector is returned, * no new request will be send and the normal proxy logic continues. */ - virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::vector<std::pair<std::string, std::string> >& /* responseHeader */) = 0; + virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::string& statusLine, const std::vector<std::pair<std::string, std::string> >& /* responseHeader */) = 0; }; } diff --git a/Swiften/Network/ProxiedConnection.cpp b/Swiften/Network/ProxiedConnection.cpp index a3fb352..17f7e09 100644 --- a/Swiften/Network/ProxiedConnection.cpp +++ b/Swiften/Network/ProxiedConnection.cpp @@ -1,21 +1,22 @@ /* - * Copyright (c) 2012 Isode Limited. + * Copyright (c) 2012-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Network/ProxiedConnection.h> #include <iostream> + #include <boost/bind.hpp> #include <Swiften/Base/ByteArray.h> -#include <Swiften/Network/HostAddressPort.h> #include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/HostAddressPort.h> using namespace Swift; ProxiedConnection::ProxiedConnection( DomainNameResolver* resolver, ConnectionFactory* connectionFactory, @@ -107,6 +108,15 @@ void ProxiedConnection::setProxyInitializeFinished(bool success) { connected_ = success; if (!success) { disconnect(); } onConnectFinished(!success); } + +void ProxiedConnection::reconnect() { + if (connected_) { + connection_->onDataRead.disconnect(boost::bind(&ProxiedConnection::handleDataRead, shared_from_this(), _1)); + connection_->onDisconnected.disconnect(boost::bind(&ProxiedConnection::handleDisconnected, shared_from_this(), _1)); + connection_->disconnect(); + } + connect(server_); +} diff --git a/Swiften/Network/ProxiedConnection.h b/Swiften/Network/ProxiedConnection.h index 91488a1..f301e84 100644 --- a/Swiften/Network/ProxiedConnection.h +++ b/Swiften/Network/ProxiedConnection.h @@ -7,16 +7,16 @@ #pragma once #include <boost/enable_shared_from_this.hpp> #include <Swiften/Base/API.h> +#include <Swiften/Base/SafeString.h> #include <Swiften/Network/Connection.h> #include <Swiften/Network/Connector.h> #include <Swiften/Network/HostAddressPort.h> -#include <Swiften/Base/SafeString.h> namespace boost { class thread; namespace system { class error_code; } @@ -25,13 +25,13 @@ namespace boost { namespace Swift { class ConnectionFactory; class SWIFTEN_API ProxiedConnection : public Connection, public boost::enable_shared_from_this<ProxiedConnection> { public: ProxiedConnection(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort); - ~ProxiedConnection(); + virtual ~ProxiedConnection(); virtual void listen(); virtual void connect(const HostAddressPort& address); virtual void disconnect(); virtual void write(const SafeByteArray& data); @@ -50,12 +50,14 @@ namespace Swift { virtual void handleProxyInitializeData(boost::shared_ptr<SafeByteArray> data) = 0; const HostAddressPort& getServer() const { return server_; } + void reconnect(); + private: bool connected_; DomainNameResolver* resolver_; ConnectionFactory* connectionFactory_; TimerFactory* timerFactory_; std::string proxyHost_; diff --git a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp index a115f9b..c8bf1f0 100644 --- a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp +++ b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp @@ -1,31 +1,31 @@ /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/optional.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + #include <QA/Checker/IO.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/optional.hpp> -#include <boost/bind.hpp> -#include <boost/smart_ptr/make_shared.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/lexical_cast.hpp> - #include <Swiften/Base/Algorithm.h> +#include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/Network/BOSHConnection.h> #include <Swiften/Network/Connection.h> #include <Swiften/Network/ConnectionFactory.h> -#include <Swiften/Network/BOSHConnection.h> +#include <Swiften/Network/DummyTimerFactory.h> #include <Swiften/Network/HostAddressPort.h> #include <Swiften/Network/StaticDomainNameResolver.h> -#include <Swiften/Network/DummyTimerFactory.h> -#include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/Parser/PlatformXMLParserFactory.h> using namespace Swift; class BOSHConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(BOSHConnectionTest); @@ -36,13 +36,13 @@ class BOSHConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testReadiness_noSID); CPPUNIT_TEST(testWrite_Receive); CPPUNIT_TEST(testWrite_ReceiveTwice); CPPUNIT_TEST(testRead_Fragment); CPPUNIT_TEST(testHTTPRequest); CPPUNIT_TEST(testHTTPRequest_Empty); - CPPUNIT_TEST_SUITE_END(); + CPPUNIT_TEST_SUITE_END(); public: void setUp() { eventLoop = new DummyEventLoop(); connectionFactory = new MockConnectionFactory(eventLoop); resolver = new StaticDomainNameResolver(eventLoop); diff --git a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp index d3db79d..56ace5c 100644 --- a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp +++ b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp @@ -1,50 +1,94 @@ /* * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ + +#include <boost/algorithm/string.hpp> +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/optional.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + #include <QA/Checker/IO.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/optional.hpp> -#include <boost/bind.hpp> -#include <boost/smart_ptr/make_shared.hpp> -#include <boost/shared_ptr.hpp> - #include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/Network/Connection.h> #include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/DummyTimerFactory.h> #include <Swiften/Network/HTTPConnectProxiedConnection.h> #include <Swiften/Network/HTTPTrafficFilter.h> #include <Swiften/Network/HostAddressPort.h> #include <Swiften/Network/StaticDomainNameResolver.h> -#include <Swiften/Network/DummyTimerFactory.h> -#include <Swiften/EventLoop/DummyEventLoop.h> -#include <Swiften/Base/Log.h> using namespace Swift; namespace { class ExampleHTTPTrafficFilter : public HTTPTrafficFilter { public: ExampleHTTPTrafficFilter() {} virtual ~ExampleHTTPTrafficFilter() {} - virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::vector<std::pair<std::string, std::string> >& response) { + virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::string& /* statusLine */, const std::vector<std::pair<std::string, std::string> >& response) { filterResponses.push_back(response); SWIFT_LOG(debug) << std::endl; return filterResponseReturn; } std::vector<std::vector<std::pair<std::string, std::string> > > filterResponses; std::vector<std::pair<std::string, std::string> > filterResponseReturn; }; + + class ProxyAuthenticationHTTPTrafficFilter : public HTTPTrafficFilter { + static std::string to_lower(const std::string& str) { + std::string lower = str; + boost::algorithm::to_lower(lower); + return lower; + } + + public: + ProxyAuthenticationHTTPTrafficFilter() {} + virtual ~ProxyAuthenticationHTTPTrafficFilter() {} + + virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::string& statusLine, const std::vector<std::pair<std::string, std::string> >& response) { + std::vector<std::pair<std::string, std::string> > filterResponseReturn; + std::vector<std::string> statusLineFields; + boost::split(statusLineFields, statusLine, boost::is_any_of(" "), boost::token_compress_on); + + int statusCode = boost::lexical_cast<int>(statusLineFields[1]); + if (statusCode == 407) { + typedef std::pair<std::string, std::string> StrPair; + foreach (const StrPair& field, response) { + if (to_lower(field.first) == to_lower("Proxy-Authenticate")) { + if (field.second.size() >= 6 && field.second.substr(0, 6) == " NTLM ") { + filterResponseReturn.push_back(std::pair<std::string, std::string>("Proxy-Authorization", "NTLM TlRMTVNTUAADAAAAGAAYAHIAAAAYABgAigAAABIAEgBIAAAABgAGAFoAAAASABIVNTUAADAAYAAAABAAEACiAAAANYKI4gUBKAoAAAAPTABBAEIAUwBNAE8ASwBFADMAXwBxAGEATABBAEIAUwBNAE8ASwBFADMA0NKq8HYYhj8AAAAAAAAAAAAAAAAAAAAAOIiih3mR+AkyM4r99sy1mdFonCu2ILODro1WTTrJ4b4JcXEzUBA2Ig==")); + return filterResponseReturn; + } + else if (field.second.size() >= 5 && field.second.substr(0, 5) == " NTLM") { + filterResponseReturn.push_back(std::pair<std::string, std::string>("Proxy-Authorization", "NTLM TlRMTVNTUAABAAAAt7II4gkACQAxAAAACQAJACgAAAVNTUAADAAFASgKAAAAD0xBQlNNT0tFM1dPUktHUk9VUA==")); + return filterResponseReturn; + } + } + } + + return filterResponseReturn; + } + else { + return std::vector<std::pair<std::string, std::string> >(); + } + } + }; } class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(HTTPConnectProxiedConnectionTest); CPPUNIT_TEST(testConnect_CreatesConnectionToProxy); CPPUNIT_TEST(testConnect_SendsConnectRequest); @@ -54,12 +98,13 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testConnect_ReceiveErrorConnectResponse); CPPUNIT_TEST(testConnect_ReceiveDataAfterConnect); CPPUNIT_TEST(testWrite_AfterConnect); CPPUNIT_TEST(testDisconnect_AfterConnectRequest); CPPUNIT_TEST(testDisconnect_AfterConnect); CPPUNIT_TEST(testTrafficFilter); + CPPUNIT_TEST(testTrafficFilterNoConnectionReuse); CPPUNIT_TEST_SUITE_END(); public: void setUp() { proxyHost = "doo.bah"; proxyPort = 1234; @@ -68,12 +113,13 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { eventLoop = new DummyEventLoop(); resolver = new StaticDomainNameResolver(eventLoop); resolver->addAddress(proxyHost, proxyHostAddress.getAddress()); timerFactory = new DummyTimerFactory(); connectionFactory = new MockConnectionFactory(eventLoop); connectFinished = false; + connectFinishedWithError = false; disconnected = false; } void tearDown() { delete timerFactory; delete connectionFactory; @@ -235,18 +281,75 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { // remove the default response from the traffic filter httpTrafficFilter->filterResponseReturn.clear(); eventLoop->processEvents(); // verify that the traffic filter answer is send over the wire - CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345\r\nAuthorization:Negotiate a87421000492aa874209af8bc028\r\n\r\n"), connectionFactory->connections[0]->dataWritten); + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\nAuthorization: Negotiate a87421000492aa874209af8bc028\r\n\r\n"), connectionFactory->connections[1]->dataWritten); // verify that after without the default response, the traffic filter is skipped, authentication proceeds and traffic goes right through - connectionFactory->connections[0]->dataWritten.clear(); + connectionFactory->connections[1]->dataWritten.clear(); testling->write(createSafeByteArray("abcdef")); - CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), connectionFactory->connections[0]->dataWritten); + CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), connectionFactory->connections[1]->dataWritten); + } + + void testTrafficFilterNoConnectionReuse() { + HTTPConnectProxiedConnection::ref testling = createTestling(); + + boost::shared_ptr<ProxyAuthenticationHTTPTrafficFilter> httpTrafficFilter = boost::make_shared<ProxyAuthenticationHTTPTrafficFilter>(); + testling->setHTTPTrafficFilter(httpTrafficFilter); + + connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); + + // First HTTP CONNECT request assumes the proxy will work. + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n" + "\r\n"), connectionFactory->connections[0]->dataWritten); + + // First reply presents initiator with authentication options. + connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef( + "HTTP/1.0 407 ProxyAuthentication Required\r\n" + "proxy-Authenticate: Negotiate\r\n" + "Proxy-Authenticate: Kerberos\r\n" + "proxy-Authenticate: NTLM\r\n" + "\r\n")); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(false, connectFinished); + CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError); + + // The HTTP proxy responds with code 407, so the traffic filter should inject the authentication response on a new connection. + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n" + "Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4gkACQAxAAAACQAJACgAAAVNTUAADAAFASgKAAAAD0xBQlNNT0tFM1dPUktHUk9VUA==\r\n" + "\r\n"), connectionFactory->connections[1]->dataWritten); + + // The proxy responds with another authentication step. + connectionFactory->connections[1]->onDataRead(createSafeByteArrayRef( + "HTTP/1.0 407 ProxyAuthentication Required\r\n" + "Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAEAAQADgAAAA1goriluCDYHcYI/sAAAAAAAAAAFQAVABIAAAABQLODgAAAA9TAFAASQBSAEkAVAAxAEIAAgAQAFMAUABJAFIASQBUADEAQgABABAAUwBQAEkAUgBJAFQAMQBCAAQAEABzAHAAaQByAGkAdAAxAGIAAwAQAHMAcABpAHIAaQB0ADEAYgAAAAAA\r\n" + "\r\n")); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(false, connectFinished); + CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError); + + // Last HTTP request that should succeed. Further traffic will go over the connection of this request. + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n" + "Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHIAAAAYABgAigAAABIAEgBIAAAABgAGAFoAAAASABIVNTUAADAAYAAAABAAEACiAAAANYKI4gUBKAoAAAAPTABBAEIAUwBNAE8ASwBFADMAXwBxAGEATABBAEIAUwBNAE8ASwBFADMA0NKq8HYYhj8AAAAAAAAAAAAAAAAAAAAAOIiih3mR+AkyM4r99sy1mdFonCu2ILODro1WTTrJ4b4JcXEzUBA2Ig==\r\n" + "\r\n"), connectionFactory->connections[2]->dataWritten); + + connectionFactory->connections[2]->onDataRead(createSafeByteArrayRef( + "HTTP/1.0 200 OK\r\n" + "\r\n")); + eventLoop->processEvents(); + + // The HTTP CONNECT proxy initialization finished without error. + CPPUNIT_ASSERT_EQUAL(true, connectFinished); + CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError); + + // Further traffic is written directly, without interception of the filter. + connectionFactory->connections[2]->dataWritten.clear(); + testling->write(createSafeByteArray("This is some basic data traffic.")); + CPPUNIT_ASSERT_EQUAL(createByteArray("This is some basic data traffic."), connectionFactory->connections[2]->dataWritten); } private: HTTPConnectProxiedConnection::ref createTestling() { boost::shared_ptr<HTTPConnectProxiedConnection> c = HTTPConnectProxiedConnection::create(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, "", ""); c->onConnectFinished.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleConnectFinished, this, _1)); @@ -304,12 +407,13 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop) { } boost::shared_ptr<Connection> createConnection() { boost::shared_ptr<MockConnection> connection = boost::make_shared<MockConnection>(failingPorts, eventLoop); connections.push_back(connection); + SWIFT_LOG(debug) << "new connection created" << std::endl; return connection; } EventLoop* eventLoop; std::vector< boost::shared_ptr<MockConnection> > connections; std::vector<HostAddressPort> failingPorts; @@ -321,13 +425,12 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { int proxyPort; HostAddressPort host; DummyEventLoop* eventLoop; StaticDomainNameResolver* resolver; MockConnectionFactory* connectionFactory; TimerFactory* timerFactory; - std::vector< boost::shared_ptr<MockConnection> > connections; bool connectFinished; bool connectFinishedWithError; bool disconnected; boost::optional<Connection::Error> disconnectedError; ByteArray dataRead; }; diff --git a/Swiften/SConscript b/Swiften/SConscript index 9b82434..97dc39c 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -404,13 +404,13 @@ if env["SCONS_STAGE"] == "build" : File("LinkLocal/UnitTest/LinkLocalServiceTest.cpp"), File("MUC/UnitTest/MUCTest.cpp"), File("MUC/UnitTest/MockMUC.cpp"), File("Network/UnitTest/HostAddressTest.cpp"), File("Network/UnitTest/ConnectorTest.cpp"), File("Network/UnitTest/ChainedConnectorTest.cpp"), - File("Network/UnitTest/DomainNameServiceQueryTest.cpp"), + File("Network/UnitTest/DomainNameServiceQueryTest.cpp"), File("Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionPoolTest.cpp"), File("Parser/PayloadParsers/UnitTest/BlockParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"), diff --git a/Swiften/Session/BOSHSessionStream.cpp b/Swiften/Session/BOSHSessionStream.cpp index 62942b9..c5d0dd5 100644 --- a/Swiften/Session/BOSHSessionStream.cpp +++ b/Swiften/Session/BOSHSessionStream.cpp @@ -11,49 +11,49 @@ #include <boost/random/mersenne_twister.hpp> #include <boost/random/uniform_int.hpp> #include <boost/random/variate_generator.hpp> #include <Swiften/Base/foreach.h> #include <Swiften/Elements/StreamType.h> -#include <Swiften/StreamStack/XMPPLayer.h> -#include <Swiften/StreamStack/StreamStack.h> -#include <Swiften/StreamStack/ConnectionLayer.h> -#include <Swiften/StreamStack/WhitespacePingLayer.h> +#include <Swiften/EventLoop/EventLoop.h> #include <Swiften/StreamStack/CompressionLayer.h> +#include <Swiften/StreamStack/ConnectionLayer.h> +#include <Swiften/StreamStack/StreamStack.h> #include <Swiften/StreamStack/TLSLayer.h> -#include <Swiften/TLS/TLSContextFactory.h> +#include <Swiften/StreamStack/WhitespacePingLayer.h> +#include <Swiften/StreamStack/XMPPLayer.h> #include <Swiften/TLS/TLSContext.h> -#include <Swiften/EventLoop/EventLoop.h> +#include <Swiften/TLS/TLSContextFactory.h> namespace Swift { -BOSHSessionStream::BOSHSessionStream( - const URL& boshURL, - PayloadParserFactoryCollection* payloadParserFactories, - PayloadSerializerCollection* payloadSerializers, +BOSHSessionStream::BOSHSessionStream(const URL& boshURL, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers, ConnectionFactory* connectionFactory, - TLSContextFactory* tlsContextFactory, + TLSContextFactory* tlsContextFactory, TimerFactory* timerFactory, XMLParserFactory* xmlParserFactory, EventLoop* eventLoop, DomainNameResolver* resolver, const std::string& to, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, - const TLSOptions& tlsOptions) : + const TLSOptions& tlsOptions, + boost::shared_ptr<HTTPTrafficFilter> trafficFilter) : available(false), eventLoop(eventLoop), firstHeader(true) { boost::mt19937 random; boost::uniform_int<unsigned long long> dist(0, (1LL<<53) - 1); random.seed(static_cast<unsigned int>(time(NULL))); unsigned long long initialRID = boost::variate_generator<boost::mt19937&, boost::uniform_int<unsigned long long> >(random, dist)(); - connectionPool = new BOSHConnectionPool(boshURL, resolver, connectionFactory, xmlParserFactory, tlsContextFactory, timerFactory, eventLoop, to, initialRID, boshHTTPConnectProxyURL, boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword, tlsOptions); + connectionPool = new BOSHConnectionPool(boshURL, resolver, connectionFactory, xmlParserFactory, tlsContextFactory, timerFactory, eventLoop, to, initialRID, boshHTTPConnectProxyURL, boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword, tlsOptions, trafficFilter); connectionPool->onSessionTerminated.connect(boost::bind(&BOSHSessionStream::handlePoolSessionTerminated, this, _1)); connectionPool->onSessionStarted.connect(boost::bind(&BOSHSessionStream::handlePoolSessionStarted, this)); connectionPool->onXMPPDataRead.connect(boost::bind(&BOSHSessionStream::handlePoolXMPPDataRead, this, _1)); connectionPool->onBOSHDataRead.connect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataRead, this, _1)); connectionPool->onBOSHDataWritten.connect(boost::bind(&BOSHSessionStream::handlePoolBOSHDataWritten, this, _1)); diff --git a/Swiften/Session/BOSHSessionStream.h b/Swiften/Session/BOSHSessionStream.h index 436b941..817f5e9 100644 --- a/Swiften/Session/BOSHSessionStream.h +++ b/Swiften/Session/BOSHSessionStream.h @@ -14,24 +14,24 @@ #include <Swiften/Elements/StreamType.h> #include <Swiften/EventLoop/EventOwner.h> #include <Swiften/Network/BOSHConnectionPool.h> #include <Swiften/Session/SessionStream.h> #include <Swiften/TLS/TLSOptions.h> - namespace Swift { - class TimerFactory; + class CompressionLayer; + class ConnectionLayer; + class EventLoop; + class HTTPTrafficFilter; class PayloadParserFactoryCollection; class PayloadSerializerCollection; class StreamStack; - class XMPPLayer; - class ConnectionLayer; - class CompressionLayer; - class XMLParserFactory; class TLSContextFactory; - class EventLoop; + class TimerFactory; + class XMLParserFactory; + class XMPPLayer; class SWIFTEN_API BOSHSessionStream : public SessionStream, public EventOwner, public boost::enable_shared_from_this<BOSHSessionStream> { public: BOSHSessionStream( const URL& boshURL, PayloadParserFactoryCollection* payloadParserFactories, @@ -43,13 +43,14 @@ namespace Swift { EventLoop* eventLoop, DomainNameResolver* resolver, const std::string& to, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, - const TLSOptions& tlsOptions + const TLSOptions& tlsOptions, + boost::shared_ptr<HTTPTrafficFilter> trafficFilter ); ~BOSHSessionStream(); virtual void close(); virtual bool isOpen(); |