From 83c637aa942882880a73c7c4972c6c53332e0464 Mon Sep 17 00:00:00 2001 From: Kalle Viironen Date: Tue, 17 Apr 2012 16:43:40 +0300 Subject: Fix bug in qsslsocket peek() Calling peek() for qsslsocket caused socket data to be copied into qiodevices buffer and therefore make it unaccessible in qsslsocket. Cherry picked form 4.8-branch & modified to Qt5 API changes (int -> qintptr) Original commits: commit 621f18955082fc73471e75d1f8c35c2dcd4befeb Author: Shane Kearns commit 68b1d5c17aa38d5921bdade2b0e0cb67c6c90513 Author: Kalle Viironen Task-number: QTBUG-18498 Change-Id: I6be4b19baec2f3197537f5e7b61432040ec84ad2 Reviewed-by: Shane Kearns Reviewed-by: Thiago Macieira --- .../auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 257 +++++++++++++++++++++ 1 file changed, 257 insertions(+) (limited to 'tests/auto') diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index 23e87b7f3b..333102030a 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -189,6 +189,8 @@ private slots: void encryptWithoutConnecting(); void resume_data(); void resume(); + void qtbug18498_peek(); + void qtbug18498_peek2(); void setEmptyDefaultConfiguration(); // this test should be last static void exitLoop() @@ -2198,6 +2200,261 @@ void tst_QSslSocket::resume() } } +class WebSocket : public QSslSocket +{ + Q_OBJECT +public: + explicit WebSocket(qintptr socketDescriptor, + const QString &keyFile = SRCDIR "certs/fluke.key", + const QString &certFile = SRCDIR "certs/fluke.cert"); + +protected slots: + void onReadyReadFirstBytes(void); + +private: + void _startServerEncryption(void); + + QString m_keyFile; + QString m_certFile; + +private: + Q_DISABLE_COPY(WebSocket) +}; + +WebSocket::WebSocket (qintptr socketDescriptor, const QString &keyFile, const QString &certFile) + : m_keyFile(keyFile), + m_certFile(certFile) +{ + QVERIFY(setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered)); + connect (this, SIGNAL(readyRead()), this, SLOT(onReadyReadFirstBytes())); +} + +void WebSocket::_startServerEncryption (void) +{ + QFile file(m_keyFile); + QVERIFY(file.open(QIODevice::ReadOnly)); + QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + setPrivateKey(key); + + QList localCert = QSslCertificate::fromPath(m_certFile); + QVERIFY(!localCert.isEmpty()); + QVERIFY(localCert.first().handle()); + setLocalCertificate(localCert.first()); + + QVERIFY(!peerAddress().isNull()); + QVERIFY(peerPort() != 0); + QVERIFY(!localAddress().isNull()); + QVERIFY(localPort() != 0); + + setProtocol(QSsl::AnyProtocol); + setPeerVerifyMode(QSslSocket::VerifyNone); + ignoreSslErrors(); + startServerEncryption(); +} + +void WebSocket::onReadyReadFirstBytes (void) +{ + peek(1); + disconnect(this,SIGNAL(readyRead()), this, SLOT(onReadyReadFirstBytes())); + _startServerEncryption(); +} + +class SslServer4 : public QTcpServer +{ + Q_OBJECT +public: + SslServer4() : socket(0) {} + WebSocket *socket; + +protected: + void incomingConnection(qintptr socketDescriptor) + { + socket = new WebSocket(socketDescriptor); + } +}; + +void tst_QSslSocket::qtbug18498_peek() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer4 server; + QSslSocket *client = new QSslSocket(this); + + QVERIFY(server.listen(QHostAddress::LocalHost)); + client->connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(client->waitForConnected(5000)); + QVERIFY(server.waitForNewConnection(1000)); + client->setObjectName("client"); + client->ignoreSslErrors(); + + connect(client, SIGNAL(encrypted()), this, SLOT(exitLoop())); + connect(client, SIGNAL(disconnected()), this, SLOT(exitLoop())); + + client->startClientEncryption(); + WebSocket *serversocket = server.socket; + QVERIFY(serversocket); + serversocket->setObjectName("server"); + + enterLoop(1); + QVERIFY(!timeout()); + QVERIFY(serversocket->isEncrypted()); + QVERIFY(client->isEncrypted()); + + QByteArray data("abc123"); + client->write(data.data()); + + connect(serversocket, SIGNAL(readyRead()), this, SLOT(exitLoop())); + enterLoop(1); + QVERIFY(!timeout()); + + QByteArray peek1_data; + peek1_data.reserve(data.size()); + QByteArray peek2_data; + QByteArray read_data; + + int lngth = serversocket->peek(peek1_data.data(), 10); + peek1_data.resize(lngth); + + peek2_data = serversocket->peek(10); + read_data = serversocket->readAll(); + + QCOMPARE(peek1_data, data); + QCOMPARE(peek2_data, data); + QCOMPARE(read_data, data); +} + +class SslServer5 : public QTcpServer +{ + Q_OBJECT +public: + SslServer5() : socket(0) {} + QSslSocket *socket; + +protected: + void incomingConnection(qintptr socketDescriptor) + { + socket = new QSslSocket; + socket->setSocketDescriptor(socketDescriptor); + } +}; + +void tst_QSslSocket::qtbug18498_peek2() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer5 listener; + QVERIFY(listener.listen(QHostAddress::Any)); + QScopedPointer client(new QSslSocket); + client->connectToHost(QHostAddress::LocalHost, listener.serverPort()); + QVERIFY(client->waitForConnected(5000)); + QVERIFY(listener.waitForNewConnection(1000)); + + QScopedPointer server(listener.socket); + + QVERIFY(server->write("HELLO\r\n", 7)); + QElapsedTimer stopwatch; + stopwatch.start(); + while (client->bytesAvailable() < 7 && stopwatch.elapsed() < 5000) + QTest::qWait(100); + char c; + QVERIFY(client->peek(&c,1) == 1); + QCOMPARE(c, 'H'); + QVERIFY(client->read(&c,1) == 1); + QCOMPARE(c, 'H'); + QByteArray b = client->peek(2); + QCOMPARE(b, QByteArray("EL")); + char a[3]; + QVERIFY(client->peek(a, 2) == 2); + QCOMPARE(a[0], 'E'); + QCOMPARE(a[1], 'L'); + QCOMPARE(client->readAll(), QByteArray("ELLO\r\n")); + + //check data split between QIODevice and plain socket buffers. + QByteArray bigblock; + bigblock.fill('#', QIODEVICE_BUFFERSIZE + 1024); + QVERIFY(client->write(QByteArray("head"))); + QVERIFY(client->write(bigblock)); + while (server->bytesAvailable() < bigblock.length() + 4 && stopwatch.elapsed() < 5000) + QTest::qWait(100); + QCOMPARE(server->read(4), QByteArray("head")); + QCOMPARE(server->peek(bigblock.length()), bigblock); + b.reserve(bigblock.length()); + b.resize(server->peek(b.data(), bigblock.length())); + QCOMPARE(b, bigblock); + + //check oversized peek + QCOMPARE(server->peek(bigblock.length() * 3), bigblock); + b.reserve(bigblock.length() * 3); + b.resize(server->peek(b.data(), bigblock.length() * 3)); + QCOMPARE(b, bigblock); + + QCOMPARE(server->readAll(), bigblock); + + QVERIFY(client->write("STARTTLS\r\n")); + stopwatch.start(); + // ### Qt5 use QTRY_VERIFY + while (server->bytesAvailable() < 10 && stopwatch.elapsed() < 5000) + QTest::qWait(100); + QVERIFY(server->peek(&c,1) == 1); + QCOMPARE(c, 'S'); + b = server->peek(3); + QCOMPARE(b, QByteArray("STA")); + QCOMPARE(server->read(5), QByteArray("START")); + QVERIFY(server->peek(a, 3) == 3); + QCOMPARE(a[0], 'T'); + QCOMPARE(a[1], 'L'); + QCOMPARE(a[2], 'S'); + QCOMPARE(server->readAll(), QByteArray("TLS\r\n")); + + QFile file(SRCDIR "certs/fluke.key"); + QVERIFY(file.open(QIODevice::ReadOnly)); + QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); + server->setPrivateKey(key); + + QList localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert"); + QVERIFY(!localCert.isEmpty()); + QVERIFY(localCert.first().handle()); + server->setLocalCertificate(localCert.first()); + + server->setProtocol(QSsl::AnyProtocol); + server->setPeerVerifyMode(QSslSocket::VerifyNone); + + server->ignoreSslErrors(); + client->ignoreSslErrors(); + + server->startServerEncryption(); + client->startClientEncryption(); + + QVERIFY(server->write("hello\r\n", 7)); + stopwatch.start(); + while (client->bytesAvailable() < 7 && stopwatch.elapsed() < 5000) + QTest::qWait(100); + QVERIFY(server->mode() == QSslSocket::SslServerMode && client->mode() == QSslSocket::SslClientMode); + QVERIFY(client->peek(&c,1) == 1); + QCOMPARE(c, 'h'); + QVERIFY(client->read(&c,1) == 1); + QCOMPARE(c, 'h'); + b = client->peek(2); + QCOMPARE(b, QByteArray("el")); + QCOMPARE(client->readAll(), QByteArray("ello\r\n")); + + QVERIFY(client->write("goodbye\r\n")); + stopwatch.start(); + while (server->bytesAvailable() < 9 && stopwatch.elapsed() < 5000) + QTest::qWait(100); + QVERIFY(server->peek(&c,1) == 1); + QCOMPARE(c, 'g'); + QCOMPARE(server->readAll(), QByteArray("goodbye\r\n")); + client->disconnectFromHost(); + QVERIFY(client->waitForDisconnected(5000)); +} + void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects { // used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265 -- cgit v1.2.3