diff options
| -rw-r--r-- | Swiften/Client/ClientOptions.h | 12 | ||||
| -rw-r--r-- | Swiften/Client/CoreClient.cpp | 4 | ||||
| -rw-r--r-- | Swiften/Network/HTTPConnectProxiedConnection.cpp | 63 | ||||
| -rw-r--r-- | Swiften/Network/HTTPConnectProxiedConnection.h | 11 | ||||
| -rw-r--r-- | Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp | 10 | ||||
| -rw-r--r-- | Swiften/Network/HTTPConnectProxiedConnectionFactory.h | 9 | ||||
| -rw-r--r-- | Swiften/Network/HTTPTrafficFilter.cpp | 15 | ||||
| -rw-r--r-- | Swiften/Network/HTTPTrafficFilter.h | 31 | ||||
| -rw-r--r-- | Swiften/Network/SConscript | 7 | ||||
| -rw-r--r-- | Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp | 60 | 
10 files changed, 204 insertions, 18 deletions
| diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h index 884b1bb..20e7443 100644 --- a/Swiften/Client/ClientOptions.h +++ b/Swiften/Client/ClientOptions.h @@ -1,15 +1,19 @@  /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once +#include <boost/shared_ptr.hpp> +  #include <Swiften/Base/URL.h>  #include <Swiften/Base/SafeString.h>  namespace Swift { +	class HTTPTrafficFilter; +  	struct ClientOptions {  		enum UseTLS {  			NeverUseTLS, @@ -134,5 +138,11 @@ namespace Swift {  		 */  		SafeString boshHTTPConnectProxyAuthID;  		SafeString boshHTTPConnectProxyAuthPassword; + +		/** +		 * This can be initialized with a custom HTTPTrafficFilter, which allows HTTP CONNECT +		 * proxy initialization to be customized. +		 */ +		boost::shared_ptr<HTTPTrafficFilter> httpTrafficFilter;  	};  } diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index 4416ee4..842488d 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2014 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -96,7 +96,7 @@ void CoreClient::connect(const ClientOptions& o) {  			std::string proxyHostname = o.manualProxyHostname.empty() ? systemHTTPConnectProxy.getAddress().toString() : o.manualProxyHostname;  			int proxyPort = o.manualProxyPort == -1 ? systemHTTPConnectProxy.getPort() : o.manualProxyPort;  			SWIFT_LOG(debug) << "Proxy: " << proxyHostname << ":" << proxyPort << std::endl; -			proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory(), proxyHostname, proxyPort)); +			proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory(), proxyHostname, proxyPort, o.httpTrafficFilter));  			useDirectConnection = false;  			break;  		} diff --git a/Swiften/Network/HTTPConnectProxiedConnection.cpp b/Swiften/Network/HTTPConnectProxiedConnection.cpp index ead48e9..fc5a6c6 100644 --- a/Swiften/Network/HTTPConnectProxiedConnection.cpp +++ b/Swiften/Network/HTTPConnectProxiedConnection.cpp @@ -5,7 +5,7 @@   */  /* - * Copyright (c) 2011-2012 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -14,15 +14,20 @@  #include <Swiften/Network/HTTPConnectProxiedConnection.h>  #include <iostream> +#include <utility> +  #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/Log.h>  #include <Swiften/Base/String.h>  #include <Swiften/Base/ByteArray.h>  #include <Swiften/Network/HostAddressPort.h>  #include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/HTTPTrafficFilter.h>  #include <Swiften/StringCodecs/Base64.h>  using namespace Swift; @@ -40,6 +45,9 @@ HTTPConnectProxiedConnection::HTTPConnectProxiedConnection(  			authPassword_(authPassword) {  } +void HTTPConnectProxiedConnection::setHTTPTrafficFilter(boost::shared_ptr<HTTPTrafficFilter> trafficFilter) { +	trafficFilter_ = trafficFilter; +}  void HTTPConnectProxiedConnection::initializeProxy() {  	std::stringstream connect; @@ -58,9 +66,58 @@ void HTTPConnectProxiedConnection::initializeProxy() {  	write(data);  } +void HTTPConnectProxiedConnection::parseHTTPHeader(const std::string& data, std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields) { +	std::istringstream dataStream(data); + +	// parse status line +	std::getline(dataStream, statusLine); + +	// parse fields +	std::string headerLine; +	std::string::size_type splitIndex; +	while (std::getline(dataStream, headerLine) && headerLine != "\r") { +		splitIndex = headerLine.find(':', 0); +		if (splitIndex != std::string::npos) { +			headerFields.push_back(std::pair<std::string, std::string>(headerLine.substr(0, splitIndex), headerLine.substr(splitIndex + 1))); +		} +	} +} + +void HTTPConnectProxiedConnection::sendHTTPRequest(const std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields) { +	typedef std::pair<std::string, std::string> HTTPHeaderField; +	std::stringstream request; + +	request << statusLine << "\r\n"; +	foreach (const HTTPHeaderField& field, headerFields) { +		request << field.first << ":" << field.second << "\r\n"; +	} +	request << "\r\n"; +	write(createSafeByteArray(request.str())); +} +  void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr<SafeByteArray> data) { -	SWIFT_LOG(debug) << byteArrayToString(ByteArray(data->begin(), data->end())) << std::endl; -	std::vector<std::string> tmp = String::split(byteArrayToString(ByteArray(data->begin(), data->end())), ' '); +	std::string dataString = byteArrayToString(ByteArray(data->begin(), data->end())); +	SWIFT_LOG(debug) << data << std::endl; + +	std::string statusLine; +	std::vector<std::pair<std::string, std::string> > headerFields; + +	std::string::size_type headerEnd = dataString.find("\r\n\r\n", 0); + +	parseHTTPHeader(dataString.substr(0, headerEnd), statusLine, headerFields); + +	if (trafficFilter_) { +		std::vector<std::pair<std::string, std::string> > newHeaderFields = trafficFilter_->filterHTTPResponseHeader(headerFields); +		if (!newHeaderFields.empty()) { +			std::stringstream statusLine; +			statusLine << "CONNECT " << getServer().getAddress().toString() << ":" << getServer().getPort(); +			sendHTTPRequest(statusLine.str(), newHeaderFields); +			SWIFT_LOG(debug) << "send HTTP request from traffic filter" << std::endl; +			return; +		} +	} + +	std::vector<std::string> tmp = String::split(statusLine, ' ');  	if (tmp.size() > 1) {  		try {  			int status = boost::lexical_cast<int>(tmp[1]); diff --git a/Swiften/Network/HTTPConnectProxiedConnection.h b/Swiften/Network/HTTPConnectProxiedConnection.h index 5b28242..7d83863 100644 --- a/Swiften/Network/HTTPConnectProxiedConnection.h +++ b/Swiften/Network/HTTPConnectProxiedConnection.h @@ -5,7 +5,7 @@   */  /* - * Copyright (c) 2011-2012 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -13,6 +13,8 @@  #pragma once +#include <boost/shared_ptr.hpp> +  #include <Swiften/Base/API.h>  #include <Swiften/Network/ProxiedConnection.h> @@ -21,6 +23,7 @@ namespace Swift {  	class ConnectionFactory;  	class EventLoop;  	class TimerFactory; +	class HTTPTrafficFilter;  	class SWIFTEN_API HTTPConnectProxiedConnection : public ProxiedConnection {  		public: @@ -30,14 +33,20 @@ namespace Swift {  				return ref(new HTTPConnectProxiedConnection(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, authID, authPassword));  			} +			void setHTTPTrafficFilter(boost::shared_ptr<HTTPTrafficFilter> trafficFilter); +  		private:  			HTTPConnectProxiedConnection(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword);  			virtual void initializeProxy();  			virtual void handleProxyInitializeData(boost::shared_ptr<SafeByteArray> data); +			void sendHTTPRequest(const std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields); +			void parseHTTPHeader(const std::string& data, std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields); +  		private:  			SafeByteArray authID_;  			SafeByteArray authPassword_; +			boost::shared_ptr<HTTPTrafficFilter> trafficFilter_;  	};  } diff --git a/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp b/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp index e50c5d0..91b241e 100644 --- a/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp +++ b/Swiften/Network/HTTPConnectProxiedConnectionFactory.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.   */ @@ -16,15 +16,17 @@  namespace Swift { -HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort),  authID_(""), authPassword_("") { +HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, boost::shared_ptr<HTTPTrafficFilter> httpTrafficFilter) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort),  authID_(""), authPassword_(""), httpTrafficFilter_(httpTrafficFilter) {  } -HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort), authID_(authID), authPassword_(authPassword) { +HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword, boost::shared_ptr<HTTPTrafficFilter> httpTrafficFilter) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort), authID_(authID), authPassword_(authPassword),  httpTrafficFilter_(httpTrafficFilter) {  }  boost::shared_ptr<Connection> HTTPConnectProxiedConnectionFactory::createConnection() { -	return HTTPConnectProxiedConnection::create(resolver_, connectionFactory_, timerFactory_, proxyHost_, proxyPort_, authID_, authPassword_); +	HTTPConnectProxiedConnection::ref proxyConnection = HTTPConnectProxiedConnection::create(resolver_, connectionFactory_, timerFactory_, proxyHost_, proxyPort_, authID_, authPassword_); +	proxyConnection->setHTTPTrafficFilter(httpTrafficFilter_); +	return proxyConnection;  }  } diff --git a/Swiften/Network/HTTPConnectProxiedConnectionFactory.h b/Swiften/Network/HTTPConnectProxiedConnectionFactory.h index e7a4283..b4ddd4e 100644 --- a/Swiften/Network/HTTPConnectProxiedConnectionFactory.h +++ b/Swiften/Network/HTTPConnectProxiedConnectionFactory.h @@ -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.   */ @@ -20,10 +20,12 @@ namespace Swift {  	class DomainNameResolver;  	class TimerFactory;  	class EventLoop; +	class HTTPTrafficFilter; +  	class HTTPConnectProxiedConnectionFactory : public ConnectionFactory {  		public: -			HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort); -			HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword); +			HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, boost::shared_ptr<HTTPTrafficFilter> httpTrafficFilter = boost::shared_ptr<HTTPTrafficFilter>()); +			HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword, boost::shared_ptr<HTTPTrafficFilter> httpTrafficFilter = boost::shared_ptr<HTTPTrafficFilter>());  			virtual boost::shared_ptr<Connection> createConnection(); @@ -35,5 +37,6 @@ namespace Swift {  			int proxyPort_;  			SafeString authID_;  			SafeString authPassword_; +			boost::shared_ptr<HTTPTrafficFilter> httpTrafficFilter_;  	};  } diff --git a/Swiften/Network/HTTPTrafficFilter.cpp b/Swiften/Network/HTTPTrafficFilter.cpp new file mode 100644 index 0000000..d40fbdf --- /dev/null +++ b/Swiften/Network/HTTPTrafficFilter.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/Network/HTTPTrafficFilter.h> + +namespace Swift { + +HTTPTrafficFilter::~HTTPTrafficFilter() { + +} + +} diff --git a/Swiften/Network/HTTPTrafficFilter.h b/Swiften/Network/HTTPTrafficFilter.h new file mode 100644 index 0000000..da96c96 --- /dev/null +++ b/Swiften/Network/HTTPTrafficFilter.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <vector> +#include <string> +#include <utility> + +#include <boost/optional.hpp> + +#include <Swiften/Base/API.h> + +namespace Swift { + +class SWIFTEN_API HTTPTrafficFilter { +	public: +		virtual ~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; +}; + +} diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript index 284c1f4..c1f5c19 100644 --- a/Swiften/Network/SConscript +++ b/Swiften/Network/SConscript @@ -25,9 +25,9 @@ sourceList = [  			"ConnectionServerFactory.cpp",  			"DummyConnection.cpp",  			"FakeConnection.cpp", - 			"ChainedConnector.cpp", - 			"Connector.cpp", - 			"Connection.cpp", +			"ChainedConnector.cpp", +			"Connector.cpp", +			"Connection.cpp",  			"TimerFactory.cpp",  			"DummyTimerFactory.cpp",  			"BoostTimerFactory.cpp", @@ -53,6 +53,7 @@ sourceList = [  			"NATTraversalForwardPortRequest.cpp",  			"NATTraversalRemovePortForwardingRequest.cpp",  			"NATTraversalInterface.cpp", +			"HTTPTrafficFilter.cpp",  	]  if myenv.get("unbound", False) : diff --git a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp index 7b9a5e6..fb6914e 100644 --- a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp +++ b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */ @@ -17,13 +17,33 @@  #include <Swiften/Network/Connection.h>  #include <Swiften/Network/ConnectionFactory.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) { +				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 HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture {  		CPPUNIT_TEST_SUITE(HTTPConnectProxiedConnectionTest);  		CPPUNIT_TEST(testConnect_CreatesConnectionToProxy); @@ -35,6 +55,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture {  		CPPUNIT_TEST(testWrite_AfterConnect);  		CPPUNIT_TEST(testDisconnect_AfterConnectRequest);  		CPPUNIT_TEST(testDisconnect_AfterConnect); +		CPPUNIT_TEST(testTrafficFilter);  		CPPUNIT_TEST_SUITE_END();  	public: @@ -168,6 +189,43 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT(!disconnectedError);  		} +		void testTrafficFilter() { +			HTTPConnectProxiedConnection::ref testling(createTestling()); + +			boost::shared_ptr<ExampleHTTPTrafficFilter> httpTrafficFilter = boost::make_shared<ExampleHTTPTrafficFilter>(); + +			testling->setHTTPTrafficFilter(httpTrafficFilter); +			connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); + +			// set a default response so the server response is answered by the traffic filter +			httpTrafficFilter->filterResponseReturn.clear(); +			httpTrafficFilter->filterResponseReturn.push_back(std::pair<std::string, std::string>("Authorization", "Negotiate a87421000492aa874209af8bc028")); + +			connectionFactory->connections[0]->dataWritten.clear(); + +			connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef( +				"HTTP/1.0 401 Unauthorized\r\n" +				"WWW-Authenticate: Negotiate\r\n" +				"\r\n")); +			eventLoop->processEvents(); + +			// verify that the traffic filter got called and answered with its response +			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), httpTrafficFilter->filterResponses.size()); +			CPPUNIT_ASSERT_EQUAL(std::string("WWW-Authenticate"), httpTrafficFilter->filterResponses[0][0].first); + +			// 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); + +			// verify that after without the default response, the traffic filter is skipped, authentication proceeds and traffic goes right through +			connectionFactory->connections[0]->dataWritten.clear(); +			testling->write(createSafeByteArray("abcdef")); +			CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), connectionFactory->connections[0]->dataWritten); +		} +  	private:  		HTTPConnectProxiedConnection::ref createTestling() {  			boost::shared_ptr<HTTPConnectProxiedConnection> c = HTTPConnectProxiedConnection::create(resolver, connectionFactory, timerFactory, proxyHost, proxyPort, "", ""); | 
 Swift
 Swift