diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | examples/websockets/simplechat/chatclient.html | 15 | ||||
-rw-r--r-- | examples/websockets/simplechat/chatserver.cpp | 40 | ||||
-rw-r--r-- | examples/websockets/simplechat/chatserver.h | 8 | ||||
-rw-r--r-- | examples/websockets/simplechat/main.cpp | 1 | ||||
-rw-r--r-- | src/imports/qmlwebsockets/qqmlwebsocket.h | 2 | ||||
-rw-r--r-- | src/websockets/qdefaultmaskgenerator_p.cpp | 18 | ||||
-rw-r--r-- | src/websockets/qmaskgenerator.cpp | 2 | ||||
-rw-r--r-- | src/websockets/qwebsocket.cpp | 4 | ||||
-rw-r--r-- | src/websockets/qwebsocket_p.cpp | 2 | ||||
-rw-r--r-- | src/websockets/qwebsocketserver.h | 3 | ||||
-rw-r--r-- | tests/auto/qml/qml.pro | 3 | ||||
-rw-r--r-- | tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro | 1 | ||||
-rw-r--r-- | tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp | 57 | ||||
-rw-r--r-- | tests/auto/websockets/websockets.pro | 3 |
15 files changed, 114 insertions, 47 deletions
diff --git a/.qmake.conf b/.qmake.conf index e795316..08b8470 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -3,4 +3,4 @@ load(qt_build_config) CONFIG += warning_clean DEFINES += QT_NO_FOREACH -MODULE_VERSION = 5.9.3 +MODULE_VERSION = 5.10.0 diff --git a/examples/websockets/simplechat/chatclient.html b/examples/websockets/simplechat/chatclient.html index 511d05b..d2dbf47 100644 --- a/examples/websockets/simplechat/chatclient.html +++ b/examples/websockets/simplechat/chatclient.html @@ -5,17 +5,20 @@ <body> <h1>WebSocket Chat Client</h1> <p> + Host: <input id="webSocketHost" type="text" value="localhost:1234"/> + </p> + <p> <button onClick="initWebSocket();">Connect</button> - <button onClick="stopWebSocket();">Disconnect</button> + <button id="disconnectButton" onClick="stopWebSocket();" disabled>Disconnect</button> <button onClick="checkSocket();">State</button> </p> <p> - <textarea id="debugTextArea" style="width:400px;height:200px;"></textarea> + <textarea id="debugTextArea" style="width:400px;height:200px;" readonly></textarea> </p> <p> <input type="text" id="inputNick" value="nickname" /> <input type="text" id="inputText" onkeydown="if(event.keyCode==13)sendMessage();"/> - <button onClick="sendMessage();">Send</button> + <button id="sendButton" onClick="sendMessage();" disabled>Send</button> </p> <script type="text/javascript"> @@ -38,7 +41,6 @@ } } - var wsUri = "ws://localhost:1234"; var websocket = null; function initWebSocket() { @@ -47,12 +49,17 @@ WebSocket = MozWebSocket; if ( websocket && websocket.readyState == 1 ) websocket.close(); + var wsUri = "ws://" + document.getElementById("webSocketHost").value; websocket = new WebSocket( wsUri ); websocket.onopen = function (evt) { debug("CONNECTED"); + document.getElementById("disconnectButton").disabled = false; + document.getElementById("sendButton").disabled = false; }; websocket.onclose = function (evt) { debug("DISCONNECTED"); + document.getElementById("disconnectButton").disabled = true; + document.getElementById("sendButton").disabled = true; }; websocket.onmessage = function (evt) { console.log( "Message received :", evt.data ); diff --git a/examples/websockets/simplechat/chatserver.cpp b/examples/websockets/simplechat/chatserver.cpp index ae207f5..8885fe8 100644 --- a/examples/websockets/simplechat/chatserver.cpp +++ b/examples/websockets/simplechat/chatserver.cpp @@ -48,23 +48,31 @@ ** ****************************************************************************/ #include "chatserver.h" -#include "QtWebSockets/QWebSocketServer" -#include "QtWebSockets/QWebSocket" -#include <QtCore/QDebug> + +#include <QtWebSockets> +#include <QtCore> + +#include <cstdio> +using namespace std; QT_USE_NAMESPACE +static QString getIdentifier(QWebSocket *peer) +{ + return QStringLiteral("%1:%2").arg(peer->peerAddress().toString(), + QString::number(peer->peerPort())); +} + //! [constructor] ChatServer::ChatServer(quint16 port, QObject *parent) : QObject(parent), - m_pWebSocketServer(Q_NULLPTR) + m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Chat Server"), + QWebSocketServer::NonSecureMode, + this)) { - m_pWebSocketServer = new QWebSocketServer(QStringLiteral("Chat Server"), - QWebSocketServer::NonSecureMode, - this); if (m_pWebSocketServer->listen(QHostAddress::Any, port)) { - qDebug() << "Chat Server listening on port" << port; + QTextStream(stdout) << "Chat Server listening on port " << port << '\n'; connect(m_pWebSocketServer, &QWebSocketServer::newConnection, this, &ChatServer::onNewConnection); } @@ -73,31 +81,32 @@ ChatServer::ChatServer(quint16 port, QObject *parent) : ChatServer::~ChatServer() { m_pWebSocketServer->close(); - qDeleteAll(m_clients.begin(), m_clients.end()); } //! [constructor] //! [onNewConnection] void ChatServer::onNewConnection() { - QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection(); + auto pSocket = m_pWebSocketServer->nextPendingConnection(); + QTextStream(stdout) << getIdentifier(pSocket) << " connected!\n"; + pSocket->setParent(this); - connect(pSocket, &QWebSocket::textMessageReceived, this, &ChatServer::processMessage); - connect(pSocket, &QWebSocket::disconnected, this, &ChatServer::socketDisconnected); + connect(pSocket, &QWebSocket::textMessageReceived, + this, &ChatServer::processMessage); + connect(pSocket, &QWebSocket::disconnected, + this, &ChatServer::socketDisconnected); m_clients << pSocket; } //! [onNewConnection] //! [processMessage] -void ChatServer::processMessage(QString message) +void ChatServer::processMessage(const QString &message) { QWebSocket *pSender = qobject_cast<QWebSocket *>(sender()); for (QWebSocket *pClient : qAsConst(m_clients)) { if (pClient != pSender) //don't echo message back to sender - { pClient->sendTextMessage(message); - } } } //! [processMessage] @@ -106,6 +115,7 @@ void ChatServer::processMessage(QString message) void ChatServer::socketDisconnected() { QWebSocket *pClient = qobject_cast<QWebSocket *>(sender()); + QTextStream(stdout) << getIdentifier(pClient) << " disconnected!\n"; if (pClient) { m_clients.removeAll(pClient); diff --git a/examples/websockets/simplechat/chatserver.h b/examples/websockets/simplechat/chatserver.h index 4c652a3..4a8285b 100644 --- a/examples/websockets/simplechat/chatserver.h +++ b/examples/websockets/simplechat/chatserver.h @@ -52,21 +52,21 @@ #include <QtCore/QObject> #include <QtCore/QList> -#include <QtCore/QByteArray> QT_FORWARD_DECLARE_CLASS(QWebSocketServer) QT_FORWARD_DECLARE_CLASS(QWebSocket) +QT_FORWARD_DECLARE_CLASS(QString) class ChatServer : public QObject { Q_OBJECT public: - explicit ChatServer(quint16 port, QObject *parent = Q_NULLPTR); + explicit ChatServer(quint16 port, QObject *parent = nullptr); virtual ~ChatServer(); -private Q_SLOTS: +private slots: void onNewConnection(); - void processMessage(QString message); + void processMessage(const QString &message); void socketDisconnected(); private: diff --git a/examples/websockets/simplechat/main.cpp b/examples/websockets/simplechat/main.cpp index 0b1c753..a1ffb49 100644 --- a/examples/websockets/simplechat/main.cpp +++ b/examples/websockets/simplechat/main.cpp @@ -48,6 +48,7 @@ ** ****************************************************************************/ #include <QtCore/QCoreApplication> + #include "chatserver.h" int main(int argc, char *argv[]) diff --git a/src/imports/qmlwebsockets/qqmlwebsocket.h b/src/imports/qmlwebsockets/qqmlwebsocket.h index c3a808f..8db435d 100644 --- a/src/imports/qmlwebsockets/qqmlwebsocket.h +++ b/src/imports/qmlwebsockets/qqmlwebsocket.h @@ -54,7 +54,6 @@ class QQmlWebSocket : public QObject, public QQmlParserStatus Q_DISABLE_COPY(QQmlWebSocket) Q_INTERFACES(QQmlParserStatus) - Q_ENUMS(Status) Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged) @@ -73,6 +72,7 @@ public: Closed = 3, Error = 4 }; + Q_ENUM(Status) QUrl url() const; void setUrl(const QUrl &url); diff --git a/src/websockets/qdefaultmaskgenerator_p.cpp b/src/websockets/qdefaultmaskgenerator_p.cpp index 1035e8f..7dc0cee 100644 --- a/src/websockets/qdefaultmaskgenerator_p.cpp +++ b/src/websockets/qdefaultmaskgenerator_p.cpp @@ -48,7 +48,7 @@ malicious scripts to attack bad behaving proxies. For more information about the importance of good masking, see \l {"Talking to Yourself for Fun and Profit" by Lin-Shung Huang et al}. - The default mask generator uses the cryptographically insecure qrand() function. + The default mask generator uses the reasonably secure QRandomGenerator::get32() function. The best measure against attacks mentioned in the document above, is to use QWebSocket over a secure connection (\e wss://). In general, always be careful to not have 3rd party script access to @@ -58,8 +58,7 @@ */ #include "qdefaultmaskgenerator_p.h" -#include <QDateTime> -#include <limits> +#include <QRandomGenerator> QT_BEGIN_NAMESPACE @@ -83,25 +82,26 @@ QDefaultMaskGenerator::~QDefaultMaskGenerator() } /*! - Seeds the QDefaultMaskGenerator using qsrand(). - When seed() is not called, no seed is used at all. - \internal */ bool QDefaultMaskGenerator::seed() Q_DECL_NOEXCEPT { - qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch())); return true; } /*! - Generates a new random mask using the insecure qrand() method. + Generates a new random mask using the insecure QRandomGenerator::get32() method. \internal */ quint32 QDefaultMaskGenerator::nextMask() Q_DECL_NOEXCEPT { - return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max()); + quint32 value = QRandomGenerator::get32(); + while (Q_UNLIKELY(value == 0)) { + // a mask of zero has a special meaning + value = QRandomGenerator::get32(); + } + return value; } QT_END_NAMESPACE diff --git a/src/websockets/qmaskgenerator.cpp b/src/websockets/qmaskgenerator.cpp index 064ada2..56d1223 100644 --- a/src/websockets/qmaskgenerator.cpp +++ b/src/websockets/qmaskgenerator.cpp @@ -50,7 +50,7 @@ malicious scripts from attacking badly behaving proxies. For more information about the importance of good masking, see \l {"Talking to Yourself for Fun and Profit" by Lin-Shung Huang et al}. - By default QWebSocket uses the cryptographically insecure qrand() function. + By default QWebSocket uses the reasonably secure QRandomGenerator::get32() function. The best measure against attacks mentioned in the document above, is to use QWebSocket over a secure connection (\e wss://). In general, always be careful to not have 3rd party script access to diff --git a/src/websockets/qwebsocket.cpp b/src/websockets/qwebsocket.cpp index ba343e4..30bb39d 100644 --- a/src/websockets/qwebsocket.cpp +++ b/src/websockets/qwebsocket.cpp @@ -63,8 +63,8 @@ In that case, non-secure WebSocket connections fail. The best way to mitigate against this problem is to use WebSocket over a secure connection. - \warning To generate masks, this implementation of WebSockets uses the cryptographically - insecure qrand() function. + \warning To generate masks, this implementation of WebSockets uses the reasonably + secure QRandomGenerator::get32() function. For more information about the importance of good masking, see \l {"Talking to Yourself for Fun and Profit" by Lin-Shung Huang et al}. The best measure against attacks mentioned in the document above, diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp index bee2afa..1d23c84 100644 --- a/src/websockets/qwebsocket_p.cpp +++ b/src/websockets/qwebsocket_p.cpp @@ -1014,7 +1014,7 @@ void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) if (!ok) errorDescription = QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.") - .arg(acceptKey).arg(accept); + .arg(acceptKey, accept); } else { errorDescription = QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.") diff --git a/src/websockets/qwebsocketserver.h b/src/websockets/qwebsocketserver.h index f846290..9dc286b 100644 --- a/src/websockets/qwebsocketserver.h +++ b/src/websockets/qwebsocketserver.h @@ -65,8 +65,6 @@ class Q_WEBSOCKETS_EXPORT QWebSocketServer : public QObject Q_DISABLE_COPY(QWebSocketServer) Q_DECLARE_PRIVATE(QWebSocketServer) - Q_ENUMS(SslMode) - public: enum SslMode { #ifndef QT_NO_SSL @@ -74,6 +72,7 @@ public: #endif NonSecureMode = 1 }; + Q_ENUM(SslMode) explicit QWebSocketServer(const QString &serverName, SslMode secureMode, QObject *parent = Q_NULLPTR); diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 2d2fde4..8951c55 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -1,6 +1,3 @@ TEMPLATE = subdirs !uikit: SUBDIRS += qmlwebsockets qmlwebsockets_compat - -# QTBUG-60268 -boot2qt: SUBDIRS -= qmlwebsockets_compat diff --git a/tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro b/tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro index e166f50..178fd88 100644 --- a/tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro +++ b/tests/auto/websockets/qwebsocketserver/qwebsocketserver.pro @@ -12,3 +12,4 @@ SOURCES += tst_qwebsocketserver.cpp RESOURCES += $$PWD/../shared/qwebsocketshared.qrc +boot2qt: DEFINES += SHOULD_CHECK_SYSCALL_SUPPORT diff --git a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp index b7734a4..8a3760d 100644 --- a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp +++ b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp @@ -112,9 +112,15 @@ private Q_SLOTS: void tst_serverDestroyedWhileSocketConnected(); void tst_scheme(); // qtbug-55927 void tst_handleConnection(); + +private: + bool m_shouldSkipUnsupportedIpv6Test; +#ifdef SHOULD_CHECK_SYSCALL_SUPPORT + bool ipv6GetsockoptionMissing(int level, int optname); +#endif }; -tst_QWebSocketServer::tst_QWebSocketServer() +tst_QWebSocketServer::tst_QWebSocketServer() : m_shouldSkipUnsupportedIpv6Test(false) { } @@ -132,8 +138,42 @@ void tst_QWebSocketServer::init() #endif } +#ifdef SHOULD_CHECK_SYSCALL_SUPPORT +#include <netinet/in.h> +#include <sys/socket.h> +#include <errno.h> +#include <unistd.h> + +bool tst_QWebSocketServer::ipv6GetsockoptionMissing(int level, int optname) +{ + int testSocket; + + testSocket = socket(PF_INET6, SOCK_STREAM, 0); + + // If we can't test here, assume it's not missing + if (testSocket == -1) + return false; + + bool result = false; + if (getsockopt(testSocket, level, optname, nullptr, 0) == -1) { + if (errno == EOPNOTSUPP) { + result = true; + } + } + + close(testSocket); + return result; +} + +#endif //SHOULD_CHECK_SYSCALL_SUPPORT + void tst_QWebSocketServer::initTestCase() { +#ifdef SHOULD_CHECK_SYSCALL_SUPPORT + // Qemu does not have required support for IPV6 socket options. + // If this is detected, skip the test + m_shouldSkipUnsupportedIpv6Test = ipv6GetsockoptionMissing(SOL_IPV6, IPV6_V6ONLY); +#endif } void tst_QWebSocketServer::cleanupTestCase() @@ -283,6 +323,9 @@ void tst_QWebSocketServer::tst_listening() void tst_QWebSocketServer::tst_connectivity() { + if (m_shouldSkipUnsupportedIpv6Test) + QSKIP("Syscalls needed for ipv6 sockoptions missing functionality"); + QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode); QSignalSpy serverConnectionSpy(&server, SIGNAL(newConnection())); QSignalSpy serverErrorSpy(&server, @@ -323,6 +366,9 @@ void tst_QWebSocketServer::tst_connectivity() void tst_QWebSocketServer::tst_preSharedKey() { + if (m_shouldSkipUnsupportedIpv6Test) + QSKIP("Syscalls needed for ipv6 sockoptions missing functionality"); + #ifndef QT_NO_OPENSSL QWebSocketServer server(QString(), QWebSocketServer::SecureMode); @@ -399,6 +445,9 @@ void tst_QWebSocketServer::tst_preSharedKey() void tst_QWebSocketServer::tst_maxPendingConnections() { + if (m_shouldSkipUnsupportedIpv6Test) + QSKIP("Syscalls needed for ipv6 sockoptions missing functionality"); + //tests if maximum connections are respected //also checks if there are no side-effects like signals that are unexpectedly thrown QWebSocketServer server(QString(), QWebSocketServer::NonSecureMode); @@ -474,6 +523,9 @@ void tst_QWebSocketServer::tst_maxPendingConnections() void tst_QWebSocketServer::tst_serverDestroyedWhileSocketConnected() { + if (m_shouldSkipUnsupportedIpv6Test) + QSKIP("Syscalls needed for ipv6 sockoptions missing functionality"); + QWebSocketServer * server = new QWebSocketServer(QString(), QWebSocketServer::NonSecureMode); QSignalSpy serverConnectionSpy(server, SIGNAL(newConnection())); QSignalSpy corsAuthenticationSpy(server, @@ -505,6 +557,9 @@ void tst_QWebSocketServer::tst_serverDestroyedWhileSocketConnected() void tst_QWebSocketServer::tst_scheme() { + if (m_shouldSkipUnsupportedIpv6Test) + QSKIP("Syscalls needed for ipv6 sockoptions missing functionality"); + QWebSocketServer plainServer(QString(), QWebSocketServer::NonSecureMode); QSignalSpy plainServerConnectionSpy(&plainServer, SIGNAL(newConnection())); diff --git a/tests/auto/websockets/websockets.pro b/tests/auto/websockets/websockets.pro index 4b7ee4e..b000229 100644 --- a/tests/auto/websockets/websockets.pro +++ b/tests/auto/websockets/websockets.pro @@ -14,6 +14,3 @@ qtConfig(private_tests): SUBDIRS += \ SUBDIRS += \ qwebsocket \ qwebsocketserver - -# QTBUG-60268 -boot2qt: SUBDIRS -= qwebsocketserver |