diff options
| -rw-r--r-- | Swiften/Client/CoreClient.cpp | 39 | ||||
| -rw-r--r-- | Swiften/Network/BOSHConnection.cpp | 104 | ||||
| -rw-r--r-- | Swiften/Network/BOSHConnection.h | 58 | ||||
| -rw-r--r-- | Swiften/Network/BOSHConnectionPool.cpp | 58 | ||||
| -rw-r--r-- | Swiften/Network/BOSHConnectionPool.h | 18 | ||||
| -rw-r--r-- | Swiften/Network/BoostConnection.h | 9 | ||||
| -rw-r--r-- | Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp | 23 | ||||
| -rw-r--r-- | Swiften/Network/UnitTest/BOSHConnectionTest.cpp | 5 | ||||
| -rw-r--r-- | Swiften/QA/TLSTest/CertificateErrorTest.cpp | 3 | ||||
| -rw-r--r-- | Swiften/Session/BOSHSessionStream.cpp | 13 | ||||
| -rw-r--r-- | Swiften/Session/BOSHSessionStream.h | 1 | ||||
| -rw-r--r-- | Swiften/StreamStack/DummyStreamLayer.h | 30 | ||||
| -rw-r--r-- | Swiften/StreamStack/LowLayer.h | 3 | 
13 files changed, 286 insertions, 78 deletions
| diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index af64577..6fc491c 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -121,31 +121,36 @@ void CoreClient::connect(const ClientOptions& o) {  		connector_->start();  	}  	else {  		/* Autodiscovery of which proxy works is largely ok with a TCP session, because this is a one-off. With BOSH  		 * it would be quite painful given that potentially every stanza could be sent on a new connection.  		 */ -		//sessionStream_ = boost::make_shared<BOSHSessionStream>(boost::make_shared<BOSHConnectionFactory>(options.boshURL, networkFactories->getConnectionFactory(), networkFactories->getXMLParserFactory(), networkFactories->getTLSContextFactory()), getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword); -		sessionStream_ = boost::shared_ptr<BOSHSessionStream>(new BOSHSessionStream( -				options.boshURL, -				getPayloadParserFactories(), -				getPayloadSerializers(), -				networkFactories->getConnectionFactory(), -				networkFactories->getTLSContextFactory(), -				networkFactories->getTimerFactory(), -				networkFactories->getXMLParserFactory(), -				networkFactories->getEventLoop(), -				networkFactories->getDomainNameResolver(), -				host, -				options.boshHTTPConnectProxyURL, -				options.boshHTTPConnectProxyAuthID, -				options.boshHTTPConnectProxyAuthPassword, -				options.tlsOptions, -				options.httpTrafficFilter)); +		boost::shared_ptr<BOSHSessionStream> boshSessionStream_ = boost::shared_ptr<BOSHSessionStream>(new BOSHSessionStream( +			options.boshURL, +			getPayloadParserFactories(), +			getPayloadSerializers(), +			networkFactories->getConnectionFactory(), +			networkFactories->getTLSContextFactory(), +			networkFactories->getTimerFactory(), +			networkFactories->getXMLParserFactory(), +			networkFactories->getEventLoop(), +			networkFactories->getDomainNameResolver(), +			host, +			options.boshHTTPConnectProxyURL, +			options.boshHTTPConnectProxyAuthID, +			options.boshHTTPConnectProxyAuthPassword, +			options.tlsOptions, +			options.httpTrafficFilter)); +		sessionStream_ = boshSessionStream_;  		sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1));  		sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1)); +		if (certificate_ && !certificate_->isNull()) { +			SWIFT_LOG(debug) << "set certificate" << std::endl; +			sessionStream_->setTLSCertificate(certificate_); +		} +		boshSessionStream_->open();  		bindSessionToStream();  	}  }  void CoreClient::bindSessionToStream() { diff --git a/Swiften/Network/BOSHConnection.cpp b/Swiften/Network/BOSHConnection.cpp index 298b015..9a836cb 100644 --- a/Swiften/Network/BOSHConnection.cpp +++ b/Swiften/Network/BOSHConnection.cpp @@ -21,25 +21,35 @@  #include <Swiften/Base/ByteArray.h>  #include <Swiften/Base/Concat.h>  #include <Swiften/Base/Log.h>  #include <Swiften/Base/String.h>  #include <Swiften/Network/HostAddressPort.h>  #include <Swiften/Parser/BOSHBodyExtractor.h> +#include <Swiften/StreamStack/DummyStreamLayer.h> +#include <Swiften/StreamStack/TLSLayer.h> +#include <Swiften/TLS/TLSContext.h> +#include <Swiften/TLS/TLSOptions.h>  namespace Swift { -BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory) +BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory, TLSContextFactory* tlsContextFactory, const TLSOptions& tlsOptions)  	: boshURL_(boshURL),  	  connector_(connector),  	  parserFactory_(parserFactory),  	  sid_(),  	  waitingForStartResponse_(false),  		rid_(~0ULL),  	  pending_(false),  	  connectionReady_(false)  { +	if (boshURL_.getScheme() == "https") { +		tlsLayer_ = boost::make_shared<TLSLayer>(tlsContextFactory, tlsOptions); +		// The following dummyLayer_ is needed as the TLSLayer will pass the decrypted data to its parent layer. +		// The dummyLayer_ will serve as the parent layer. +		dummyLayer_ = boost::make_shared<DummyStreamLayer>(tlsLayer_.get()); +	}  }  BOSHConnection::~BOSHConnection() {  	cancelConnector();  	if (connection_) {  		connection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); @@ -58,12 +68,45 @@ void BOSHConnection::cancelConnector() {  		connector_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1));  		connector_->stop();  		connector_.reset();  	}  } +void BOSHConnection::handleTLSConnected() { +	SWIFT_LOG(debug) << std::endl; +	onConnectFinished(false); +} + +void BOSHConnection::handleTLSApplicationDataRead(const SafeByteArray& data) { +	SWIFT_LOG(debug) << std::endl; +	handleDataRead(boost::make_shared<SafeByteArray>(data)); +} + +void BOSHConnection::handleTLSNetowrkDataWriteRequest(const SafeByteArray& data) { +	SWIFT_LOG(debug) << std::endl; +	connection_->write(data); +} + +void BOSHConnection::handleRawDataRead(boost::shared_ptr<SafeByteArray> data) { +	SWIFT_LOG(debug) << std::endl; +	tlsLayer_->handleDataRead(*data.get()); +} + +void BOSHConnection::handleTLSError(boost::shared_ptr<TLSError> error) { + +} + +void BOSHConnection::writeData(const SafeByteArray& data) { +	if (tlsLayer_) { +		tlsLayer_->writeData(data); +	} +	else { +		connection_->write(data); +	} +} +  void BOSHConnection::disconnect() {  	if (connection_) {  		connection_->disconnect();  		sid_ = "";  	}  	else { @@ -73,12 +116,46 @@ void BOSHConnection::disconnect() {  }  void BOSHConnection::restartStream() {  	write(createSafeByteArray(""), true, false);  } +bool BOSHConnection::setClientCertificate(CertificateWithKey::ref cert) { +	if (tlsLayer_) { +		SWIFT_LOG(debug) << "set client certificate" << std::endl; +		return tlsLayer_->setClientCertificate(cert); +	} +	else { +		return false; +	} +} + +Certificate::ref BOSHConnection::getPeerCertificate() const { +	Certificate::ref peerCertificate; +	if (tlsLayer_) { +		peerCertificate = tlsLayer_->getPeerCertificate(); +	} +	return peerCertificate; +} + +std::vector<Certificate::ref> BOSHConnection::getPeerCertificateChain() const { +	std::vector<Certificate::ref> peerCertificateChain; +	if (tlsLayer_) { +		peerCertificateChain = tlsLayer_->getPeerCertificateChain(); +	} +	return peerCertificateChain; +} + +CertificateVerificationError::ref BOSHConnection::getPeerCertificateVerificationError() const { +	CertificateVerificationError::ref verificationError; +	if (tlsLayer_) { +		verificationError = tlsLayer_->getPeerCertificateVerificationError(); +	} +	return verificationError; +} +  void BOSHConnection::terminateStream() {  	write(createSafeByteArray(""), false, true);  }  void BOSHConnection::write(const SafeByteArray& data) { @@ -126,27 +203,42 @@ void BOSHConnection::write(const SafeByteArray& data, bool streamRestart, bool t  	assert(connectionReady_);  	assert(!sid_.empty());  	SafeByteArray safeHeader = createHTTPRequest(data, streamRestart, terminate, rid_, sid_, boshURL_).first;  	onBOSHDataWritten(safeHeader); -	connection_->write(safeHeader); +	writeData(safeHeader);  	pending_ = true;  	SWIFT_LOG(debug) << "write data: " << safeByteArrayToString(safeHeader) << std::endl;  }  void BOSHConnection::handleConnectFinished(Connection::ref connection) {  	cancelConnector();  	connectionReady_ = !!connection;  	if (connectionReady_) {  		connection_ = connection; -		connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); -		connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); +		if (tlsLayer_) { +			connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleRawDataRead, shared_from_this(), _1)); +			connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); + +			tlsLayer_->getContext()->onDataForNetwork.connect(boost::bind(&BOSHConnection::handleTLSNetowrkDataWriteRequest, shared_from_this(), _1)); +			tlsLayer_->getContext()->onDataForApplication.connect(boost::bind(&BOSHConnection::handleTLSApplicationDataRead, shared_from_this(), _1)); +			tlsLayer_->onConnected.connect(boost::bind(&BOSHConnection::handleTLSConnected, shared_from_this())); +			tlsLayer_->onError.connect(boost::bind(&BOSHConnection::handleTLSError, shared_from_this(), _1)); +			tlsLayer_->connect(); +		} +		else { +			connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); +			connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); +		} +	} + +	if (!connectionReady_ || !tlsLayer_) { +		onConnectFinished(!connectionReady_);  	} -	onConnectFinished(!connectionReady_);  }  void BOSHConnection::startStream(const std::string& to, unsigned long long rid) {  	assert(connectionReady_);  	// Session Creation Request  	std::stringstream content; @@ -177,13 +269,13 @@ void BOSHConnection::startStream(const std::string& to, unsigned long long rid)  			<< "Content-Length: " << contentString.size() << "\r\n\r\n"  			<< contentString;  	waitingForStartResponse_ = true;  	SafeByteArray safeHeader = createSafeByteArray(header.str());  	onBOSHDataWritten(safeHeader); -	connection_->write(safeHeader); +	writeData(safeHeader);  	SWIFT_LOG(debug) << "write stream header: " << safeByteArrayToString(safeHeader) << std::endl;  }  void BOSHConnection::handleDataRead(boost::shared_ptr<SafeByteArray> data) {  	onBOSHDataRead(*data);  	buffer_ = concat(buffer_, *data); diff --git a/Swiften/Network/BOSHConnection.h b/Swiften/Network/BOSHConnection.h index 65d179a..da43e71 100644 --- a/Swiften/Network/BOSHConnection.h +++ b/Swiften/Network/BOSHConnection.h @@ -2,30 +2,31 @@   * Copyright (c) 2011 Thilo Cestonaro   * Licensed under the simplified BSD license.   * See Documentation/Licenses/BSD-simplified.txt for more information.   */  /* - * 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/enable_shared_from_this.hpp>  #include <Swiften/Base/API.h> +#include <Swiften/Base/Error.h> +#include <Swiften/Base/String.h> +#include <Swiften/Base/URL.h>  #include <Swiften/Network/Connection.h>  #include <Swiften/Network/Connector.h>  #include <Swiften/Network/HostAddressPort.h> -#include <Swiften/Base/String.h> -#include <Swiften/Base/URL.h> -#include <Swiften/Base/Error.h>  #include <Swiften/Session/SessionStream.h> +#include <Swiften/TLS/TLSError.h>  namespace boost {  	class thread;  	namespace system {  		class error_code;  	} @@ -33,33 +34,37 @@ namespace boost {  class BOSHConnectionTest;  namespace Swift {  	class XMLParserFactory;  	class TLSContextFactory; +	class TLSLayer; +	struct TLSOptions; +	class HighLayer; -		class SWIFTEN_API BOSHError : public SessionStream::SessionStreamError { -				public: -					enum Type {BadRequest, HostGone, HostUnknown, ImproperAddressing,  -						  InternalServerError, ItemNotFound, OtherRequest, PolicyViolation,  -						  RemoteConnectionFailed, RemoteStreamError, SeeOtherURI, SystemShutdown, UndefinedCondition, -						  NoError}; -					BOSHError(Type type) : SessionStream::SessionStreamError(SessionStream::SessionStreamError::ConnectionReadError), type(type) {} -					Type getType() {return type;} -					typedef boost::shared_ptr<BOSHError> ref; -				private: -					Type type; -					 -			}; +	class SWIFTEN_API BOSHError : public SessionStream::SessionStreamError { +		public: +			enum Type { +				BadRequest, HostGone, HostUnknown, ImproperAddressing, +				InternalServerError, ItemNotFound, OtherRequest, PolicyViolation, +				RemoteConnectionFailed, RemoteStreamError, SeeOtherURI, SystemShutdown, UndefinedCondition, +				NoError}; +			BOSHError(Type type) : SessionStream::SessionStreamError(SessionStream::SessionStreamError::ConnectionReadError), type(type) {} +			Type getType() {return type;} +			typedef boost::shared_ptr<BOSHError> ref; + +		private: +			Type type; +	};  	class SWIFTEN_API BOSHConnection : public boost::enable_shared_from_this<BOSHConnection> {  		public:  			typedef boost::shared_ptr<BOSHConnection> ref; -			static ref create(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory) { -				return ref(new BOSHConnection(boshURL, connector, parserFactory)); +			static ref create(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory, TLSContextFactory* tlsContextFactory, const TLSOptions& tlsOptions) { +				return ref(new BOSHConnection(boshURL, connector, parserFactory, tlsContextFactory, tlsOptions));  			}  			virtual ~BOSHConnection();  			virtual void connect();  			virtual void disconnect();  			virtual void write(const SafeByteArray& data); @@ -68,12 +73,16 @@ namespace Swift {  			void setSID(const std::string& sid);  			void startStream(const std::string& to, unsigned long long rid);  			void terminateStream();  			bool isReadyToSend();  			void restartStream(); +			bool setClientCertificate(CertificateWithKey::ref cert); +			Certificate::ref getPeerCertificate() const; +			std::vector<Certificate::ref> getPeerCertificateChain() const; +			CertificateVerificationError::ref getPeerCertificateVerificationError() const;  			boost::signal<void (bool /* error */)> onConnectFinished;  			boost::signal<void (bool /* error */)> onDisconnected;  			boost::signal<void (BOSHError::ref)> onSessionTerminated;  			boost::signal<void (const std::string& /*sid*/, size_t /*requests*/)> onSessionStarted;  			boost::signal<void (const SafeByteArray&)> onXMPPDataRead; @@ -81,26 +90,35 @@ namespace Swift {  			boost::signal<void (const SafeByteArray&)> onBOSHDataWritten;  			boost::signal<void (const std::string&)> onHTTPError;  		private:  			friend class ::BOSHConnectionTest; -			BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory); +			BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory, TLSContextFactory* tlsContextFactory, const TLSOptions& tlsOptions);  			static std::pair<SafeByteArray, size_t> createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, unsigned long long rid, const std::string& sid, const URL& boshURL);  			void handleConnectFinished(Connection::ref);  			void handleDataRead(boost::shared_ptr<SafeByteArray> data);  			void handleDisconnected(const boost::optional<Connection::Error>& error);  			void write(const SafeByteArray& data, bool streamRestart, bool terminate); /* FIXME: refactor */  			BOSHError::Type parseTerminationCondition(const std::string& text);  			void cancelConnector(); +			void handleTLSConnected(); +			void handleTLSApplicationDataRead(const SafeByteArray& data); +			void handleTLSNetowrkDataWriteRequest(const SafeByteArray& data); +			void handleRawDataRead(boost::shared_ptr<SafeByteArray> data); +			void handleTLSError(boost::shared_ptr<TLSError> error); +			void writeData(const SafeByteArray& data); +  			URL boshURL_;  			Connector::ref connector_;  			XMLParserFactory* parserFactory_;  			boost::shared_ptr<Connection> connection_; +			boost::shared_ptr<HighLayer> dummyLayer_; +			boost::shared_ptr<TLSLayer> tlsLayer_;  			std::string sid_;  			bool waitingForStartResponse_;  			unsigned long long rid_;  			SafeByteArray buffer_;  			bool pending_;  			bool connectionReady_; diff --git a/Swiften/Network/BOSHConnectionPool.cpp b/Swiften/Network/BOSHConnectionPool.cpp index c23e2de..57c1bcc 100644 --- a/Swiften/Network/BOSHConnectionPool.cpp +++ b/Swiften/Network/BOSHConnectionPool.cpp @@ -7,44 +7,37 @@  #include <climits>  #include <boost/bind.hpp>  #include <boost/lexical_cast.hpp> +#include <Swiften/Base/Log.h>  #include <Swiften/Base/SafeString.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, boost::shared_ptr<HTTPTrafficFilter> trafficFilter) :  		boshURL(boshURL),  		connectionFactory(connectionFactoryParameter),  		xmlParserFactory(parserFactory),  		timerFactory(timerFactory),  		rid(initialRID),  		pendingTerminate(false),  		to(to),  		requestLimit(2),  		restartCount(0), -		pendingRestart(false) { +		pendingRestart(false), +		tlsContextFactory_(tlsFactory), +		tlsOptions_(tlsOptions) {  	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, trafficFilter);  	} -	if (boshURL.getScheme() == "https") { -		connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory, tlsOptions); -		myConnectionFactories.push_back(connectionFactory); -	}  	resolver = new CachingDomainNameResolver(realResolver, eventLoop); -	createConnection();  }  BOSHConnectionPool::~BOSHConnectionPool() {  	/* Don't do a normal close here. Instead kill things forcibly, as close() or writeFooter() will already have been called */  	std::vector<BOSHConnection::ref> connectionCopies = connections;  	foreach (BOSHConnection::ref connection, connectionCopies) { @@ -80,17 +73,45 @@ void BOSHConnectionPool::restartStream() {  	}  	else {  		pendingRestart = true;  	}  } +void BOSHConnectionPool::setTLSCertificate(CertificateWithKey::ref certWithKey) { +	clientCertificate = certWithKey; +} + +bool BOSHConnectionPool::isTLSEncrypted() const { +	return !pinnedCertificateChain_.empty(); +} + +Certificate::ref BOSHConnectionPool::getPeerCertificate() const { +	Certificate::ref peerCertificate; +	if (!pinnedCertificateChain_.empty()) { +		peerCertificate = pinnedCertificateChain_[0]; +	} +	return peerCertificate; +} + +std::vector<Certificate::ref> BOSHConnectionPool::getPeerCertificateChain() const { +	return pinnedCertificateChain_; +} + +boost::shared_ptr<CertificateVerificationError> BOSHConnectionPool::getPeerCertificateVerificationError() const { +	return lastVerificationError_; +} +  void BOSHConnectionPool::writeFooter() {  	pendingTerminate = true;  	tryToSendQueuedData();  } +void BOSHConnectionPool::open() { +	createConnection(); +} +  void BOSHConnectionPool::close() {  	if (!sid.empty()) {  		writeFooter();  	}  	else {  		pendingTerminate = true; @@ -114,12 +135,19 @@ void BOSHConnectionPool::handleConnectFinished(bool error, BOSHConnection::ref c  		onSessionTerminated(boost::make_shared<BOSHError>(BOSHError::UndefinedCondition));  		/*TODO: We can probably manage to not terminate the stream here and use the rid/ack retry  		 * logic to just swallow the error and try again (some number of tries).  		 */  	}  	else { +		if (connection->getPeerCertificate() && pinnedCertificateChain_.empty()) { +			pinnedCertificateChain_ = connection->getPeerCertificateChain(); +		} +		if (!pinnedCertificateChain_.empty()) { +			lastVerificationError_ = connection->getPeerCertificateVerificationError(); +		} +  		if (sid.empty()) {  			connection->startStream(to, rid);  		}  		if (pendingRestart) {  			restartStream();  		} @@ -223,21 +251,27 @@ void BOSHConnectionPool::handleConnectionDisconnected(bool/* error*/, BOSHConnec  		tryToSendQueuedData();  	}  }  boost::shared_ptr<BOSHConnection> BOSHConnectionPool::createConnection() {  	Connector::ref connector = Connector::create(boshURL.getHost(), URL::getPortOrDefaultPort(boshURL), boost::optional<std::string>(), resolver, connectionFactory, timerFactory); -	BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory); +	BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory, tlsContextFactory_, tlsOptions_);  	connection->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPool::handleDataRead, this, _1));  	connection->onSessionStarted.connect(boost::bind(&BOSHConnectionPool::handleSessionStarted, this, _1, _2));  	connection->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataRead, this, _1));  	connection->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataWritten, this, _1));  	connection->onDisconnected.connect(boost::bind(&BOSHConnectionPool::handleConnectionDisconnected, this, _1, connection));  	connection->onConnectFinished.connect(boost::bind(&BOSHConnectionPool::handleConnectFinished, this, _1, connection));  	connection->onSessionTerminated.connect(boost::bind(&BOSHConnectionPool::handleSessionTerminated, this, _1));  	connection->onHTTPError.connect(boost::bind(&BOSHConnectionPool::handleHTTPError, this, _1)); + +	if (boshURL.getScheme() == "https") { +		bool success = connection->setClientCertificate(clientCertificate); +		SWIFT_LOG(debug) << "setClientCertificate, success: " << success << std::endl; +	} +  	connection->connect();  	connections.push_back(connection);  	return connection;  }  void BOSHConnectionPool::destroyConnection(boost::shared_ptr<BOSHConnection> connection) { diff --git a/Swiften/Network/BOSHConnectionPool.h b/Swiften/Network/BOSHConnectionPool.h index eaef56c..df116ff 100644 --- a/Swiften/Network/BOSHConnectionPool.h +++ b/Swiften/Network/BOSHConnectionPool.h @@ -9,30 +9,41 @@  #include <vector>  #include <Swiften/Base/API.h>  #include <Swiften/Base/SafeString.h>  #include <Swiften/Network/BOSHConnection.h> +#include <Swiften/TLS/CertificateWithKey.h>  #include <Swiften/TLS/TLSOptions.h>  namespace Swift {  	class CachingDomainNameResolver;  	class EventLoop;  	class HTTPConnectProxiedConnectionFactory;  	class HTTPTrafficFilter; -	class TLSConnectionFactory; +	class TLSContextFactory; +	class CachingDomainNameResolver; +	class EventLoop;  	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, boost::shared_ptr<HTTPTrafficFilter> trafficFilter = boost::shared_ptr<HTTPTrafficFilter>());  			~BOSHConnectionPool(); + +			void open();  			void write(const SafeByteArray& data);  			void writeFooter();  			void close();  			void restartStream(); +			void setTLSCertificate(CertificateWithKey::ref certWithKey); +			bool isTLSEncrypted() const; +			Certificate::ref getPeerCertificate() const; +			std::vector<Certificate::ref> getPeerCertificateChain() const; +			boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; +  			boost::signal<void (BOSHError::ref)> onSessionTerminated;  			boost::signal<void ()> onSessionStarted;  			boost::signal<void (const SafeByteArray&)> onXMPPDataRead;  			boost::signal<void (const SafeByteArray&)> onBOSHDataRead;  			boost::signal<void (const SafeByteArray&)> onBOSHDataWritten; @@ -65,8 +76,13 @@ namespace Swift {  			std::string to;  			size_t requestLimit;  			int restartCount;  			bool pendingRestart;  			std::vector<ConnectionFactory*> myConnectionFactories;  			CachingDomainNameResolver* resolver; +			CertificateWithKey::ref clientCertificate; +			TLSContextFactory* tlsContextFactory_; +			TLSOptions tlsOptions_; +			std::vector<boost::shared_ptr<Certificate> > pinnedCertificateChain_; +			boost::shared_ptr<CertificateVerificationError> lastVerificationError_;  	};  } diff --git a/Swiften/Network/BoostConnection.h b/Swiften/Network/BoostConnection.h index 83a8f01..8e89263 100644 --- a/Swiften/Network/BoostConnection.h +++ b/Swiften/Network/BoostConnection.h @@ -12,12 +12,15 @@  #include <boost/thread/mutex.hpp>  #include <Swiften/Base/API.h>  #include <Swiften/Base/SafeByteArray.h>  #include <Swiften/EventLoop/EventOwner.h>  #include <Swiften/Network/Connection.h> +#include <Swiften/TLS/Certificate.h> +#include <Swiften/TLS/CertificateVerificationError.h> +#include <Swiften/TLS/CertificateWithKey.h>  namespace boost {  	class thread;  	namespace system {  		class error_code;  	} @@ -44,12 +47,18 @@ namespace Swift {  			boost::asio::ip::tcp::socket& getSocket() {  				return socket_;  			}  			HostAddressPort getLocalAddress() const; +			bool setClientCertificate(CertificateWithKey::ref cert); + +			Certificate::ref getPeerCertificate() const; +			std::vector<Certificate::ref> getPeerCertificateChain() const; +			boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const; +  		private:  			BoostConnection(boost::shared_ptr<boost::asio::io_service> ioService, EventLoop* eventLoop);  			void handleConnectFinished(const boost::system::error_code& error);  			void handleSocketRead(const boost::system::error_code& error, size_t bytesTransferred);  			void handleDataWritten(const boost::system::error_code& error); diff --git a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp index e687517..83ad548 100644 --- a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp +++ b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp @@ -1,37 +1,35 @@  /* - * 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/BOSHConnectionPool.h>  #include <Swiften/Network/Connection.h>  #include <Swiften/Network/ConnectionFactory.h>  #include <Swiften/Network/DummyTimerFactory.h>  #include <Swiften/Network/HostAddressPort.h>  #include <Swiften/Network/StaticDomainNameResolver.h>  #include <Swiften/Parser/PlatformXMLParserFactory.h>  #include <Swiften/TLS/TLSOptions.h> - -  using namespace Swift;  typedef boost::shared_ptr<BOSHConnectionPool> PoolRef;  class BOSHConnectionPoolTest : public CppUnit::TestFixture { @@ -320,16 +318,15 @@ class BOSHConnectionPoolTest : public CppUnit::TestFixture {  		}  	private:  		PoolRef createTestling() { -			BOSHConnectionPool* a = new BOSHConnectionPool(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString(""), TLSOptions()); -			PoolRef pool(a); -			//FIXME: Remko - why does the above work, but the below fail? -			//PoolRef pool = boost::make_shared<BOSHConnectionPool>(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString("")); +			// make_shared is limited to 9 arguments; instead new is used here. +			PoolRef pool = PoolRef(new BOSHConnectionPool(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString(""), TLSOptions())); +			pool->open();  			pool->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleXMPPDataRead, this, _1));  			pool->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataRead, this, _1));  			pool->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataWritten, this, _1));  			pool->onSessionStarted.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionStarted, this));  			pool->onSessionTerminated.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionTerminated, this));  			eventLoop->processEvents(); diff --git a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp index c8bf1f0..349c282 100644 --- a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp +++ b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp @@ -21,12 +21,13 @@  #include <Swiften/Network/Connection.h>  #include <Swiften/Network/ConnectionFactory.h>  #include <Swiften/Network/DummyTimerFactory.h>  #include <Swiften/Network/HostAddressPort.h>  #include <Swiften/Network/StaticDomainNameResolver.h>  #include <Swiften/Parser/PlatformXMLParserFactory.h> +#include <Swiften/TLS/TLSOptions.h>  using namespace Swift;  class BOSHConnectionTest : public CppUnit::TestFixture {  	CPPUNIT_TEST_SUITE(BOSHConnectionTest);  	CPPUNIT_TEST(testHeader); @@ -44,12 +45,13 @@ class BOSHConnectionTest : public CppUnit::TestFixture {  	public:  		void setUp() {  			eventLoop = new DummyEventLoop();  			connectionFactory = new MockConnectionFactory(eventLoop);  			resolver = new StaticDomainNameResolver(eventLoop);  			timerFactory = new DummyTimerFactory(); +			tlsContextFactory = NULL;  			connectFinished = false;  			disconnected = false;  			disconnectedError = false;  			dataRead.clear();  		} @@ -190,13 +192,13 @@ class BOSHConnectionTest : public CppUnit::TestFixture {  	private:  		BOSHConnection::ref createTestling() {  			resolver->addAddress("wonderland.lit", HostAddress("127.0.0.1"));  			Connector::ref connector = Connector::create("wonderland.lit", 5280, boost::optional<std::string>(), resolver, connectionFactory, timerFactory); -			BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "/http-bind"), connector, &parserFactory); +			BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "/http-bind"), connector, &parserFactory, tlsContextFactory, TLSOptions());  			c->onConnectFinished.connect(boost::bind(&BOSHConnectionTest::handleConnectFinished, this, _1));  			c->onDisconnected.connect(boost::bind(&BOSHConnectionTest::handleDisconnected, this, _1));  			c->onXMPPDataRead.connect(boost::bind(&BOSHConnectionTest::handleDataRead, this, _1));  			c->onSessionStarted.connect(boost::bind(&BOSHConnectionTest::handleSID, this, _1));  			c->setRID(42);  			return c; @@ -291,12 +293,13 @@ class BOSHConnectionTest : public CppUnit::TestFixture {  		bool disconnected;  		bool disconnectedError;  		ByteArray dataRead;  		PlatformXMLParserFactory parserFactory;  		StaticDomainNameResolver* resolver;  		TimerFactory* timerFactory; +		TLSContextFactory* tlsContextFactory;  		std::string sid;	  };  CPPUNIT_TEST_SUITE_REGISTRATION(BOSHConnectionTest); diff --git a/Swiften/QA/TLSTest/CertificateErrorTest.cpp b/Swiften/QA/TLSTest/CertificateErrorTest.cpp index d7c2c55..e69af0b 100644 --- a/Swiften/QA/TLSTest/CertificateErrorTest.cpp +++ b/Swiften/QA/TLSTest/CertificateErrorTest.cpp @@ -45,13 +45,12 @@ class CertificateErrorTest : public CppUnit::TestFixture {  #endif  		CPPUNIT_TEST_SUITE_END();  	public:  		void setUp() { -			SWIFT_LOG(debug) << std::endl << std::endl;  			eventLoop_ = new DummyEventLoop();  			boostIOServiceThread_ = new BoostIOServiceThread();  			boostIOService_ = boost::make_shared<boost::asio::io_service>();  			connectionFactory_ = new BoostConnectionFactory(boostIOServiceThread_->getIOService(), eventLoop_);  			idnConverter_ = PlatformIDNConverter::create();  			domainNameResolver_ = new PlatformDomainNameResolver(idnConverter_, eventLoop_), @@ -93,13 +92,12 @@ class CertificateErrorTest : public CppUnit::TestFixture {  			}  			return lastResoverResult_;  		}  		void connectToServer(boost::shared_ptr<TLSConnection> connection, const std::string& hostname, int port) { -			Log::setLogLevel(Log::debug);  			connection->onConnectFinished.connect(boost::bind(&CertificateErrorTest::handleConnectFinished, this, _1));  			HostAddress address = resolveName(hostname);  			connection->connect(HostAddressPort(address, port)); @@ -138,13 +136,12 @@ class CertificateErrorTest : public CppUnit::TestFixture {  			CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_);  			CPPUNIT_ASSERT(context->getPeerCertificateVerificationError());  			CPPUNIT_ASSERT_EQUAL(CertificateVerificationError::Expired, context->getPeerCertificateVerificationError()->getType());  		}  		void testTLS_O_MaticCertificateFromUnknownCA() { -			Log::setLogLevel(Log::debug);  			boost::shared_ptr<TLSConnection> connection = boost::dynamic_pointer_cast<TLSConnection>(tlsConnectionFactory_->createConnection());  			TLSContext* context = connection->getTLSContext();  			connectToServer(connection, "test7.tls-o-matic.com", 407);  			CPPUNIT_ASSERT_EQUAL(true, connectFinishedWithError_); diff --git a/Swiften/Session/BOSHSessionStream.cpp b/Swiften/Session/BOSHSessionStream.cpp index 99af71d..7fb8b31 100644 --- a/Swiften/Session/BOSHSessionStream.cpp +++ b/Swiften/Session/BOSHSessionStream.cpp @@ -80,12 +80,17 @@ BOSHSessionStream::~BOSHSessionStream() {  	xmppLayer->onError.disconnect(boost::bind(&BOSHSessionStream::handleXMPPError, this));  	xmppLayer->onWriteData.disconnect(boost::bind(&BOSHSessionStream::handleXMPPLayerDataWritten, this, _1));  	delete xmppLayer;  	xmppLayer = NULL;  } +void BOSHSessionStream::open() { +	connectionPool->setTLSCertificate(getTLSCertificate()); +	connectionPool->open(); +} +  void BOSHSessionStream::handlePoolXMPPDataRead(const SafeByteArray& data) {  	xmppLayer->handleDataRead(data);  }  void BOSHSessionStream::writeElement(boost::shared_ptr<ToplevelElement> element) {  	assert(available); @@ -115,25 +120,25 @@ bool BOSHSessionStream::supportsTLSEncryption() {  void BOSHSessionStream::addTLSEncryption() {  	assert(available);  }  bool BOSHSessionStream::isTLSEncrypted() { -	return false; +	return connectionPool->isTLSEncrypted();  }  Certificate::ref BOSHSessionStream::getPeerCertificate() const { -	return Certificate::ref(); +	return connectionPool->getPeerCertificate();  }  std::vector<Certificate::ref> BOSHSessionStream::getPeerCertificateChain() const { -	return std::vector<Certificate::ref>(); +	return connectionPool->getPeerCertificateChain();  }  boost::shared_ptr<CertificateVerificationError> BOSHSessionStream::getPeerCertificateVerificationError() const { -	return boost::shared_ptr<CertificateVerificationError>(); +	return connectionPool->getPeerCertificateVerificationError();  }  ByteArray BOSHSessionStream::getTLSFinishMessage() const {  	return ByteArray();  } diff --git a/Swiften/Session/BOSHSessionStream.h b/Swiften/Session/BOSHSessionStream.h index 191a6f4..66e41ea 100644 --- a/Swiften/Session/BOSHSessionStream.h +++ b/Swiften/Session/BOSHSessionStream.h @@ -48,12 +48,13 @@ namespace Swift {  					const SafeString& boshHTTPConnectProxyAuthPassword,  					const TLSOptions& tlsOptions,  					boost::shared_ptr<HTTPTrafficFilter> trafficFilter  			);  			virtual ~BOSHSessionStream(); +			void open();  			virtual void close();  			virtual bool isOpen();  			virtual void writeHeader(const ProtocolHeader& header);  			virtual void writeElement(boost::shared_ptr<ToplevelElement>);  			virtual void writeFooter(); diff --git a/Swiften/StreamStack/DummyStreamLayer.h b/Swiften/StreamStack/DummyStreamLayer.h new file mode 100644 index 0000000..b06c8aa --- /dev/null +++ b/Swiften/StreamStack/DummyStreamLayer.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/API.h> +#include <Swiften/StreamStack/HighLayer.h> +#include <Swiften/StreamStack/LowLayer.h> + +namespace Swift { +	/** +	 *  The \ref DummyStreamLayer can be used to use a \ref LowLayer on its own, without a functioning parent layer. +	 *  The \ref DummyStreamLayer will serve as the parent layer to the \ref LowLayer and is called when the \ref LowLayer +	 *  wants to write data to its parent layer. +	 */ +	class SWIFTEN_API DummyStreamLayer : public HighLayer { +		public: +			DummyStreamLayer(LowLayer* lowLayer) { +				setChildLayer(lowLayer); +				lowLayer->setParentLayer(this); +			} + +			virtual void handleDataRead(const SafeByteArray& /* data */) { + +			} +	}; +} diff --git a/Swiften/StreamStack/LowLayer.h b/Swiften/StreamStack/LowLayer.h index 7d7e0b5..b1bddec 100644 --- a/Swiften/StreamStack/LowLayer.h +++ b/Swiften/StreamStack/LowLayer.h @@ -1,8 +1,8 @@  /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited.   * All rights reserved.   * See the COPYING file for more information.   */  #pragma once @@ -11,12 +11,13 @@  namespace Swift {  	class HighLayer;  	class SWIFTEN_API LowLayer {  			friend class StreamStack; +			friend class DummyStreamLayer;  		public:  			LowLayer();  			virtual ~LowLayer();  			virtual void writeData(const SafeByteArray& data) = 0; | 
 Swift
 Swift