diff options
Diffstat (limited to 'tests/auto/qsslsocket')
-rw-r--r-- | tests/auto/qsslsocket/.gitignore | 1 | ||||
-rw-r--r-- | tests/auto/qsslsocket/certs/fluke.cert | 75 | ||||
-rw-r--r-- | tests/auto/qsslsocket/certs/fluke.key | 15 | ||||
-rw-r--r-- | tests/auto/qsslsocket/certs/qt-test-server-cacert.pem | 22 | ||||
-rw-r--r-- | tests/auto/qsslsocket/qsslsocket.pro | 22 | ||||
-rw-r--r-- | tests/auto/qsslsocket/ssl.tar.gz | bin | 0 -> 36299 bytes | |||
-rw-r--r-- | tests/auto/qsslsocket/tst_qsslsocket.cpp | 1513 |
7 files changed, 1648 insertions, 0 deletions
diff --git a/tests/auto/qsslsocket/.gitignore b/tests/auto/qsslsocket/.gitignore new file mode 100644 index 0000000..f2319a9 --- /dev/null +++ b/tests/auto/qsslsocket/.gitignore @@ -0,0 +1 @@ +tst_qsslsocket diff --git a/tests/auto/qsslsocket/certs/fluke.cert b/tests/auto/qsslsocket/certs/fluke.cert new file mode 100644 index 0000000..069fa6b --- /dev/null +++ b/tests/auto/qsslsocket/certs/fluke.cert @@ -0,0 +1,75 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 0 (0x0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, ST=Oslo, L=Nydalen, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + Validity + Not Before: Dec 4 01:10:32 2007 GMT + Not After : Apr 21 01:10:32 2035 GMT + Subject: C=NO, ST=Oslo, O=Nokia Corporation and/or its subsidiary(-ies), OU=Development, CN=fluke.troll.no + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:a7:c8:a0:4a:c4:19:05:1b:66:ba:32:e2:d2:f1: + 1c:6f:17:82:e4:39:2e:01:51:90:db:04:34:32:11: + 21:c2:0d:6f:59:d8:53:90:54:3f:83:8f:a9:d3:b3: + d5:ee:1a:9b:80:ae:c3:25:c9:5e:a5:af:4b:60:05: + aa:a0:d1:91:01:1f:ca:04:83:e3:58:1c:99:32:45: + 84:70:72:58:03:98:4a:63:8b:41:f5:08:49:d2:91: + 02:60:6b:e4:64:fe:dd:a0:aa:74:08:e9:34:4c:91: + 5f:12:3d:37:4d:54:2c:ad:7f:5b:98:60:36:02:8c: + 3b:f6:45:f3:27:6a:9b:94:9d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + 21:85:04:3D:23:01:66:E5:F7:9F:1A:84:24:8A:AF:0A:79:F4:E5:AC + X509v3 Authority Key Identifier: + DirName:/C=NO/ST=Oslo/L=Nydalen/O=Nokia Corporation and/or its subsidiary(-ies)/OU=Development/CN=fluke.troll.no/emailAddress=ahanssen@trolltech.com + serial:8E:A8:B4:E8:91:B7:54:2E + + Signature Algorithm: sha1WithRSAEncryption + 6d:57:5f:d1:05:43:f0:62:05:ec:2a:71:a5:dc:19:08:f2:c4: + a6:bd:bb:25:d9:ca:89:01:0e:e4:cf:1f:c1:8c:c8:24:18:35: + 53:59:7b:c0:43:b4:32:e6:98:b2:a6:ef:15:05:0b:48:5f:e1: + a0:0c:97:a9:a1:77:d8:35:18:30:bc:a9:8f:d3:b7:54:c7:f1: + a9:9e:5d:e6:19:bf:f6:3c:5b:2b:d8:e4:3e:62:18:88:8b:d3: + 24:e1:40:9b:0c:e6:29:16:62:ab:ea:05:24:70:36:aa:55:93: + ef:02:81:1b:23:10:a2:04:eb:56:95:75:fc:f8:94:b1:5d:42: + c5:3f:36:44:85:5d:3a:2e:90:46:8a:a2:b9:6f:87:ae:0c:15: + 40:19:31:90:fc:3b:25:bb:ae:f1:66:13:0d:85:90:d9:49:34: + 8f:f2:5d:f9:7a:db:4d:5d:27:f6:76:9d:35:8c:06:a6:4c:a3: + b1:b2:b6:6f:1d:d7:a3:00:fd:72:eb:9e:ea:44:a1:af:21:34: + 7d:c7:42:e2:49:91:19:8b:c0:ad:ba:82:80:a8:71:70:f4:35: + 31:91:63:84:20:95:e9:60:af:64:8b:cc:ff:3d:8a:76:74:3d: + c8:55:6d:e4:8e:c3:2b:1c:e8:42:18:ae:9f:e6:6b:9c:34:06: + ec:6a:f2:c3 +-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgIBADANBgkqhkiG9w0BAQUFADCBnDELMAkGA1UEBhMCTk8x +DTALBgNVBAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xs +dGVjaCBBU0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50 +cm9sbC5ubzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbTAe +Fw0wNzEyMDQwMTEwMzJaFw0zNTA0MjEwMTEwMzJaMGMxCzAJBgNVBAYTAk5PMQ0w +CwYDVQQIEwRPc2xvMRYwFAYDVQQKEw1Ucm9sbHRlY2ggQVNBMRQwEgYDVQQLEwtE +ZXZlbG9wbWVudDEXMBUGA1UEAxMOZmx1a2UudHJvbGwubm8wgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBAKfIoErEGQUbZroy4tLxHG8XguQ5LgFRkNsENDIRIcIN +b1nYU5BUP4OPqdOz1e4am4CuwyXJXqWvS2AFqqDRkQEfygSD41gcmTJFhHByWAOY +SmOLQfUISdKRAmBr5GT+3aCqdAjpNEyRXxI9N01ULK1/W5hgNgKMO/ZF8ydqm5Sd +AgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM +IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIYUEPSMBZuX3nxqEJIqv +Cnn05awwgbsGA1UdIwSBszCBsKGBoqSBnzCBnDELMAkGA1UEBhMCTk8xDTALBgNV +BAgTBE9zbG8xEDAOBgNVBAcTB055ZGFsZW4xFjAUBgNVBAoTDVRyb2xsdGVjaCBB +U0ExFDASBgNVBAsTC0RldmVsb3BtZW50MRcwFQYDVQQDEw5mbHVrZS50cm9sbC5u +bzElMCMGCSqGSIb3DQEJARYWYWhhbnNzZW5AdHJvbGx0ZWNoLmNvbYIJAI6otOiR +t1QuMA0GCSqGSIb3DQEBBQUAA4IBAQBtV1/RBUPwYgXsKnGl3BkI8sSmvbsl2cqJ +AQ7kzx/BjMgkGDVTWXvAQ7Qy5piypu8VBQtIX+GgDJepoXfYNRgwvKmP07dUx/Gp +nl3mGb/2PFsr2OQ+YhiIi9Mk4UCbDOYpFmKr6gUkcDaqVZPvAoEbIxCiBOtWlXX8 ++JSxXULFPzZEhV06LpBGiqK5b4euDBVAGTGQ/Dslu67xZhMNhZDZSTSP8l35ettN +XSf2dp01jAamTKOxsrZvHdejAP1y657qRKGvITR9x0LiSZEZi8CtuoKAqHFw9DUx +kWOEIJXpYK9ki8z/PYp2dD3IVW3kjsMrHOhCGK6f5mucNAbsavLD +-----END CERTIFICATE----- diff --git a/tests/auto/qsslsocket/certs/fluke.key b/tests/auto/qsslsocket/certs/fluke.key new file mode 100644 index 0000000..9d1664d --- /dev/null +++ b/tests/auto/qsslsocket/certs/fluke.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCnyKBKxBkFG2a6MuLS8RxvF4LkOS4BUZDbBDQyESHCDW9Z2FOQ +VD+Dj6nTs9XuGpuArsMlyV6lr0tgBaqg0ZEBH8oEg+NYHJkyRYRwclgDmEpji0H1 +CEnSkQJga+Rk/t2gqnQI6TRMkV8SPTdNVCytf1uYYDYCjDv2RfMnapuUnQIDAQAB +AoGANFzLkanTeSGNFM0uttBipFT9F4a00dqHz6JnO7zXAT26I5r8sU1pqQBb6uLz +/+Qz5Zwk8RUAQcsMRgJetuPQUb0JZjF6Duv24hNazqXBCu7AZzUenjafwmKC/8ri +KpX3fTwqzfzi//FKGgbXQ80yykSSliDL3kn/drATxsLCgQECQQDXhEFWLJ0vVZ1s +1Ekf+3NITE+DR16X+LQ4W6vyEHAjTbaNWtcTKdAWLA2l6N4WAAPYSi6awm+zMxx4 +VomVTsjdAkEAx0z+e7natLeFcrrq8pbU+wa6SAP1VfhQWKitxL1e7u/QO90NCpxE +oQYKzMkmmpOOFjQwEMAy1dvFMbm4LHlewQJAC/ksDBaUcQHHqjktCtrUb8rVjAyW +A8lscckeB2fEYyG5J6dJVaY4ClNOOs5yMDS2Afk1F6H/xKvtQ/5CzInA/QJATDub +K+BPU8jO9q+gpuIi3VIZdupssVGmCgObVCHLakG4uO04y9IyPhV9lA9tALtoIf4c +VIvv5fWGXBrZ48kZAQJBAJmVCdzQxd9LZI5vxijUCj5EI4e+x5DRqVUvyP8KCZrC +AiNyoDP85T+hBZaSXK3aYGpVwelyj3bvo1GrTNwNWLw= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/qsslsocket/certs/qt-test-server-cacert.pem b/tests/auto/qsslsocket/certs/qt-test-server-cacert.pem new file mode 100644 index 0000000..83adca2 --- /dev/null +++ b/tests/auto/qsslsocket/certs/qt-test-server-cacert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDuDCCAyGgAwIBAgIJAM17QpZu2GP7MA0GCSqGSIb3DQEBBAUAMIGaMQ4wDAYD +VQQKEwVOb2tpYTEUMBIGA1UECxMLUXQgU29mdHdhcmUxIjAgBgkqhkiG9w0BCQEW +E25vYm9keUBub2RvbWFpbi5vcmcxDTALBgNVBAcTBE9zbG8xDTALBgNVBAgTBE9z +bG8xCzAJBgNVBAYTAk5PMSMwIQYDVQQDExpxdC10ZXN0LXNlcnZlci5xdC10ZXN0 +LW5ldDAeFw0wODEyMDIxNDQ3MjZaFw0xODExMzAxNDQ3MjZaMIGaMQ4wDAYDVQQK +EwVOb2tpYTEUMBIGA1UECxMLUXQgU29mdHdhcmUxIjAgBgkqhkiG9w0BCQEWE25v +Ym9keUBub2RvbWFpbi5vcmcxDTALBgNVBAcTBE9zbG8xDTALBgNVBAgTBE9zbG8x +CzAJBgNVBAYTAk5PMSMwIQYDVQQDExpxdC10ZXN0LXNlcnZlci5xdC10ZXN0LW5l +dDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAz7dQ0l6IYpwVUcvj0mQxvG80 +yzoRzYr+alh7HMmOFI6/xjBHD6zAEEmLBafY7M/xe8PGH7ds2l2BFJkz0OS+IJRX +8CdOoeFvmVyp+L84tzXk81NKnMQ3y8DiFc6aUkfnyybA0whIv/TlqNyrYeQUin+t +61dPf1vr0LAAm5HdeYECAwEAAaOCAQIwgf8wDAYDVR0TBAUwAwEB/zAdBgNVHQ4E +FgQUwhEr5xV4r0deMQd3XwFkFtwls20wgc8GA1UdIwSBxzCBxIAUwhEr5xV4r0de +MQd3XwFkFtwls22hgaCkgZ0wgZoxDjAMBgNVBAoTBU5va2lhMRQwEgYDVQQLEwtR +dCBTb2Z0d2FyZTEiMCAGCSqGSIb3DQEJARYTbm9ib2R5QG5vZG9tYWluLm9yZzEN +MAsGA1UEBxMET3NsbzENMAsGA1UECBMET3NsbzELMAkGA1UEBhMCTk8xIzAhBgNV +BAMTGnF0LXRlc3Qtc2VydmVyLnF0LXRlc3QtbmV0ggkAzXtClm7YY/swDQYJKoZI +hvcNAQEEBQADgYEAQ/8YDtHrUoEsu9j5J6GY8iuuT8jvs/W1se5vXzoITgld+vLM +RWzxz35Hwzy2n31MNmUagRyQsTNOvEtJTxPCP97eLLxxrHDAbRmY/PPcZfolfOQf +xKQYf9naBv2F9Bs0WcY9z0Dgdl27szTAN67vGddYx5HpU9UE8Or5hdFJI3I= +-----END CERTIFICATE----- diff --git a/tests/auto/qsslsocket/qsslsocket.pro b/tests/auto/qsslsocket/qsslsocket.pro new file mode 100644 index 0000000..147b40d --- /dev/null +++ b/tests/auto/qsslsocket/qsslsocket.pro @@ -0,0 +1,22 @@ +load(qttest_p4) + +SOURCES += tst_qsslsocket.cpp +!wince*:win32:LIBS += -lws2_32 +QT += network +QT -= gui + +TARGET = tst_qsslsocket + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = debug +} else { + DESTDIR = release + } +} + +wince*: { + certFiles.sources = certs ssl.tar.gz + certFiles.path = . + DEPLOYMENT += certFiles +} diff --git a/tests/auto/qsslsocket/ssl.tar.gz b/tests/auto/qsslsocket/ssl.tar.gz Binary files differnew file mode 100644 index 0000000..b59af51 --- /dev/null +++ b/tests/auto/qsslsocket/ssl.tar.gz diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp new file mode 100644 index 0000000..4f98624 --- /dev/null +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -0,0 +1,1513 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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> + +#ifndef TEST_QNETWORK_PROXY +#define TEST_QNETWORK_PROXY +#endif +#ifdef TEST_QNETWORK_PROXY +#include <QNetworkProxy> +#include <QAuthenticator> +#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) +#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 + +#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 sessionCipher(); + void flush(); + void isEncrypted(); + void localCertificate(); + void mode(); + void peerCertificate(); + void peerCertificateChain(); + void privateKey(); + void protocol(); + void setCaCertificates(); + void setLocalCertificate(); + void setPrivateKey(); + void setProtocol(); + void setSocketDescriptor(); + void waitForEncrypted(); + 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 wildcard(); + void setEmptyKey(); + void spontaneousWrite(); + void setReadBufferSize(); + void waitForMinusOne(); + void verifyMode(); + void verifyDepth(); + void peerVerifyError(); + void disconnectFromHostWhenConnecting(); + void disconnectFromHostWhenConnected(); + void resetProxy(); + + 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) + socket->ignoreSslErrors(); + } + +private: + QSslSocket *socket; +#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 +} + +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; +#ifdef TEST_QNETWORK_PROXY + 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); +#endif +} + +void tst_QSslSocket::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { +#ifdef TEST_QNETWORK_PROXY + 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); +#endif + } +} + +void tst_QSslSocket::cleanup() +{ +#ifdef TEST_QNETWORK_PROXY + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +#endif +} + +#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'; + QTest::ignoreMessage(QtWarningMsg, "QIODevice::getChar: Closed device"); + 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::SslV3); + + QSslConfiguration savedDefault = QSslConfiguration::defaultConfiguration(); + + // verify that changing the default config doesn't affect this socket + 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("imap.troll.no", 993); + QCOMPARE(socket.state(), QAbstractSocket::HostLookupState); + enterLoop(10); + + // Entered connecting state + QCOMPARE(socket.state(), QAbstractSocket::ConnectingState); + QCOMPARE(connectedSpy.count(), 0); + 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("imap.troll.no", 993); + QCOMPARE(socket.state(), QAbstractSocket::HostLookupState); + 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); + + QCOMPARE(socket.readAll(), QByteArray("* OK esparsett Cyrus IMAP4 v2.2.8 server ready\r\n")); + + socket.disconnectFromHost(); +} + +void tst_QSslSocket::sslErrors_data() +{ + QTest::addColumn<QString>("host"); + QTest::addColumn<int>("port"); + QTest::addColumn<SslErrorList>("errors"); + + QTest::newRow("imap.troll.no") << "imap.troll.no" << 993 + << (SslErrorList() + << QSslError::HostNameMismatch + << QSslError::SelfSignedCertificateInChain); + QTest::newRow("imap.trolltech.com") << "imap.trolltech.com" << 993 + << (SslErrorList() + << QSslError::SelfSignedCertificateInChain); +} + +void tst_QSslSocket::sslErrors() +{ + QFETCH(QString, host); + QFETCH(int, port); + QFETCH(SslErrorList, errors); + + QSslSocketPtr socket = newSocket(); + socket->connectToHostEncrypted(host, port); + socket->waitForEncrypted(5000); + + SslErrorList list; + foreach (QSslError error, socket->sslErrors()) + list << error.error(); + +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + if (list.last() == QSslError::CertificateUntrusted) + list.takeLast(); +#endif + QCOMPARE(list, errors); +} + +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; + + socket->addCaCertificates(QLatin1String("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. + 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::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(5000)); + 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() +{ +} + +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("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::protocol() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocketPtr socket = newSocket(); + this->socket = socket; + QList<QSslCertificate> certs = QSslCertificate::fromPath("certs/qt-test-server-cacert.pem"); + +// qDebug() << "certs:" << certs.at(0).issuerInfo(QSslCertificate::CommonName); + socket->setCaCertificates(certs); +#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND + connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), + this, SLOT(untrustedWorkaroundSlot(QList<QSslError>))); +#endif + +// qDebug() << "socket cert:" << socket->caCertificates().at(0).issuerInfo(QSslCertificate::CommonName); + QCOMPARE(socket->protocol(), QSsl::SslV3); + { + // 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(); + } +} + +void tst_QSslSocket::setCaCertificates() +{ + if (!QSslSocket::supportsSsl()) + return; + + QSslSocket socket; + QCOMPARE(socket.caCertificates(), QSslSocket::defaultCaCertificates()); + socket.setCaCertificates(QSslCertificate::fromPath("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::setProtocol() +{ +} + +class SslServer : public QTcpServer +{ + Q_OBJECT +public: + SslServer() : 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("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("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); + + socket->startServerEncryption(); + } + +protected slots: + void ignoreErrorSlot() + { + socket->ignoreSslErrors(); + } +}; + +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::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::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); + + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->waitForEncrypted(10000)); + QVERIFY(socket->waitForReadyRead(10000)); + QVERIFY(!socket->peerCertificate().isNull()); + QVERIFY(!socket->peerCertificateChain().isEmpty()); +} + +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("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::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/qt-test-server-cacert.pem")); + 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("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 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("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("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); + server.waitForNewConnection(2000); + + // delayed start of encryption + QTest::qSleep(100); + QSslSocket *socket = server.socket; + 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; + 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("certs/fluke.key"); + socket->setLocalCertificate("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::UnableToGetLocalIssuerCertificate, socket.peerCertificate()) + << QSslError(QSslError::CertificateUntrusted, socket.peerCertificate()) + << QSslError(QSslError::UnableToVerifyFirstCertificate, 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(5000)); + 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)); +} + +void tst_QSslSocket::disconnectFromHostWhenConnected() +{ + QSslSocketPtr socket = newSocket(); + socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 993); + socket->ignoreSslErrors(); + QVERIFY(socket->waitForEncrypted(5000)); + socket->write("XXXX LOGOUT\r\n"); + QCOMPARE(socket->state(), QAbstractSocket::ConnectedState); + socket->disconnectFromHost(); + QCOMPARE(socket->state(), QAbstractSocket::ClosingState); + QVERIFY(socket->waitForDisconnected(5000)); + 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("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)); + + // 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("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())); +} + +#endif // QT_NO_OPENSSL + +QTEST_MAIN(tst_QSslSocket) +#include "tst_qsslsocket.moc" |