diff options
-rw-r--r-- | src/network/ssl/qsslserver.cpp | 55 | ||||
-rw-r--r-- | src/network/ssl/qsslserver.h | 3 | ||||
-rw-r--r-- | src/network/ssl/qsslserver_p.h | 12 | ||||
-rw-r--r-- | tests/auto/network/ssl/qsslserver/tst_qsslserver.cpp | 28 |
4 files changed, 95 insertions, 3 deletions
diff --git a/src/network/ssl/qsslserver.cpp b/src/network/ssl/qsslserver.cpp index 1611046d0e..2ec42bf3ce 100644 --- a/src/network/ssl/qsslserver.cpp +++ b/src/network/ssl/qsslserver.cpp @@ -228,6 +228,42 @@ QSslConfiguration QSslServer::sslConfiguration() const } /*! + Sets the \a timeout to use for all incoming handshakes, in milliseconds. + + This is relevant in the scenario where a client, whether malicious or + accidental, connects to the server but makes no attempt at communicating or + initiating a handshake. QSslServer will then automatically end the + connection after \a timeout milliseconds have elapsed. + + By default the timeout is 5000 milliseconds (5 seconds). + + \note The underlying TLS framework may have their own timeout logic now or + in the future, this function does not affect that. + + \note The \a timeout passed to this function will only apply to \e{new} + connections. If a client is already connected it will use the timeout which + was set when it connected. + + \sa handshakeTimeout() +*/ +void QSslServer::setHandshakeTimeout(int timeout) +{ + Q_D(QSslServer); + d->handshakeTimeout = timeout; +} + +/*! + Returns the currently configured handshake timeout. + + \sa setHandshakeTimeout() +*/ +int QSslServer::handshakeTimeout() const +{ + const Q_D(QSslServer); + return d->handshakeTimeout; +} + +/*! Called when a new connection is established. Converts \a socket to a QSslSocket. @@ -296,7 +332,11 @@ void QSslServerPrivate::initializeHandshakeProcess(QSslSocket *socket) // QObject dtor, but we only use the pointer value! removeSocketData(quintptr(obj)); }); - socketData.emplace(quintptr(socket), readyRead, destroyed); + auto it = socketData.emplace(quintptr(socket), readyRead, destroyed, std::make_shared<QTimer>()); + it->timeoutTimer->setSingleShot(true); + it->timeoutTimer->callOnTimeout([this, socket]() { handleHandshakeTimedOut(socket); }); + it->timeoutTimer->setInterval(handshakeTimeout); + it->timeoutTimer->start(); } // This function may be called while in the socket's QObject dtor, __never__ use @@ -335,8 +375,21 @@ void QSslServerPrivate::checkClientHelloAndContinue() return; } + // Be nice and restart the timeout timer since some progress was made + if (foundData) + it->timeoutTimer->start(); + socket->startServerEncryption(); Q_EMIT q->startedEncryptionHandshake(socket); } +void QSslServerPrivate::handleHandshakeTimedOut(QSslSocket *socket) +{ + Q_Q(QSslServer); + removeSocketData(quintptr(socket)); + socket->disconnectFromHost(); + Q_EMIT q->errorOccurred(socket, QAbstractSocket::SocketTimeoutError); + socket->deleteLater(); +} + QT_END_NAMESPACE diff --git a/src/network/ssl/qsslserver.h b/src/network/ssl/qsslserver.h index d2f3abc456..aaa0f43c35 100644 --- a/src/network/ssl/qsslserver.h +++ b/src/network/ssl/qsslserver.h @@ -33,6 +33,9 @@ public: void setSslConfiguration(const QSslConfiguration &sslConfiguration); QSslConfiguration sslConfiguration() const; + void setHandshakeTimeout(int timeout); + int handshakeTimeout() const; + Q_SIGNALS: void sslErrors(QSslSocket *socket, const QList<QSslError> &errors); void peerVerifyError(QSslSocket *socket, const QSslError &error); diff --git a/src/network/ssl/qsslserver_p.h b/src/network/ssl/qsslserver_p.h index 3c7cce0355..71359f6cff 100644 --- a/src/network/ssl/qsslserver_p.h +++ b/src/network/ssl/qsslserver_p.h @@ -19,6 +19,7 @@ #include <QtNetwork/private/qtnetworkglobal_p.h> #include <QtCore/qhash.h> +#include <QtCore/qtimer.h> #include <QtNetwork/QSslConfiguration> #include <QtNetwork/private/qtcpserver_p.h> @@ -28,6 +29,7 @@ QT_BEGIN_NAMESPACE class Q_NETWORK_EXPORT QSslServerPrivate : public QTcpServerPrivate { + static constexpr int DefaultHandshakeTimeout = 5'000; // 5 seconds public: Q_DECLARE_PUBLIC(QSslServer) @@ -35,13 +37,18 @@ public: void checkClientHelloAndContinue(); void initializeHandshakeProcess(QSslSocket *socket); void removeSocketData(quintptr socket); + void handleHandshakeTimedOut(QSslSocket *socket); struct SocketData { QMetaObject::Connection readyReadConnection; QMetaObject::Connection destroyedConnection; + std::shared_ptr<QTimer> timeoutTimer; // shared_ptr because QHash demands copying - SocketData(QMetaObject::Connection readyRead, QMetaObject::Connection destroyed) - : readyReadConnection(readyRead), destroyedConnection(destroyed) + SocketData(QMetaObject::Connection readyRead, QMetaObject::Connection destroyed, + std::shared_ptr<QTimer> &&timer) + : readyReadConnection(readyRead), + destroyedConnection(destroyed), + timeoutTimer(std::move(timer)) { } @@ -54,6 +61,7 @@ public: QHash<quintptr, SocketData> socketData; QSslConfiguration sslConfiguration; + int handshakeTimeout = DefaultHandshakeTimeout; }; diff --git a/tests/auto/network/ssl/qsslserver/tst_qsslserver.cpp b/tests/auto/network/ssl/qsslserver/tst_qsslserver.cpp index fb8a74d8de..088f0170f4 100644 --- a/tests/auto/network/ssl/qsslserver/tst_qsslserver.cpp +++ b/tests/auto/network/ssl/qsslserver/tst_qsslserver.cpp @@ -24,6 +24,7 @@ private slots: void testPreSharedKeyAuthenticationRequired(); #endif void plaintextClient(); + void quietClient(); private: QString testDataDir; @@ -459,6 +460,33 @@ void tst_QSslServer::plaintextClient() QCOMPARE(socket.state(), QAbstractSocket::SocketState::UnconnectedState); } +void tst_QSslServer::quietClient() +{ + QSslConfiguration serverConfiguration = selfSignedServerQSslConfiguration(); + SslServerSpy server(serverConfiguration); + server.server.setHandshakeTimeout(1'000); + QVERIFY(server.server.listen()); + + quint16 serverPeerPort = 0; + auto grabServerPeerPort = [&serverPeerPort](QSslSocket *socket) { + serverPeerPort = socket->peerPort(); + }; + QObject::connect(&server.server, &QSslServer::errorOccurred, &server.server, + grabServerPeerPort); + + QTcpSocket socket; + QSignalSpy socketDisconnectedSpy(&socket, &QTcpSocket::disconnected); + socket.connectToHost(QHostAddress::LocalHost, server.server.serverPort()); + quint16 clientLocalPort = socket.localPort(); + QVERIFY(socket.waitForConnected()); + // Disconnects after overlong break: + QVERIFY(socketDisconnectedSpy.wait(5'000)); + QCOMPARE(socket.state(), QAbstractSocket::SocketState::UnconnectedState); + + QCOMPARE_GT(server.errorOccurredSpy.size(), 0); + QCOMPARE(serverPeerPort, clientLocalPort); +} + QTEST_MAIN(tst_QSslServer) #include "tst_qsslserver.moc" |