diff options
author | Tobias Markmann <tm@ayena.de> | 2015-09-30 14:45:28 (GMT) |
---|---|---|
committer | Kevin Smith <kevin.smith@isode.com> | 2015-10-30 18:00:01 (GMT) |
commit | 87ff0cf5d0efcda674bbeb0aec306e92c7527ac5 (patch) | |
tree | 232a1d7ff34ed4479ce5c302b112cabc21d95305 | |
parent | 4a6950af0f324091553f7ab7271de45721b8667f (diff) | |
download | swift-87ff0cf5d0efcda674bbeb0aec306e92c7527ac5.zip swift-87ff0cf5d0efcda674bbeb0aec306e92c7527ac5.tar.bz2 |
Fix issues with HTTPTrafficFilter for HTTP proxy / BOSH usage
This patch fixes the code to handle HTTP/1.0 proxies which
do not keep the connection alive after a single request.
If a HTTPTrafficFilter returns a new header reply, the HTTP
CONNECT proxy code will issue the request over a new connection.
The final connection is kept alive, as it used for the
persistent connection forwarding.
In addition, the response status line is now passed to the
HTTPTrafficFilter handling method to provide ability to act
upon the response status code.
Missing passthrough of the HTTPTrafficFilter object the down
the stack to the HTTPConnectProxiedConnection is added.
Test-Information:
Added a unit test following an NTLM HTTP proxy authentication
with a static HTTPTrafficFilter. This and other unit tests
still pass.
Change-Id: Ida0d1aec08a60f10c1480e1eecaecbd3f87b0dca
-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 @@ -10,24 +10,24 @@ #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 { @@ -139,7 +139,8 @@ void CoreClient::connect(const ClientOptions& o) { 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 @@ -10,14 +10,14 @@ #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), @@ -34,7 +34,7 @@ BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* r 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); 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 @@ -14,16 +14,16 @@ #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(); 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 @@ -16,18 +16,18 @@ #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; @@ -45,11 +45,17 @@ HTTPConnectProxiedConnection::HTTPConnectProxiedConnection( 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()); @@ -61,6 +67,17 @@ void HTTPConnectProxiedConnection::initializeProxy() { 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); @@ -114,11 +131,11 @@ void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr<S 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; } } 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 @@ -19,16 +19,18 @@ #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)); } @@ -49,5 +51,6 @@ namespace Swift { 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 @@ -6,9 +6,9 @@ #pragma once -#include <vector> #include <string> #include <utility> +#include <vector> #include <boost/optional.hpp> @@ -25,7 +25,7 @@ class SWIFTEN_API HTTPTrafficFilter { * @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,5 +1,5 @@ /* - * Copyright (c) 2012 Isode Limited. + * Copyright (c) 2012-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -8,11 +8,12 @@ #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; @@ -110,3 +111,12 @@ void ProxiedConnection::setProxyInitializeFinished(bool success) { } 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 @@ -10,10 +10,10 @@ #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; @@ -28,7 +28,7 @@ namespace Swift { 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); @@ -53,6 +53,8 @@ namespace Swift { return server_; } + void reconnect(); + private: bool connected_; DomainNameResolver* resolver_; 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,28 +1,28 @@ /* - * 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; @@ -39,7 +39,7 @@ class BOSHConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testRead_Fragment); CPPUNIT_TEST(testHTTPRequest); CPPUNIT_TEST(testHTTPRequest_Empty); - CPPUNIT_TEST_SUITE_END(); + CPPUNIT_TEST_SUITE_END(); public: void setUp() { 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 @@ -3,26 +3,30 @@ * 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; @@ -32,7 +36,7 @@ namespace { 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; @@ -42,6 +46,46 @@ namespace { 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 { @@ -57,6 +101,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testDisconnect_AfterConnectRequest); CPPUNIT_TEST(testDisconnect_AfterConnect); CPPUNIT_TEST(testTrafficFilter); + CPPUNIT_TEST(testTrafficFilterNoConnectionReuse); CPPUNIT_TEST_SUITE_END(); public: @@ -71,6 +116,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { timerFactory = new DummyTimerFactory(); connectionFactory = new MockConnectionFactory(eventLoop); connectFinished = false; + connectFinishedWithError = false; disconnected = false; } @@ -238,12 +284,69 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { 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: @@ -307,6 +410,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { 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; } @@ -324,7 +428,6 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { StaticDomainNameResolver* resolver; MockConnectionFactory* connectionFactory; TimerFactory* timerFactory; - std::vector< boost::shared_ptr<MockConnection> > connections; bool connectFinished; bool connectFinishedWithError; bool disconnected; diff --git a/Swiften/SConscript b/Swiften/SConscript index 9b82434..97dc39c 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -407,7 +407,7 @@ if env["SCONS_STAGE"] == "build" : 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"), 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 @@ -14,24 +14,23 @@ #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, @@ -40,7 +39,8 @@ BOSHSessionStream::BOSHSessionStream( 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) { @@ -50,7 +50,7 @@ BOSHSessionStream::BOSHSessionStream( 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)); 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 @@ -17,18 +17,18 @@ #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: @@ -46,7 +46,8 @@ namespace Swift { const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword, - const TLSOptions& tlsOptions + const TLSOptions& tlsOptions, + boost::shared_ptr<HTTPTrafficFilter> trafficFilter ); ~BOSHSessionStream(); |