summaryrefslogtreecommitdiffstats
path: root/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp')
-rw-r--r--tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp2097
1 files changed, 2097 insertions, 0 deletions
diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
new file mode 100644
index 0000000000..6b7720308d
--- /dev/null
+++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
@@ -0,0 +1,2097 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtCore/qthread.h>
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qhostinfo.h>
+#include <QtNetwork/qnetworkproxy.h>
+#include <QtNetwork/qsslcipher.h>
+#include <QtNetwork/qsslconfiguration.h>
+#include <QtNetwork/qsslkey.h>
+#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtTest/QtTest>
+
+#include <QNetworkProxy>
+#include <QAuthenticator>
+
+#include "private/qhostinfo_p.h"
+#ifndef QT_NO_OPENSSL
+#include "private/qsslsocket_openssl_p.h"
+#include "private/qsslsocket_openssl_symbols_p.h"
+#endif
+
+#include "../../../network-settings.h"
+
+Q_DECLARE_METATYPE(QAbstractSocket::SocketState)
+Q_DECLARE_METATYPE(QAbstractSocket::SocketError)
+#ifndef QT_NO_OPENSSL
+Q_DECLARE_METATYPE(QSslSocket::SslMode)
+typedef QList<QSslError::SslError> SslErrorList;
+Q_DECLARE_METATYPE(SslErrorList)
+Q_DECLARE_METATYPE(QSslError)
+Q_DECLARE_METATYPE(QSsl::SslProtocol)
+Q_DECLARE_METATYPE(QSslConfiguration)
+#endif
+
+#if defined Q_OS_HPUX && defined Q_CC_GNU
+// This error is delivered every time we try to use the fluke CA
+// certificate. For now we work around this bug. Task 202317.
+#define QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+#endif
+
+#ifdef Q_OS_SYMBIAN
+#define SRCDIR ""
+#endif
+
+#ifndef QT_NO_OPENSSL
+class QSslSocketPtr: public QSharedPointer<QSslSocket>
+{
+public:
+ inline QSslSocketPtr(QSslSocket *ptr = 0)
+ : QSharedPointer<QSslSocket>(ptr)
+ { }
+
+ inline operator QSslSocket *() const { return data(); }
+};
+#endif
+
+class tst_QSslSocket : public QObject
+{
+ Q_OBJECT
+
+ int proxyAuthCalled;
+
+public:
+ tst_QSslSocket();
+ virtual ~tst_QSslSocket();
+
+ static void enterLoop(int secs)
+ {
+ ++loopLevel;
+ QTestEventLoop::instance().enterLoop(secs);
+ }
+
+ static bool timeout()
+ {
+ return QTestEventLoop::instance().timeout();
+ }
+
+#ifndef QT_NO_OPENSSL
+ QSslSocketPtr newSocket();
+#endif
+
+public slots:
+ void initTestCase_data();
+ void init();
+ void cleanup();
+ void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth);
+
+#ifndef QT_NO_OPENSSL
+private slots:
+ void constructing();
+ void simpleConnect();
+ void simpleConnectWithIgnore();
+
+ // API tests
+ void sslErrors_data();
+ void sslErrors();
+ void addCaCertificate();
+ void addCaCertificates();
+ void addCaCertificates2();
+ void ciphers();
+ void connectToHostEncrypted();
+ void connectToHostEncryptedWithVerificationPeerName();
+ void sessionCipher();
+ void flush();
+ void isEncrypted();
+ void localCertificate();
+ void mode();
+ void peerCertificate();
+ void peerCertificateChain();
+ void privateKey();
+ void privateKeyOpaque();
+ void protocol();
+ void protocolServerSide_data();
+ void protocolServerSide();
+ void setCaCertificates();
+ void setLocalCertificate();
+ void setPrivateKey();
+ void setSocketDescriptor();
+ void setSslConfiguration_data();
+ void setSslConfiguration();
+ void waitForEncrypted();
+ void waitForEncryptedMinusOne();
+ void waitForConnectedEncryptedReadyRead();
+ void startClientEncryption();
+ void startServerEncryption();
+ void addDefaultCaCertificate();
+ void addDefaultCaCertificates();
+ void addDefaultCaCertificates2();
+ void defaultCaCertificates();
+ void defaultCiphers();
+ void resetDefaultCiphers();
+ void setDefaultCaCertificates();
+ void setDefaultCiphers();
+ void supportedCiphers();
+ void systemCaCertificates();
+ void wildcardCertificateNames();
+ void wildcard();
+ void setEmptyKey();
+ void spontaneousWrite();
+ void setReadBufferSize();
+ void setReadBufferSize_task_250027();
+ void waitForMinusOne();
+ void verifyMode();
+ void verifyDepth();
+ void peerVerifyError();
+ void disconnectFromHostWhenConnecting();
+ void disconnectFromHostWhenConnected();
+ void resetProxy();
+ void ignoreSslErrorsList_data();
+ void ignoreSslErrorsList();
+ void ignoreSslErrorsListWithSlot_data();
+ void ignoreSslErrorsListWithSlot();
+ void readFromClosedSocket();
+ void writeBigChunk();
+ void blacklistedCertificates();
+ void setEmptyDefaultConfiguration();
+ void versionAccessors();
+
+ static void exitLoop()
+ {
+ // Safe exit - if we aren't in an event loop, don't
+ // exit one.
+ if (loopLevel > 0) {
+ --loopLevel;
+ QTestEventLoop::instance().exitLoop();
+ }
+ }
+
+protected slots:
+ void ignoreErrorSlot()
+ {
+ socket->ignoreSslErrors();
+ }
+ void untrustedWorkaroundSlot(const QList<QSslError> &errors)
+ {
+ if (errors.size() == 1 &&
+ (errors.first().error() == QSslError::CertificateUntrusted ||
+ errors.first().error() == QSslError::SelfSignedCertificate))
+ socket->ignoreSslErrors();
+ }
+ void ignoreErrorListSlot(const QList<QSslError> &errors);
+
+private:
+ QSslSocket *socket;
+ QList<QSslError> storedExpectedSslErrors;
+#endif // QT_NO_OPENSSL
+private:
+ static int loopLevel;
+};
+
+int tst_QSslSocket::loopLevel = 0;
+
+tst_QSslSocket::tst_QSslSocket()
+{
+#ifndef QT_NO_OPENSSL
+ qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
+ qRegisterMetaType<QSslError>("QSslError");
+ qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
+ qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
+ qRegisterMetaType<QAbstractSocket::SocketState>("QSslSocket::SslMode");
+#endif
+ Q_SET_DEFAULT_IAP
+}
+
+tst_QSslSocket::~tst_QSslSocket()
+{
+}
+
+enum ProxyTests {
+ NoProxy = 0x00,
+ Socks5Proxy = 0x01,
+ HttpProxy = 0x02,
+ TypeMask = 0x0f,
+
+ NoAuth = 0x00,
+ AuthBasic = 0x10,
+ AuthNtlm = 0x20,
+ AuthMask = 0xf0
+};
+
+void tst_QSslSocket::initTestCase_data()
+{
+ QTest::addColumn<bool>("setProxy");
+ QTest::addColumn<int>("proxyType");
+
+ QTest::newRow("WithoutProxy") << false << 0;
+ QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy);
+ QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic);
+
+ QTest::newRow("WithHttpProxy") << true << int(HttpProxy);
+ QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic);
+ // uncomment the line below when NTLM works
+// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm);
+}
+
+void tst_QSslSocket::init()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy) {
+ QFETCH_GLOBAL(int, proxyType);
+ QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString();
+ QNetworkProxy proxy;
+
+ switch (proxyType) {
+ case Socks5Proxy:
+ proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1080);
+ break;
+
+ case Socks5Proxy | AuthBasic:
+ proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1081);
+ break;
+
+ case HttpProxy | NoAuth:
+ proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3128);
+ break;
+
+ case HttpProxy | AuthBasic:
+ proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3129);
+ break;
+
+ case HttpProxy | AuthNtlm:
+ proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3130);
+ break;
+ }
+ QNetworkProxy::setApplicationProxy(proxy);
+ }
+
+ qt_qhostinfo_clear_cache();
+}
+
+void tst_QSslSocket::cleanup()
+{
+ QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
+}
+
+#ifndef QT_NO_OPENSSL
+QSslSocketPtr tst_QSslSocket::newSocket()
+{
+ QSslSocket *socket = new QSslSocket;
+
+ proxyAuthCalled = 0;
+ connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ Qt::DirectConnection);
+
+ return QSslSocketPtr(socket);
+}
+#endif
+
+void tst_QSslSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth)
+{
+ ++proxyAuthCalled;
+ auth->setUser("qsockstest");
+ auth->setPassword("password");
+}
+
+#ifndef QT_NO_OPENSSL
+
+void tst_QSslSocket::constructing()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocket socket;
+
+ QCOMPARE(socket.state(), QSslSocket::UnconnectedState);
+ QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(socket.bytesAvailable(), qint64(0));
+ QCOMPARE(socket.bytesToWrite(), qint64(0));
+ QVERIFY(!socket.canReadLine());
+ QVERIFY(socket.atEnd());
+ QCOMPARE(socket.localCertificate(), QSslCertificate());
+ QCOMPARE(socket.sslConfiguration(), QSslConfiguration::defaultConfiguration());
+ QCOMPARE(socket.errorString(), QString("Unknown error"));
+ char c = '\0';
+ QVERIFY(!socket.getChar(&c));
+ QCOMPARE(c, '\0');
+ QVERIFY(!socket.isOpen());
+ QVERIFY(!socket.isReadable());
+ QVERIFY(socket.isSequential());
+ QVERIFY(!socket.isTextModeEnabled());
+ QVERIFY(!socket.isWritable());
+ QCOMPARE(socket.openMode(), QIODevice::NotOpen);
+ QVERIFY(socket.peek(2).isEmpty());
+ QCOMPARE(socket.pos(), qint64(0));
+ QVERIFY(!socket.putChar('c'));
+ QVERIFY(socket.read(2).isEmpty());
+ QCOMPARE(socket.read(0, 0), qint64(-1));
+ QVERIFY(socket.readAll().isEmpty());
+ QTest::ignoreMessage(QtWarningMsg, "QIODevice::readLine: Called with maxSize < 2");
+ QCOMPARE(socket.readLine(0, 0), qint64(-1));
+ char buf[10];
+ QCOMPARE(socket.readLine(buf, sizeof(buf)), qint64(-1));
+ QTest::ignoreMessage(QtWarningMsg, "QIODevice::seek: The device is not open");
+ QVERIFY(!socket.reset());
+ QTest::ignoreMessage(QtWarningMsg, "QIODevice::seek: The device is not open");
+ QVERIFY(!socket.seek(2));
+ QCOMPARE(socket.size(), qint64(0));
+ QVERIFY(!socket.waitForBytesWritten(10));
+ QVERIFY(!socket.waitForReadyRead(10));
+ QCOMPARE(socket.write(0, 0), qint64(-1));
+ QCOMPARE(socket.write(QByteArray()), qint64(-1));
+ QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
+ QVERIFY(!socket.flush());
+ QVERIFY(!socket.isValid());
+ QCOMPARE(socket.localAddress(), QHostAddress());
+ QCOMPARE(socket.localPort(), quint16(0));
+ QCOMPARE(socket.peerAddress(), QHostAddress());
+ QVERIFY(socket.peerName().isEmpty());
+ QCOMPARE(socket.peerPort(), quint16(0));
+ QCOMPARE(socket.proxy().type(), QNetworkProxy::DefaultProxy);
+ QCOMPARE(socket.readBufferSize(), qint64(0));
+ QCOMPARE(socket.socketDescriptor(), -1);
+ QCOMPARE(socket.socketType(), QAbstractSocket::TcpSocket);
+ QVERIFY(!socket.waitForConnected(10));
+ QTest::ignoreMessage(QtWarningMsg, "QSslSocket::waitForDisconnected() is not allowed in UnconnectedState");
+ QVERIFY(!socket.waitForDisconnected(10));
+ QCOMPARE(socket.protocol(), QSsl::SecureProtocols);
+
+ QSslConfiguration savedDefault = QSslConfiguration::defaultConfiguration();
+
+ // verify that changing the default config doesn't affect this socket
+ // (on Unix, the ca certs might be empty, depending on whether we load
+ // them on demand or not, so set them explicitly)
+ socket.setCaCertificates(QSslSocket::systemCaCertificates());
+ QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
+ QSslSocket::setDefaultCiphers(QList<QSslCipher>());
+ QVERIFY(!socket.caCertificates().isEmpty());
+ QVERIFY(!socket.ciphers().isEmpty());
+
+ // verify the default as well:
+ QVERIFY(QSslConfiguration::defaultConfiguration().caCertificates().isEmpty());
+ QVERIFY(QSslConfiguration::defaultConfiguration().ciphers().isEmpty());
+
+ QSslConfiguration::setDefaultConfiguration(savedDefault);
+}
+
+void tst_QSslSocket::simpleConnect()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ QSslSocket socket;
+ QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
+ QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound()));
+ QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
+ QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted()));
+ QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(const QList<QSslError> &)));
+
+ connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(exitLoop()));
+
+ // Start connecting
+ socket.connectToHost(QtNetworkSettings::serverName(), 993);
+ QCOMPARE(socket.state(), QAbstractSocket::HostLookupState);
+ enterLoop(10);
+
+ // Entered connecting state
+#ifndef Q_OS_SYMBIAN
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectingState);
+ QCOMPARE(connectedSpy.count(), 0);
+#endif
+ QCOMPARE(hostFoundSpy.count(), 1);
+ QCOMPARE(disconnectedSpy.count(), 0);
+ enterLoop(10);
+
+ // Entered connected state
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(connectedSpy.count(), 1);
+ QCOMPARE(hostFoundSpy.count(), 1);
+ QCOMPARE(disconnectedSpy.count(), 0);
+
+ // Enter encrypted mode
+ socket.startClientEncryption();
+ QCOMPARE(socket.mode(), QSslSocket::SslClientMode);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(connectionEncryptedSpy.count(), 0);
+ QCOMPARE(sslErrorsSpy.count(), 0);
+
+ // Starting handshake
+ enterLoop(10);
+ QCOMPARE(sslErrorsSpy.count(), 1);
+ QCOMPARE(connectionEncryptedSpy.count(), 0);
+ QVERIFY(!socket.isEncrypted());
+ QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
+}
+
+void tst_QSslSocket::simpleConnectWithIgnore()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ QSslSocket socket;
+ this->socket = &socket;
+ QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted()));
+ QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(const QList<QSslError> &)));
+
+ connect(&socket, SIGNAL(readyRead()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ connect(&socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(exitLoop()));
+
+ // Start connecting
+ socket.connectToHost(QtNetworkSettings::serverName(), 993);
+ QVERIFY(socket.state() != QAbstractSocket::UnconnectedState); // something must be in progress
+ enterLoop(10);
+
+ // Start handshake
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ socket.startClientEncryption();
+ enterLoop(10);
+
+ // Done; encryption should be enabled.
+ QCOMPARE(sslErrorsSpy.count(), 1);
+ QVERIFY(socket.isEncrypted());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(encryptedSpy.count(), 1);
+
+ // Wait for incoming data
+ if (!socket.canReadLine())
+ enterLoop(10);
+
+ QByteArray data = socket.readAll();
+ socket.disconnectFromHost();
+ QVERIFY2(QtNetworkSettings::compareReplyIMAPSSL(data), data.constData());
+}
+
+void tst_QSslSocket::sslErrors_data()
+{
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<int>("port");
+ QTest::addColumn<SslErrorList>("expected");
+
+ QTest::newRow(qPrintable(QtNetworkSettings::serverLocalName()))
+ << QtNetworkSettings::serverLocalName()
+ << 993
+ << (SslErrorList() << QSslError::HostNameMismatch
+ << QSslError::SelfSignedCertificate);
+}
+
+void tst_QSslSocket::sslErrors()
+{
+ QFETCH(QString, host);
+ QFETCH(int, port);
+ QFETCH(SslErrorList, expected);
+
+ QSslSocketPtr socket = newSocket();
+ socket->connectToHostEncrypted(host, port);
+ if (!socket->waitForConnected())
+ QEXPECT_FAIL("imap.trolltech.com", "server not open to internet", Continue);
+ socket->waitForEncrypted(5000);
+
+ SslErrorList output;
+ foreach (QSslError error, socket->sslErrors()) {
+ output << error.error();
+ }
+
+#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+ if (output.count() && output.last() == QSslError::CertificateUntrusted)
+ output.takeLast();
+#endif
+ QCOMPARE(output, expected);
+}
+
+void tst_QSslSocket::addCaCertificate()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+}
+
+void tst_QSslSocket::addCaCertificates()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+}
+
+void tst_QSslSocket::addCaCertificates2()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+}
+
+void tst_QSslSocket::ciphers()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocket socket;
+ QCOMPARE(socket.ciphers(), QSslSocket::supportedCiphers());
+ socket.setCiphers(QList<QSslCipher>());
+ QVERIFY(socket.ciphers().isEmpty());
+ socket.setCiphers(socket.defaultCiphers());
+ QCOMPARE(socket.ciphers(), QSslSocket::supportedCiphers());
+ socket.setCiphers(socket.defaultCiphers());
+ QCOMPARE(socket.ciphers(), QSslSocket::supportedCiphers());
+
+ // Task 164356
+ socket.setCiphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+}
+
+void tst_QSslSocket::connectToHostEncrypted()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+ QVERIFY(socket->addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem")));
+#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#endif
+
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+
+ // This should pass unconditionally when using fluke's CA certificate.
+ // or use untrusted certificate workaround
+ QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString()));
+
+ socket->disconnectFromHost();
+ QVERIFY(socket->waitForDisconnected());
+
+ QCOMPARE(socket->mode(), QSslSocket::SslClientMode);
+
+ socket->connectToHost(QtNetworkSettings::serverName(), 13);
+
+ QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
+
+ QVERIFY(socket->waitForDisconnected());
+}
+
+void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+
+ socket->addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"));
+#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#endif
+
+ // connect to the server with its local name, but use the full name for verification.
+ socket->connectToHostEncrypted(QtNetworkSettings::serverLocalName(), 443, QtNetworkSettings::serverName());
+
+ // This should pass unconditionally when using fluke's CA certificate.
+ QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString()));
+
+ socket->disconnectFromHost();
+ QVERIFY(socket->waitForDisconnected());
+
+ QCOMPARE(socket->mode(), QSslSocket::SslClientMode);
+}
+
+void tst_QSslSocket::sessionCipher()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+ QVERIFY(socket->sessionCipher().isNull());
+ socket->connectToHost(QtNetworkSettings::serverName(), 443 /* https */);
+ QVERIFY(socket->waitForConnected(10000));
+ QVERIFY(socket->sessionCipher().isNull());
+ socket->startClientEncryption();
+ QVERIFY(socket->waitForEncrypted(5000));
+ QVERIFY(!socket->sessionCipher().isNull());
+ QVERIFY(QSslSocket::supportedCiphers().contains(socket->sessionCipher()));
+ socket->disconnectFromHost();
+ QVERIFY(socket->waitForDisconnected());
+}
+
+void tst_QSslSocket::flush()
+{
+}
+
+void tst_QSslSocket::isEncrypted()
+{
+}
+
+void tst_QSslSocket::localCertificate()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ // This test does not make 100% sense yet. We just set some local CA/cert/key and use it
+ // to authenticate ourselves against the server. The server does not actually check this
+ // values. This test should just run the codepath inside qsslsocket_openssl.cpp
+
+ QSslSocketPtr socket = newSocket();
+ QList<QSslCertificate> localCert = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem");
+ socket->setCaCertificates(localCert);
+ socket->setLocalCertificate(QLatin1String(SRCDIR "certs/fluke.cert"));
+ socket->setPrivateKey(QLatin1String(SRCDIR "certs/fluke.key"));
+
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(socket->waitForEncrypted(10000));
+}
+
+void tst_QSslSocket::mode()
+{
+}
+
+void tst_QSslSocket::peerCertificate()
+{
+}
+
+void tst_QSslSocket::peerCertificateChain()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+
+ QList<QSslCertificate> caCertificates = QSslCertificate::fromPath(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"));
+ QVERIFY(caCertificates.count() == 1);
+ socket->addCaCertificates(caCertificates);
+#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#endif
+
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(socket->peerCertificateChain().isEmpty());
+ QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString()));
+
+ QList<QSslCertificate> certChain = socket->peerCertificateChain();
+ QVERIFY(certChain.count() > 0);
+ QCOMPARE(certChain.first(), socket->peerCertificate());
+
+ socket->disconnectFromHost();
+ QVERIFY(socket->waitForDisconnected());
+
+ // connect again to a different server
+ socket->connectToHostEncrypted("trolltech.com", 443);
+ socket->ignoreSslErrors();
+ QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(socket->peerCertificateChain().isEmpty());
+ QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString()));
+
+ QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate());
+ QVERIFY(socket->peerCertificateChain() != certChain);
+
+ socket->disconnectFromHost();
+ QVERIFY(socket->waitForDisconnected());
+
+ // now do it again back to the original server
+ socket->connectToHost(QtNetworkSettings::serverName(), 443);
+ QCOMPARE(socket->mode(), QSslSocket::UnencryptedMode);
+ QVERIFY(socket->peerCertificateChain().isEmpty());
+ QVERIFY2(socket->waitForConnected(10000), "Network timeout");
+
+ socket->startClientEncryption();
+ QVERIFY2(socket->waitForEncrypted(10000), qPrintable(socket->errorString()));
+
+ QCOMPARE(socket->peerCertificateChain().first(), socket->peerCertificate());
+ QVERIFY(socket->peerCertificateChain() == certChain);
+
+ socket->disconnectFromHost();
+ QVERIFY(socket->waitForDisconnected());
+}
+
+void tst_QSslSocket::privateKey()
+{
+}
+
+void tst_QSslSocket::privateKeyOpaque()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QFile file(SRCDIR "certs/fluke.key");
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QVERIFY(!key.isNull());
+
+ EVP_PKEY *pkey = q_EVP_PKEY_new();
+ q_EVP_PKEY_set1_RSA(pkey, reinterpret_cast<RSA *>(key.handle()));
+
+ // This test does not make 100% sense yet. We just set some local CA/cert/key and use it
+ // to authenticate ourselves against the server. The server does not actually check this
+ // values. This test should just run the codepath inside qsslsocket_openssl.cpp
+
+ QSslSocketPtr socket = newSocket();
+ QList<QSslCertificate> localCert = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem");
+ socket->setCaCertificates(localCert);
+ socket->setLocalCertificate(QLatin1String(SRCDIR "certs/fluke.cert"));
+ socket->setPrivateKey(QSslKey(reinterpret_cast<Qt::HANDLE>(pkey)));
+
+ socket->setPeerVerifyMode(QSslSocket::QueryPeer);
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(socket->waitForEncrypted(10000));
+}
+
+void tst_QSslSocket::protocol()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem");
+
+ socket->setCaCertificates(certs);
+#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#endif
+
+ QCOMPARE(socket->protocol(), QSsl::SecureProtocols);
+ {
+ // Fluke allows SSLv3.
+ socket->setProtocol(QSsl::SslV3);
+ QCOMPARE(socket->protocol(), QSsl::SslV3);
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString()));
+ QCOMPARE(socket->protocol(), QSsl::SslV3);
+ socket->abort();
+ QCOMPARE(socket->protocol(), QSsl::SslV3);
+ socket->connectToHost(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
+ socket->startClientEncryption();
+ QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString()));
+ QCOMPARE(socket->protocol(), QSsl::SslV3);
+ socket->abort();
+ }
+ {
+ // Fluke allows TLSV1.
+ socket->setProtocol(QSsl::TlsV1);
+ QCOMPARE(socket->protocol(), QSsl::TlsV1);
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString()));
+ QCOMPARE(socket->protocol(), QSsl::TlsV1);
+ socket->abort();
+ QCOMPARE(socket->protocol(), QSsl::TlsV1);
+ socket->connectToHost(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
+ socket->startClientEncryption();
+ QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString()));
+ QCOMPARE(socket->protocol(), QSsl::TlsV1);
+ socket->abort();
+ }
+ {
+ // Fluke allows SSLV2.
+ socket->setProtocol(QSsl::SslV2);
+ QCOMPARE(socket->protocol(), QSsl::SslV2);
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(socket->waitForEncrypted());
+ QCOMPARE(socket->protocol(), QSsl::SslV2);
+ socket->abort();
+ QCOMPARE(socket->protocol(), QSsl::SslV2);
+ socket->connectToHost(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
+ socket->startClientEncryption();
+ QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString()));
+ socket->abort();
+ }
+ {
+ // Fluke allows SSLV3, so it allows AnyProtocol.
+ socket->setProtocol(QSsl::AnyProtocol);
+ QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(socket->waitForEncrypted());
+ QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
+ socket->abort();
+ QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
+ socket->connectToHost(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
+ socket->startClientEncryption();
+ QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString()));
+ QCOMPARE(socket->protocol(), QSsl::AnyProtocol);
+ socket->abort();
+ }
+ {
+ // Fluke allows SSLV3, so it allows NoSslV2
+ socket->setProtocol(QSsl::TlsV1SslV3);
+ QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(socket->waitForEncrypted());
+ QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
+ socket->abort();
+ QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
+ socket->connectToHost(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString()));
+ socket->startClientEncryption();
+ QVERIFY2(socket->waitForEncrypted(), qPrintable(socket->errorString()));
+ QCOMPARE(socket->protocol(), QSsl::TlsV1SslV3);
+ socket->abort();
+ }
+}
+
+class SslServer : public QTcpServer
+{
+ Q_OBJECT
+public:
+ SslServer(const QString &keyFile = SRCDIR "certs/fluke.key", const QString &certFile = SRCDIR "certs/fluke.cert")
+ : socket(0),
+ protocol(QSsl::TlsV1),
+ m_keyFile(keyFile),
+ m_certFile(certFile) { }
+ QSslSocket *socket;
+ QSsl::SslProtocol protocol;
+ QString m_keyFile;
+ QString m_certFile;
+
+protected:
+ void incomingConnection(int socketDescriptor)
+ {
+ socket = new QSslSocket(this);
+ socket->setProtocol(protocol);
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+
+ QFile file(m_keyFile);
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QVERIFY(!key.isNull());
+ socket->setPrivateKey(key);
+
+ QList<QSslCertificate> localCert = QSslCertificate::fromPath(m_certFile);
+ QVERIFY(!localCert.isEmpty());
+ QVERIFY(localCert.first().handle());
+ socket->setLocalCertificate(localCert.first());
+
+ QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
+ QVERIFY(!socket->peerAddress().isNull());
+ QVERIFY(socket->peerPort() != 0);
+ QVERIFY(!socket->localAddress().isNull());
+ QVERIFY(socket->localPort() != 0);
+
+ socket->startServerEncryption();
+ }
+
+protected slots:
+ void ignoreErrorSlot()
+ {
+ socket->ignoreSslErrors();
+ }
+};
+
+void tst_QSslSocket::protocolServerSide_data()
+{
+
+ QTest::addColumn<QSsl::SslProtocol>("serverProtocol");
+ QTest::addColumn<QSsl::SslProtocol>("clientProtocol");
+ QTest::addColumn<bool>("works");
+
+ QTest::newRow("ssl2-ssl2") << QSsl::SslV2 << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2
+ QTest::newRow("ssl3-ssl3") << QSsl::SslV3 << QSsl::SslV3 << true;
+ QTest::newRow("tls1-tls1") << QSsl::TlsV1 << QSsl::TlsV1 << true;
+ QTest::newRow("tls1ssl3-tls1ssl3") << QSsl::TlsV1SslV3 << QSsl::TlsV1SslV3 << true;
+ QTest::newRow("any-any") << QSsl::AnyProtocol << QSsl::AnyProtocol << true;
+ QTest::newRow("secure-secure") << QSsl::SecureProtocols << QSsl::SecureProtocols << true;
+
+ QTest::newRow("ssl2-ssl3") << QSsl::SslV2 << QSsl::SslV3 << false;
+ QTest::newRow("ssl2-tls1") << QSsl::SslV2 << QSsl::TlsV1 << false;
+ QTest::newRow("ssl2-tls1ssl3") << QSsl::SslV2 << QSsl::TlsV1SslV3 << false;
+ QTest::newRow("ssl2-secure") << QSsl::SslV2 << QSsl::SecureProtocols << false;
+ QTest::newRow("ssl2-any") << QSsl::SslV2 << QSsl::AnyProtocol << false; // no idea why it does not work, but we don't care about SSL 2
+
+ QTest::newRow("ssl3-ssl2") << QSsl::SslV3 << QSsl::SslV2 << false;
+ QTest::newRow("ssl3-tls1") << QSsl::SslV3 << QSsl::TlsV1 << false;
+ QTest::newRow("ssl3-tls1ssl3") << QSsl::SslV3 << QSsl::TlsV1SslV3 << true;
+ QTest::newRow("ssl3-secure") << QSsl::SslV3 << QSsl::SecureProtocols << true;
+ QTest::newRow("ssl3-any") << QSsl::SslV3 << QSsl::AnyProtocol << false; // we wont set a SNI header here because we connect to a
+ // numerical IP, so OpenSSL will send a SSL 2 handshake
+
+ QTest::newRow("tls1-ssl2") << QSsl::TlsV1 << QSsl::SslV2 << false;
+ QTest::newRow("tls1-ssl3") << QSsl::TlsV1 << QSsl::SslV3 << false;
+ QTest::newRow("tls1-tls1ssl3") << QSsl::TlsV1 << QSsl::TlsV1SslV3 << true;
+ QTest::newRow("tls1-secure") << QSsl::TlsV1 << QSsl::SecureProtocols << true;
+ QTest::newRow("tls1-any") << QSsl::TlsV1 << QSsl::AnyProtocol << false; // we wont set a SNI header here because we connect to a
+ // numerical IP, so OpenSSL will send a SSL 2 handshake
+
+ QTest::newRow("tls1ssl3-ssl2") << QSsl::TlsV1SslV3 << QSsl::SslV2 << false;
+ QTest::newRow("tls1ssl3-ssl3") << QSsl::TlsV1SslV3 << QSsl::SslV3 << true;
+ QTest::newRow("tls1ssl3-tls1") << QSsl::TlsV1SslV3 << QSsl::TlsV1 << true;
+ QTest::newRow("tls1ssl3-secure") << QSsl::TlsV1SslV3 << QSsl::SecureProtocols << true;
+ QTest::newRow("tls1ssl3-any") << QSsl::TlsV1SslV3 << QSsl::AnyProtocol << true;
+
+ QTest::newRow("secure-ssl2") << QSsl::SecureProtocols << QSsl::SslV2 << false;
+ QTest::newRow("secure-ssl3") << QSsl::SecureProtocols << QSsl::SslV3 << true;
+ QTest::newRow("secure-tls1") << QSsl::SecureProtocols << QSsl::TlsV1 << true;
+ QTest::newRow("secure-tls1ssl3") << QSsl::SecureProtocols << QSsl::TlsV1SslV3 << true;
+ QTest::newRow("secure-any") << QSsl::SecureProtocols << QSsl::AnyProtocol << true;
+
+ QTest::newRow("any-ssl2") << QSsl::AnyProtocol << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2
+ QTest::newRow("any-ssl3") << QSsl::AnyProtocol << QSsl::SslV3 << true;
+ QTest::newRow("any-tls1") << QSsl::AnyProtocol << QSsl::TlsV1 << true;
+ QTest::newRow("any-tls1ssl3") << QSsl::AnyProtocol << QSsl::TlsV1SslV3 << true;
+ QTest::newRow("any-secure") << QSsl::AnyProtocol << QSsl::SecureProtocols << true;
+}
+
+void tst_QSslSocket::protocolServerSide()
+{
+ if (!QSslSocket::supportsSsl()) {
+ qWarning("SSL not supported, skipping test");
+ return;
+ }
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ QFETCH(QSsl::SslProtocol, serverProtocol);
+ SslServer server;
+ server.protocol = serverProtocol;
+ QVERIFY(server.listen());
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+
+ QSslSocketPtr client = new QSslSocket;
+ socket = client;
+ QFETCH(QSsl::SslProtocol, clientProtocol);
+ socket->setProtocol(clientProtocol);
+ // upon SSL wrong version error, error will be triggered, not sslErrors
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit()));
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ connect(client, SIGNAL(encrypted()), &loop, SLOT(quit()));
+
+ client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+
+ loop.exec();
+
+ QFETCH(bool, works);
+ QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState;
+ QCOMPARE(client->state(), expectedState);
+ QCOMPARE(client->isEncrypted(), works);
+}
+
+void tst_QSslSocket::setCaCertificates()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocket socket;
+ QCOMPARE(socket.caCertificates(), QSslSocket::defaultCaCertificates());
+ socket.setCaCertificates(QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem"));
+ QCOMPARE(socket.caCertificates().size(), 1);
+ socket.setCaCertificates(socket.defaultCaCertificates());
+ QCOMPARE(socket.caCertificates(), QSslSocket::defaultCaCertificates());
+}
+
+void tst_QSslSocket::setLocalCertificate()
+{
+}
+
+void tst_QSslSocket::setPrivateKey()
+{
+}
+
+void tst_QSslSocket::setSocketDescriptor()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer server;
+ QVERIFY(server.listen());
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+
+ QSslSocketPtr client = new QSslSocket;
+ socket = client;
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ connect(client, SIGNAL(encrypted()), &loop, SLOT(quit()));
+
+ client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+
+ loop.exec();
+
+ QCOMPARE(client->state(), QAbstractSocket::ConnectedState);
+ QVERIFY(client->isEncrypted());
+ QVERIFY(!client->peerAddress().isNull());
+ QVERIFY(client->peerPort() != 0);
+ QVERIFY(!client->localAddress().isNull());
+ QVERIFY(client->localPort() != 0);
+}
+
+void tst_QSslSocket::setSslConfiguration_data()
+{
+ QTest::addColumn<QSslConfiguration>("configuration");
+ QTest::addColumn<bool>("works");
+
+ QTest::newRow("empty") << QSslConfiguration() << false;
+ QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
+ QTest::newRow("default") << conf << false; // does not contain test server cert
+ QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem");
+ conf.setCaCertificates(testServerCert);
+ QTest::newRow("set-root-cert") << conf << true;
+ conf.setProtocol(QSsl::SecureProtocols);
+ QTest::newRow("secure") << conf << true;
+}
+
+void tst_QSslSocket::setSslConfiguration()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ QFETCH(QSslConfiguration, configuration);
+ socket->setSslConfiguration(configuration);
+ this->socket = socket;
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QFETCH(bool, works);
+ QCOMPARE(socket->waitForEncrypted(10000), works);
+ if (works) {
+ socket->disconnectFromHost();
+ QVERIFY2(socket->waitForDisconnected(), qPrintable(socket->errorString()));
+ }
+}
+
+void tst_QSslSocket::waitForEncrypted()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+
+ QVERIFY(socket->waitForEncrypted(10000));
+}
+
+void tst_QSslSocket::waitForEncryptedMinusOne()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+
+ QVERIFY(socket->waitForEncrypted(-1));
+}
+
+void tst_QSslSocket::waitForConnectedEncryptedReadyRead()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 993);
+
+#ifdef Q_OS_SYMBIAN
+ QVERIFY(socket->waitForConnected(10000));
+ QVERIFY(socket->waitForEncrypted(10000));
+
+ // dont forget to login
+ QCOMPARE((int) socket->write("USER ftptest\r\n"), 14);
+ QCOMPARE((int) socket->write("PASS ftP2Ptf\r\n"), 14);
+
+ QVERIFY(socket->waitForReadyRead(10000));
+ QVERIFY(!socket->peerCertificate().isNull());
+ QVERIFY(!socket->peerCertificateChain().isEmpty());
+#else
+ QVERIFY(socket->waitForConnected(10000));
+ QVERIFY(socket->waitForEncrypted(10000));
+ QVERIFY(socket->waitForReadyRead(10000));
+ QVERIFY(!socket->peerCertificate().isNull());
+ QVERIFY(!socket->peerCertificateChain().isEmpty());
+#endif
+}
+
+void tst_QSslSocket::startClientEncryption()
+{
+}
+
+void tst_QSslSocket::startServerEncryption()
+{
+}
+
+void tst_QSslSocket::addDefaultCaCertificate()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ // Reset the global CA chain
+ QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates());
+
+ QList<QSslCertificate> flukeCerts = QSslCertificate::fromPath(SRCDIR "certs/qt-test-server-cacert.pem");
+ QCOMPARE(flukeCerts.size(), 1);
+ QList<QSslCertificate> globalCerts = QSslSocket::defaultCaCertificates();
+ QVERIFY(!globalCerts.contains(flukeCerts.first()));
+ QSslSocket::addDefaultCaCertificate(flukeCerts.first());
+ QCOMPARE(QSslSocket::defaultCaCertificates().size(), globalCerts.size() + 1);
+ QVERIFY(QSslSocket::defaultCaCertificates().contains(flukeCerts.first()));
+
+ // Restore the global CA chain
+ QSslSocket::setDefaultCaCertificates(QSslSocket::systemCaCertificates());
+}
+
+void tst_QSslSocket::addDefaultCaCertificates()
+{
+}
+
+void tst_QSslSocket::addDefaultCaCertificates2()
+{
+}
+
+void tst_QSslSocket::defaultCaCertificates()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QList<QSslCertificate> certs = QSslSocket::defaultCaCertificates();
+ QVERIFY(certs.size() > 1);
+ QCOMPARE(certs, QSslSocket::systemCaCertificates());
+}
+
+void tst_QSslSocket::defaultCiphers()
+{
+}
+
+void tst_QSslSocket::resetDefaultCiphers()
+{
+}
+
+void tst_QSslSocket::setDefaultCaCertificates()
+{
+}
+
+void tst_QSslSocket::setDefaultCiphers()
+{
+}
+
+void tst_QSslSocket::supportedCiphers()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QList<QSslCipher> ciphers = QSslSocket::supportedCiphers();
+ QVERIFY(ciphers.size() > 1);
+
+ QSslSocket socket;
+ QCOMPARE(socket.supportedCiphers(), ciphers);
+ QCOMPARE(socket.defaultCiphers(), ciphers);
+ QCOMPARE(socket.ciphers(), ciphers);
+}
+
+void tst_QSslSocket::systemCaCertificates()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QList<QSslCertificate> certs = QSslSocket::systemCaCertificates();
+ QVERIFY(certs.size() > 1);
+ QCOMPARE(certs, QSslSocket::defaultCaCertificates());
+}
+
+void tst_QSslSocket::wildcardCertificateNames()
+{
+ // Passing CN matches
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("www.example.com"), QString("www.example.com")), true );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("www.example.com")), true );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx*.example.com"), QString("xxxwww.example.com")), true );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("foo.example.com")), true );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("192.168.0.0"), QString("192.168.0.0")), true );
+
+ // Failing CN matches
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx.example.com"), QString("www.example.com")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www.example.com")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.*.com"), QString("www.example.com")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("baa.foo.example.com")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("baa.example.com")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.com"), QString("example.com")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*fail.com"), QString("example.com")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example.")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString(""), QString("www")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www")), false );
+ QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.168.0.0"), QString("192.168.0.0")), false );
+}
+
+void tst_QSslSocket::wildcard()
+{
+ QSKIP("TODO: solve wildcard problem", SkipAll);
+
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ // Fluke runs an apache server listening on port 4443, serving the
+ // wildcard fluke.*.troll.no. The DNS entry for
+ // fluke.wildcard.dev.troll.no, served by ares (root for dev.troll.no),
+ // returns the CNAME fluke.troll.no for this domain. The web server
+ // responds with the wildcard, and QSslSocket should accept that as a
+ // valid connection. This was broken in 4.3.0.
+ QSslSocketPtr socket = newSocket();
+ socket->addCaCertificates(QLatin1String("certs/aspiriniks.ca.crt"));
+ this->socket = socket;
+#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(untrustedWorkaroundSlot(QList<QSslError>)));
+#endif
+ socket->connectToHostEncrypted(QtNetworkSettings::wildcardServerName(), 4443);
+
+ QVERIFY2(socket->waitForEncrypted(3000), qPrintable(socket->errorString()));
+
+ QSslCertificate certificate = socket->peerCertificate();
+ QCOMPARE(certificate.subjectInfo(QSslCertificate::CommonName), QString(QtNetworkSettings::serverLocalName() + ".*." + QtNetworkSettings::serverDomainName()));
+ QCOMPARE(certificate.issuerInfo(QSslCertificate::CommonName), QtNetworkSettings::serverName());
+
+ socket->close();
+}
+
+class SslServer2 : public QTcpServer
+{
+protected:
+ void incomingConnection(int socketDescriptor)
+ {
+ QSslSocket *socket = new QSslSocket(this);
+ socket->ignoreSslErrors();
+
+ // Only set the certificate
+ QList<QSslCertificate> localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
+ QVERIFY(!localCert.isEmpty());
+ QVERIFY(localCert.first().handle());
+ socket->setLocalCertificate(localCert.first());
+
+ QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
+
+ socket->startServerEncryption();
+ }
+};
+
+void tst_QSslSocket::setEmptyKey()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer2 server;
+ server.listen();
+
+ QSslSocket socket;
+ socket.connectToHostEncrypted("127.0.0.1", server.serverPort());
+
+ QTestEventLoop::instance().enterLoop(2);
+
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError);
+}
+
+void tst_QSslSocket::spontaneousWrite()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer server;
+ QSslSocket *receiver = new QSslSocket(this);
+ connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop()));
+
+ // connect two sockets to each other:
+ QVERIFY(server.listen(QHostAddress::LocalHost));
+ receiver->connectToHost("127.0.0.1", server.serverPort());
+ QVERIFY(receiver->waitForConnected(5000));
+ QVERIFY(server.waitForNewConnection(0));
+
+ QSslSocket *sender = server.socket;
+ QVERIFY(sender);
+ QVERIFY(sender->state() == QAbstractSocket::ConnectedState);
+ receiver->setObjectName("receiver");
+ sender->setObjectName("sender");
+ receiver->ignoreSslErrors();
+ receiver->startClientEncryption();
+
+ // SSL handshake:
+ connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop()));
+ enterLoop(1);
+ QVERIFY(!timeout());
+ QVERIFY(sender->isEncrypted());
+ QVERIFY(receiver->isEncrypted());
+
+ // make sure there's nothing to be received on the sender:
+ while (sender->waitForReadyRead(10) || receiver->waitForBytesWritten(10)) {}
+
+ // spontaneously write something:
+ QByteArray data("Hello World");
+ sender->write(data);
+
+ // check if the other side receives it:
+ enterLoop(1);
+ QVERIFY(!timeout());
+ QCOMPARE(receiver->bytesAvailable(), qint64(data.size()));
+ QCOMPARE(receiver->readAll(), data);
+}
+
+void tst_QSslSocket::setReadBufferSize()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer server;
+ QSslSocket *receiver = new QSslSocket(this);
+ connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop()));
+
+ // connect two sockets to each other:
+ QVERIFY(server.listen(QHostAddress::LocalHost));
+ receiver->connectToHost("127.0.0.1", server.serverPort());
+ QVERIFY(receiver->waitForConnected(5000));
+ QVERIFY(server.waitForNewConnection(0));
+
+ QSslSocket *sender = server.socket;
+ QVERIFY(sender);
+ QVERIFY(sender->state() == QAbstractSocket::ConnectedState);
+ receiver->setObjectName("receiver");
+ sender->setObjectName("sender");
+ receiver->ignoreSslErrors();
+ receiver->startClientEncryption();
+
+ // SSL handshake:
+ connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop()));
+ enterLoop(1);
+ QVERIFY(!timeout());
+ QVERIFY(sender->isEncrypted());
+ QVERIFY(receiver->isEncrypted());
+
+ QByteArray data(2048, 'b');
+ receiver->setReadBufferSize(39 * 1024); // make it a non-multiple of the data.size()
+
+ // saturate the incoming buffer
+ while (sender->state() == QAbstractSocket::ConnectedState &&
+ receiver->state() == QAbstractSocket::ConnectedState &&
+ receiver->bytesAvailable() < receiver->readBufferSize()) {
+ sender->write(data);
+ //qDebug() << receiver->bytesAvailable() << "<" << receiver->readBufferSize() << (receiver->bytesAvailable() < receiver->readBufferSize());
+
+ while (sender->bytesToWrite())
+ QVERIFY(sender->waitForBytesWritten(10));
+
+ // drain it:
+ while (receiver->bytesAvailable() < receiver->readBufferSize() &&
+ receiver->waitForReadyRead(10)) {}
+ }
+
+ //qDebug() << sender->bytesToWrite() << "bytes to write";
+ //qDebug() << receiver->bytesAvailable() << "bytes available";
+
+ // send a bit more
+ sender->write(data);
+ sender->write(data);
+ sender->write(data);
+ sender->write(data);
+ QVERIFY(sender->waitForBytesWritten(10));
+
+ qint64 oldBytesAvailable = receiver->bytesAvailable();
+
+ // now unset the read buffer limit and iterate
+ receiver->setReadBufferSize(0);
+ enterLoop(1);
+ QVERIFY(!timeout());
+
+ QVERIFY(receiver->bytesAvailable() > oldBytesAvailable);
+}
+
+class SetReadBufferSize_task_250027_handler : public QObject {
+ Q_OBJECT
+public slots:
+ void readyReadSlot() {
+ QTestEventLoop::instance().exitLoop();
+ }
+ void waitSomeMore(QSslSocket *socket) {
+ QTime t;
+ t.start();
+ while (!socket->encryptedBytesAvailable()) {
+ QCoreApplication::processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents, 250);
+ if (t.elapsed() > 1000 || socket->state() != QAbstractSocket::ConnectedState)
+ return;
+ }
+ }
+};
+
+void tst_QSslSocket::setReadBufferSize_task_250027()
+{
+ // do not execute this when a proxy is set.
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ socket->setReadBufferSize(1000); // limit to 1 kb/sec
+ socket->ignoreSslErrors();
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ socket->ignoreSslErrors();
+ QVERIFY(socket->waitForConnected(10*1000));
+ QVERIFY(socket->waitForEncrypted(10*1000));
+
+ // exit the event loop as soon as we receive a readyRead()
+ SetReadBufferSize_task_250027_handler setReadBufferSize_task_250027_handler;
+ connect(socket, SIGNAL(readyRead()), &setReadBufferSize_task_250027_handler, SLOT(readyReadSlot()));
+
+ // provoke a response by sending a request
+ socket->write("GET /qtest/fluke.gif HTTP/1.0\n"); // this file is 27 KB
+ socket->write("Host: ");
+ socket->write(QtNetworkSettings::serverName().toLocal8Bit().constData());
+ socket->write("\n");
+ socket->write("Connection: close\n");
+ socket->write("\n");
+ socket->flush();
+
+ QTestEventLoop::instance().enterLoop(10);
+ setReadBufferSize_task_250027_handler.waitSomeMore(socket);
+ QByteArray firstRead = socket->readAll();
+ // First read should be some data, but not the whole file
+ QVERIFY(firstRead.size() > 0 && firstRead.size() < 20*1024);
+
+ QTestEventLoop::instance().enterLoop(10);
+ setReadBufferSize_task_250027_handler.waitSomeMore(socket);
+ QByteArray secondRead = socket->readAll();
+ // second read should be some more data
+ QVERIFY(secondRead.size() > 0);
+
+ socket->close();
+}
+
+class SslServer3 : public QTcpServer
+{
+ Q_OBJECT
+public:
+ SslServer3() : socket(0) { }
+ QSslSocket *socket;
+
+protected:
+ void incomingConnection(int socketDescriptor)
+ {
+ socket = new QSslSocket(this);
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+
+ QFile file(SRCDIR "certs/fluke.key");
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QVERIFY(!key.isNull());
+ socket->setPrivateKey(key);
+
+ QList<QSslCertificate> localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
+ QVERIFY(!localCert.isEmpty());
+ QVERIFY(localCert.first().handle());
+ socket->setLocalCertificate(localCert.first());
+
+ QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState));
+ QVERIFY(!socket->peerAddress().isNull());
+ QVERIFY(socket->peerPort() != 0);
+ QVERIFY(!socket->localAddress().isNull());
+ QVERIFY(socket->localPort() != 0);
+ }
+
+protected slots:
+ void ignoreErrorSlot()
+ {
+ socket->ignoreSslErrors();
+ }
+};
+
+class ThreadedSslServer: public QThread
+{
+ Q_OBJECT
+public:
+ QSemaphore dataReadSemaphore;
+ int serverPort;
+ bool ok;
+
+ ThreadedSslServer() : serverPort(-1), ok(false)
+ { }
+
+ ~ThreadedSslServer()
+ {
+ if (isRunning()) wait(2000);
+ QVERIFY(ok);
+ }
+
+signals:
+ void listening();
+
+protected:
+ void run()
+ {
+ // if all goes well (no timeouts), this thread will sleep for a total of 500 ms
+ // (i.e., 5 times 100 ms, one sleep for each operation)
+
+ SslServer3 server;
+ server.listen(QHostAddress::LocalHost);
+ serverPort = server.serverPort();
+ emit listening();
+
+ // delayed acceptance:
+ QTest::qSleep(100);
+#ifndef Q_OS_SYMBIAN
+ bool ret = server.waitForNewConnection(2000);
+#else
+ bool ret = server.waitForNewConnection(20000);
+#endif
+ Q_UNUSED(ret);
+
+ // delayed start of encryption
+ QTest::qSleep(100);
+ QSslSocket *socket = server.socket;
+ if (!socket || !socket->isValid())
+ return; // error
+ socket->ignoreSslErrors();
+ socket->startServerEncryption();
+ if (!socket->waitForEncrypted(2000))
+ return; // error
+
+ // delayed reading data
+ QTest::qSleep(100);
+ if (!socket->waitForReadyRead(2000))
+ return; // error
+ socket->readAll();
+ dataReadSemaphore.release();
+
+ // delayed sending data
+ QTest::qSleep(100);
+ socket->write("Hello, World");
+ while (socket->bytesToWrite())
+ if (!socket->waitForBytesWritten(2000))
+ return; // error
+
+ // delayed replying (reading then sending)
+ QTest::qSleep(100);
+ if (!socket->waitForReadyRead(2000))
+ return; // error
+ socket->write("Hello, World");
+ while (socket->bytesToWrite())
+ if (!socket->waitForBytesWritten(2000))
+ return; // error
+
+ // delayed disconnection:
+ QTest::qSleep(100);
+ socket->disconnectFromHost();
+ if (!socket->waitForDisconnected(2000))
+ return; // error
+
+ delete socket;
+ ok = true;
+ }
+};
+
+void tst_QSslSocket::waitForMinusOne()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ ThreadedSslServer server;
+ connect(&server, SIGNAL(listening()), SLOT(exitLoop()));
+
+ // start the thread and wait for it to be ready
+ server.start();
+ enterLoop(1);
+ QVERIFY(!timeout());
+
+ // connect to the server
+ QSslSocket socket;
+ QTest::qSleep(100);
+ socket.connectToHost("127.0.0.1", server.serverPort);
+ QVERIFY(socket.waitForConnected(-1));
+ socket.ignoreSslErrors();
+ socket.startClientEncryption();
+
+ // first verification: this waiting should take 200 ms
+ QVERIFY2(socket.waitForEncrypted(-1), qPrintable(socket.errorString()));
+ QVERIFY(socket.isEncrypted());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(socket.bytesAvailable(), Q_INT64_C(0));
+
+ // second verification: write and make sure the other side got it (100 ms)
+ socket.write("How are you doing?");
+ QVERIFY(socket.bytesToWrite() != 0);
+ QVERIFY(socket.waitForBytesWritten(-1));
+ QVERIFY(server.dataReadSemaphore.tryAcquire(1, 2000));
+
+ // third verification: it should wait for 100 ms:
+ QVERIFY(socket.waitForReadyRead(-1));
+ QVERIFY(socket.isEncrypted());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QVERIFY(socket.bytesAvailable() != 0);
+
+ // fourth verification: deadlock prevention:
+ // we write and then wait for reading; the other side needs to receive before
+ // replying (100 ms delay)
+ socket.write("I'm doing just fine!");
+ QVERIFY(socket.bytesToWrite() != 0);
+ QVERIFY(socket.waitForReadyRead(-1));
+
+ // fifth verification: it should wait for 200 ms more
+ QVERIFY(socket.waitForDisconnected(-1));
+}
+
+class VerifyServer : public QTcpServer
+{
+ Q_OBJECT
+public:
+ VerifyServer() : socket(0) { }
+ QSslSocket *socket;
+
+protected:
+ void incomingConnection(int socketDescriptor)
+ {
+ socket = new QSslSocket(this);
+
+ socket->setPrivateKey(SRCDIR "certs/fluke.key");
+ socket->setLocalCertificate(SRCDIR "certs/fluke.cert");
+ socket->setSocketDescriptor(socketDescriptor);
+ socket->startServerEncryption();
+ }
+};
+
+void tst_QSslSocket::verifyMode()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ QSslSocket socket;
+ QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer);
+ socket.setPeerVerifyMode(QSslSocket::VerifyNone);
+ QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone);
+ socket.setPeerVerifyMode(QSslSocket::VerifyNone);
+ socket.setPeerVerifyMode(QSslSocket::VerifyPeer);
+ QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyPeer);
+
+ socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(!socket.waitForEncrypted());
+
+ QList<QSslError> expectedErrors = QList<QSslError>()
+ << QSslError(QSslError::SelfSignedCertificate, socket.peerCertificate());
+ QCOMPARE(socket.sslErrors(), expectedErrors);
+ socket.abort();
+
+ VerifyServer server;
+ server.listen();
+
+ QSslSocket clientSocket;
+ clientSocket.connectToHostEncrypted("127.0.0.1", server.serverPort());
+ clientSocket.ignoreSslErrors();
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+ connect(&clientSocket, SIGNAL(encrypted()), &loop, SLOT(quit()));
+ loop.exec();
+
+ QVERIFY(clientSocket.isEncrypted());
+ QVERIFY(server.socket->sslErrors().isEmpty());
+}
+
+void tst_QSslSocket::verifyDepth()
+{
+ QSslSocket socket;
+ QCOMPARE(socket.peerVerifyDepth(), 0);
+ socket.setPeerVerifyDepth(1);
+ QCOMPARE(socket.peerVerifyDepth(), 1);
+ QTest::ignoreMessage(QtWarningMsg, "QSslSocket::setPeerVerifyDepth: cannot set negative depth of -1");
+ socket.setPeerVerifyDepth(-1);
+ QCOMPARE(socket.peerVerifyDepth(), 1);
+}
+
+void tst_QSslSocket::peerVerifyError()
+{
+ QSslSocketPtr socket = newSocket();
+ QSignalSpy sslErrorsSpy(socket, SIGNAL(sslErrors(QList<QSslError>)));
+ QSignalSpy peerVerifyErrorSpy(socket, SIGNAL(peerVerifyError(QSslError)));
+
+ socket->connectToHostEncrypted(QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(), 443);
+ QVERIFY(!socket->waitForEncrypted(10000));
+ QVERIFY(!peerVerifyErrorSpy.isEmpty());
+ QVERIFY(!sslErrorsSpy.isEmpty());
+ QCOMPARE(qVariantValue<QSslError>(peerVerifyErrorSpy.last().at(0)).error(), QSslError::HostNameMismatch);
+ QCOMPARE(qVariantValue<QList<QSslError> >(sslErrorsSpy.at(0).at(0)).size(), peerVerifyErrorSpy.size());
+}
+
+void tst_QSslSocket::disconnectFromHostWhenConnecting()
+{
+ QSslSocketPtr socket = newSocket();
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 993);
+ socket->ignoreSslErrors();
+ socket->write("XXXX LOGOUT\r\n");
+ QAbstractSocket::SocketState state = socket->state();
+ // without proxy, the state will be HostLookupState;
+ // with proxy, the state will be ConnectingState.
+ QVERIFY(socket->state() == QAbstractSocket::HostLookupState ||
+ socket->state() == QAbstractSocket::ConnectingState);
+ socket->disconnectFromHost();
+ // the state of the socket must be the same before and after calling
+ // disconnectFromHost()
+ QCOMPARE(state, socket->state());
+ QVERIFY(socket->state() == QAbstractSocket::HostLookupState ||
+ socket->state() == QAbstractSocket::ConnectingState);
+ QVERIFY(socket->waitForDisconnected(10000));
+ QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState);
+ // we did not call close, so the socket must be still open
+ QVERIFY(socket->isOpen());
+ QCOMPARE(socket->bytesToWrite(), qint64(0));
+
+ // dont forget to login
+ QCOMPARE((int) socket->write("USER ftptest\r\n"), 14);
+
+}
+
+void tst_QSslSocket::disconnectFromHostWhenConnected()
+{
+ QSslSocketPtr socket = newSocket();
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 993);
+ socket->ignoreSslErrors();
+#ifndef Q_OS_SYMBIAN
+ QVERIFY(socket->waitForEncrypted(5000));
+#else
+ QVERIFY(socket->waitForEncrypted(10000));
+#endif
+ socket->write("XXXX LOGOUT\r\n");
+ QCOMPARE(socket->state(), QAbstractSocket::ConnectedState);
+ socket->disconnectFromHost();
+ QCOMPARE(socket->state(), QAbstractSocket::ClosingState);
+#ifdef Q_OS_SYMBIAN
+ // I don't understand how socket->waitForDisconnected can work on other platforms
+ // since socket->write will end to:
+ // QMetaObject::invokeMethod(this, "_q_flushWriteBuffer", Qt::QueuedConnection);
+ // In order that _q_flushWriteBuffer will be called the eventloop need to run
+ // If we just call waitForDisconnected, which blocks the whole thread how that can happen?
+ connect(socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
+ enterLoop(5);
+ QVERIFY(!timeout());
+#else
+ QVERIFY(socket->waitForDisconnected(5000));
+#endif
+ QCOMPARE(socket->bytesToWrite(), qint64(0));
+}
+
+void tst_QSslSocket::resetProxy()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ // check fix for bug 199941
+
+ QNetworkProxy goodProxy(QNetworkProxy::NoProxy);
+ QNetworkProxy badProxy(QNetworkProxy::HttpProxy, "thisCannotWorkAbsolutelyNotForSure", 333);
+
+ // make sure the connection works, and then set a nonsense proxy, and then
+ // make sure it does not work anymore
+ QSslSocket socket;
+ socket.addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"));
+ socket.setProxy(goodProxy);
+ socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket.waitForConnected(10000), qPrintable(socket.errorString()));
+ socket.abort();
+ socket.setProxy(badProxy);
+ socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(! socket.waitForConnected(10000));
+
+ // dont forget to login
+ QCOMPARE((int) socket.write("USER ftptest\r\n"), 14);
+ QCOMPARE((int) socket.write("PASS password\r\n"), 15);
+
+ enterLoop(10);
+
+ // now the other way round:
+ // set the nonsense proxy and make sure the connection does not work,
+ // and then set the right proxy and make sure it works
+ QSslSocket socket2;
+ socket2.addCaCertificates(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"));
+ socket2.setProxy(badProxy);
+ socket2.connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY(! socket2.waitForConnected(10000));
+ socket2.abort();
+ socket2.setProxy(goodProxy);
+ socket2.connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(socket2.waitForConnected(10000), qPrintable(socket.errorString()));
+}
+
+void tst_QSslSocket::ignoreSslErrorsList_data()
+{
+ QTest::addColumn<QList<QSslError> >("expectedSslErrors");
+ QTest::addColumn<int>("expectedSslErrorSignalCount");
+
+ // construct the list of errors that we will get with the SSL handshake and that we will ignore
+ QList<QSslError> expectedSslErrors;
+ // fromPath gives us a list of certs, but it actually only contains one
+ QList<QSslCertificate> certs = QSslCertificate::fromPath(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"));
+ QSslError rightError(QSslError::SelfSignedCertificate, certs.at(0));
+ QSslError wrongError(QSslError::SelfSignedCertificate);
+
+
+ QTest::newRow("SSL-failure-empty-list") << expectedSslErrors << 1;
+ expectedSslErrors.append(wrongError);
+ QTest::newRow("SSL-failure-wrong-error") << expectedSslErrors << 1;
+ expectedSslErrors.append(rightError);
+ QTest::newRow("allErrorsInExpectedList1") << expectedSslErrors << 0;
+ expectedSslErrors.removeAll(wrongError);
+ QTest::newRow("allErrorsInExpectedList2") << expectedSslErrors << 0;
+ expectedSslErrors.removeAll(rightError);
+ QTest::newRow("SSL-failure-empty-list-again") << expectedSslErrors << 1;
+}
+
+void tst_QSslSocket::ignoreSslErrorsList()
+{
+ QSslSocket socket;
+ connect(&socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+
+// this->socket = &socket;
+ QSslCertificate cert;
+
+ QFETCH(QList<QSslError>, expectedSslErrors);
+ socket.ignoreSslErrors(expectedSslErrors);
+
+ QFETCH(int, expectedSslErrorSignalCount);
+ QSignalSpy sslErrorsSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
+
+ socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+
+ bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0);
+ QCOMPARE(socket.waitForEncrypted(10000), expectEncryptionSuccess);
+ QCOMPARE(sslErrorsSpy.count(), expectedSslErrorSignalCount);
+}
+
+void tst_QSslSocket::ignoreSslErrorsListWithSlot_data()
+{
+ ignoreSslErrorsList_data();
+}
+
+// this is not a test, just a slot called in the test below
+void tst_QSslSocket::ignoreErrorListSlot(const QList<QSslError> &)
+{
+ socket->ignoreSslErrors(storedExpectedSslErrors);
+}
+
+void tst_QSslSocket::ignoreSslErrorsListWithSlot()
+{
+ QSslSocket socket;
+ this->socket = &socket;
+
+ QFETCH(QList<QSslError>, expectedSslErrors);
+ // store the errors to ignore them later in the slot connected below
+ storedExpectedSslErrors = expectedSslErrors;
+ connect(&socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+ this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
+ connect(&socket, SIGNAL(sslErrors(const QList<QSslError> &)),
+ this, SLOT(ignoreErrorListSlot(const QList<QSslError> &)));
+ socket.connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+
+ QFETCH(int, expectedSslErrorSignalCount);
+ bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0);
+ QCOMPARE(socket.waitForEncrypted(10000), expectEncryptionSuccess);
+}
+
+// make sure a closed socket has no bytesAvailable()
+// related to https://bugs.webkit.org/show_bug.cgi?id=28016
+void tst_QSslSocket::readFromClosedSocket()
+{
+ QSslSocketPtr socket = newSocket();
+ socket->ignoreSslErrors();
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ socket->ignoreSslErrors();
+ socket->waitForConnected();
+ socket->waitForEncrypted();
+ // provoke a response by sending a request
+ socket->write("GET /qtest/fluke.gif HTTP/1.1\n");
+ socket->write("Host: ");
+ socket->write(QtNetworkSettings::serverName().toLocal8Bit().constData());
+ socket->write("\n");
+ socket->write("\n");
+ socket->waitForBytesWritten();
+ socket->waitForReadyRead();
+ QVERIFY(socket->state() == QAbstractSocket::ConnectedState);
+ QVERIFY(socket->bytesAvailable());
+ socket->close();
+ QVERIFY(!socket->bytesAvailable());
+ QVERIFY(!socket->bytesToWrite());
+ QVERIFY(socket->state() == QAbstractSocket::UnconnectedState);
+}
+
+void tst_QSslSocket::writeBigChunk()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslSocketPtr socket = newSocket();
+ this->socket = socket;
+
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+
+ QByteArray data;
+ data.resize(1024*1024*10); // 10 MB
+ // init with garbage. needed so ssl cannot compress it in an efficient way.
+ for (size_t i = 0; i < data.size() / sizeof(int); i++) {
+ int r = qrand();
+ data.data()[i*sizeof(int)] = r;
+ }
+
+ QVERIFY(socket->waitForEncrypted(10000));
+ QString errorBefore = socket->errorString();
+
+ int ret = socket->write(data.constData(), data.size());
+ QVERIFY(data.size() == ret);
+
+ // spin the event loop once so QSslSocket::transmit() gets called
+ QCoreApplication::processEvents();
+ QString errorAfter = socket->errorString();
+
+ // no better way to do this right now since the error is the same as the default error.
+ if (socket->errorString().startsWith(QLatin1String("Unable to write data")))
+ {
+ qWarning() << socket->error() << socket->errorString();
+ QFAIL("Error while writing! Check if the OpenSSL BIO size is limited?!");
+ }
+ // also check the error string. If another error (than UnknownError) occurred, it should be different than before
+ QVERIFY(errorBefore == errorAfter);
+
+ // check that everything has been written to OpenSSL
+ QVERIFY(socket->bytesToWrite() == 0);
+
+ socket->close();
+}
+
+void tst_QSslSocket::blacklistedCertificates()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ SslServer server(SRCDIR "certs/fake-login.live.com.key", SRCDIR "certs/fake-login.live.com.pem");
+ QSslSocket *receiver = new QSslSocket(this);
+ connect(receiver, SIGNAL(readyRead()), SLOT(exitLoop()));
+
+ // connect two sockets to each other:
+ QVERIFY(server.listen(QHostAddress::LocalHost));
+ receiver->connectToHost("127.0.0.1", server.serverPort());
+ QVERIFY(receiver->waitForConnected(5000));
+ QVERIFY(server.waitForNewConnection(0));
+
+ QSslSocket *sender = server.socket;
+ QVERIFY(sender);
+ QVERIFY(sender->state() == QAbstractSocket::ConnectedState);
+ receiver->setObjectName("receiver");
+ sender->setObjectName("sender");
+ receiver->startClientEncryption();
+
+ connect(receiver, SIGNAL(sslErrors(QList<QSslError>)), SLOT(exitLoop()));
+ connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop()));
+ enterLoop(1);
+ QList<QSslError> sslErrors = receiver->sslErrors();
+ QVERIFY(sslErrors.count() > 0);
+ // there are more errors (self signed cert and hostname mismatch), but we only care about the blacklist error
+ QCOMPARE(sslErrors.at(0).error(), QSslError::CertificateBlacklisted);
+}
+
+void tst_QSslSocket::setEmptyDefaultConfiguration()
+{
+ // used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265
+
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ QSslConfiguration emptyConf;
+ QSslConfiguration::setDefaultConfiguration(emptyConf);
+
+ QSslSocketPtr socket = newSocket();
+ connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(ignoreErrorSlot()));
+ socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
+ QVERIFY2(!socket->waitForEncrypted(4000), qPrintable(socket->errorString()));
+}
+
+void tst_QSslSocket::versionAccessors()
+{
+ if (!QSslSocket::supportsSsl())
+ return;
+
+ qDebug() << QSslSocket::sslLibraryVersionString();
+ qDebug() << QString::number(QSslSocket::sslLibraryVersionNumber(), 16);
+}
+
+#endif // QT_NO_OPENSSL
+
+QTEST_MAIN(tst_QSslSocket)
+#include "tst_qsslsocket.moc"