diff options
-rw-r--r-- | src/corelib/io/qiodevice_p.h | 5 | ||||
-rw-r--r-- | src/network/ssl/qsslsocket.cpp | 38 | ||||
-rw-r--r-- | tests/auto/qsslsocket/tst_qsslsocket.cpp | 132 |
3 files changed, 163 insertions, 12 deletions
diff --git a/src/corelib/io/qiodevice_p.h b/src/corelib/io/qiodevice_p.h index 881d859..c6b27b1 100644 --- a/src/corelib/io/qiodevice_p.h +++ b/src/corelib/io/qiodevice_p.h @@ -110,6 +110,11 @@ public: first += r; return r; } + int peek(char* target, int size) { + int r = qMin(size, len); + memcpy(target, first, r); + return r; + } char* reserve(int size) { makeSpace(size + len, freeSpaceAtEnd); char* writePtr = first + len; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 7fc36c8..2b809a7 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -2249,15 +2249,24 @@ void QSslSocketPrivate::_q_flushReadBuffer() qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize) { if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) { - if (plainSocket) - return plainSocket->peek(data, maxSize); - else + //unencrypted mode - do not use QIODevice::peek, as it reads ahead data from the plain socket + //peek at data already in the QIODevice buffer (from a previous read) + qint64 r = buffer.peek(data, maxSize); + if (r == maxSize) + return r; + data += r; + //peek at data in the plain socket + if (plainSocket) { + qint64 r2 = plainSocket->peek(data, maxSize - r); + if (r2 < 0) + return (r > 0 ? r : r2); + return r + r2; + } else { return -1; + } } else { - QByteArray tmp; - tmp = readBuffer.peek(maxSize); - memcpy(data, tmp.data(), tmp.length()); - return tmp.length(); + //encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer + return QTcpSocketPrivate::peek(data, maxSize); } } @@ -2267,14 +2276,21 @@ qint64 QSslSocketPrivate::peek(char *data, qint64 maxSize) QByteArray QSslSocketPrivate::peek(qint64 maxSize) { if (mode == QSslSocket::UnencryptedMode && !autoStartHandshake) { + //unencrypted mode - do not use QIODevice::peek, as it reads ahead data from the plain socket + //peek at data already in the QIODevice buffer (from a previous read) + QByteArray ret; + ret.reserve(maxSize); + ret.resize(buffer.peek(ret.data(), maxSize)); + if (ret.length() == maxSize) + return ret; + //peek at data in the plain socket if (plainSocket) - return plainSocket->peek(maxSize); + return ret + plainSocket->peek(maxSize - ret.length()); else return QByteArray(); } else { - QByteArray tmp; - tmp = readBuffer.peek(maxSize); - return tmp; + //encrypted mode - the socket engine will read and decrypt data into the QIODevice buffer + return QTcpSocketPrivate::peek(maxSize); } } diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp index cf4d04e..1c43095 100644 --- a/tests/auto/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -191,6 +191,7 @@ private slots: void writeBigChunk(); void blacklistedCertificates(); void qtbug18498_peek(); + void qtbug18498_peek2(); void setEmptyDefaultConfiguration(); static void exitLoop() @@ -2119,7 +2120,7 @@ void tst_QSslSocket::qtbug18498_peek() QVERIFY(server.listen(QHostAddress::LocalHost)); client->connectToHost("127.0.0.1", server.serverPort()); QVERIFY(client->waitForConnected(5000)); - QVERIFY(server.waitForNewConnection(0)); + QVERIFY(server.waitForNewConnection(1000)); client->setObjectName("client"); client->ignoreSslErrors(); @@ -2159,6 +2160,135 @@ void tst_QSslSocket::qtbug18498_peek() QCOMPARE(read_data, data); } +class SslServer5 : public QTcpServer +{ + Q_OBJECT +public: + SslServer5() : socket(0) {} + QSslSocket *socket; + +protected: + void incomingConnection(int 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<QSslSocket> client(new QSslSocket); + client->connectToHost(QHostAddress::LocalHost, listener.serverPort()); + QVERIFY(client->waitForConnected(5000)); + QVERIFY(listener.waitForNewConnection(1000)); + + QScopedPointer<QSslSocket> 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<QSslCertificate> 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() { // used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265 |