1/****************************************************************************
2**
3** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWebSockets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qwebsocketserver.h"
41#include "qwebsocketserver_p.h"
42#ifndef QT_NO_SSL
43#include "qsslserver_p.h"
44#endif
45#include "qwebsocketprotocol.h"
46#include "qwebsockethandshakerequest_p.h"
47#include "qwebsockethandshakeresponse_p.h"
48#include "qwebsocket.h"
49#include "qwebsocket_p.h"
50#include "qwebsocketcorsauthenticator.h"
51
52#include <QtCore/QTimer>
53#include <QtNetwork/QTcpServer>
54#include <QtNetwork/QTcpSocket>
55#include <QtNetwork/QNetworkProxy>
56
57QT_BEGIN_NAMESPACE
58
59//both constants are taken from the default settings of Apache
60//see: http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfieldsize and
61//http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfields
62const int MAX_HEADERLINE_LENGTH = 8 * 1024; //maximum length of a http request header line
63const int MAX_HEADERLINES = 100; //maximum number of http request header lines
64
65/*!
66 \internal
67 */
68QWebSocketServerPrivate::QWebSocketServerPrivate(const QString &serverName,
69 QWebSocketServerPrivate::SslMode secureMode) :
70 QObjectPrivate(),
71 m_pTcpServer(nullptr),
72 m_serverName(serverName),
73 m_secureMode(secureMode),
74 m_pendingConnections(),
75 m_error(QWebSocketProtocol::CloseCodeNormal),
76 m_errorString(),
77 m_maxPendingConnections(30),
78 m_handshakeTimeout(10000)
79{}
80
81/*!
82 \internal
83 */
84void QWebSocketServerPrivate::init()
85{
86 Q_Q(QWebSocketServer);
87 if (m_secureMode == NonSecureMode) {
88 m_pTcpServer = new QTcpServer(q);
89 if (Q_LIKELY(m_pTcpServer))
90 QObjectPrivate::connect(sender: m_pTcpServer, signal: &QTcpServer::newConnection,
91 receiverPrivate: this, slot: &QWebSocketServerPrivate::onNewConnection);
92 else
93 qFatal(msg: "Could not allocate memory for tcp server.");
94 } else {
95#ifndef QT_NO_SSL
96 QSslServer *pSslServer = new QSslServer(q);
97 m_pTcpServer = pSslServer;
98 if (Q_LIKELY(m_pTcpServer)) {
99 QObjectPrivate::connect(sender: pSslServer, signal: &QSslServer::newEncryptedConnection,
100 receiverPrivate: this, slot: &QWebSocketServerPrivate::onNewConnection,
101 type: Qt::QueuedConnection);
102 QObjectPrivate::connect(sender: pSslServer, signal: &QSslServer::startedEncryptionHandshake,
103 receiverPrivate: this, slot: &QWebSocketServerPrivate::startHandshakeTimeout);
104 QObject::connect(sender: pSslServer, signal: &QSslServer::peerVerifyError,
105 receiver: q, slot: &QWebSocketServer::peerVerifyError);
106 QObject::connect(sender: pSslServer, signal: &QSslServer::sslErrors,
107 receiver: q, slot: &QWebSocketServer::sslErrors);
108 QObject::connect(sender: pSslServer, signal: &QSslServer::preSharedKeyAuthenticationRequired,
109 receiver: q, slot: &QWebSocketServer::preSharedKeyAuthenticationRequired);
110 }
111#else
112 qFatal("SSL not supported on this platform.");
113#endif
114 }
115 QObject::connect(sender: m_pTcpServer, signal: &QTcpServer::acceptError, receiver: q, slot: &QWebSocketServer::acceptError);
116}
117
118/*!
119 \internal
120 */
121QWebSocketServerPrivate::~QWebSocketServerPrivate()
122{
123}
124
125/*!
126 \internal
127 */
128void QWebSocketServerPrivate::close(bool aboutToDestroy)
129{
130 Q_Q(QWebSocketServer);
131 m_pTcpServer->close();
132 while (!m_pendingConnections.isEmpty()) {
133 QWebSocket *pWebSocket = m_pendingConnections.dequeue();
134 pWebSocket->close(closeCode: QWebSocketProtocol::CloseCodeGoingAway,
135 reason: QWebSocketServer::tr(s: "Server closed."));
136 pWebSocket->deleteLater();
137 }
138 if (!aboutToDestroy) {
139 //emit signal via the event queue, so the server gets time
140 //to process any hanging events, like flushing buffers aso
141 QMetaObject::invokeMethod(obj: q, member: "closed", type: Qt::QueuedConnection);
142 }
143}
144
145/*!
146 \internal
147 */
148QString QWebSocketServerPrivate::errorString() const
149{
150 if (m_errorString.isEmpty())
151 return m_pTcpServer->errorString();
152 else
153 return m_errorString;
154}
155
156/*!
157 \internal
158 */
159bool QWebSocketServerPrivate::hasPendingConnections() const
160{
161 return !m_pendingConnections.isEmpty();
162}
163
164/*!
165 \internal
166 */
167bool QWebSocketServerPrivate::isListening() const
168{
169 return m_pTcpServer->isListening();
170}
171
172/*!
173 \internal
174 */
175bool QWebSocketServerPrivate::listen(const QHostAddress &address, quint16 port)
176{
177 bool success = m_pTcpServer->listen(address, port);
178 if (!success)
179 setErrorFromSocketError(error: m_pTcpServer->serverError(), errorDescription: m_pTcpServer->errorString());
180 return success;
181}
182
183/*!
184 \internal
185 */
186int QWebSocketServerPrivate::maxPendingConnections() const
187{
188 return m_maxPendingConnections;
189}
190
191/*!
192 \internal
193 */
194void QWebSocketServerPrivate::addPendingConnection(QWebSocket *pWebSocket)
195{
196 if (m_pendingConnections.size() < maxPendingConnections())
197 m_pendingConnections.enqueue(t: pWebSocket);
198}
199
200/*!
201 \internal
202 */
203void QWebSocketServerPrivate::setErrorFromSocketError(QAbstractSocket::SocketError error,
204 const QString &errorDescription)
205{
206 Q_UNUSED(error);
207 setError(code: QWebSocketProtocol::CloseCodeAbnormalDisconnection, errorString: errorDescription);
208}
209
210/*!
211 \internal
212 */
213QWebSocket *QWebSocketServerPrivate::nextPendingConnection()
214{
215 QWebSocket *pWebSocket = nullptr;
216 if (Q_LIKELY(!m_pendingConnections.isEmpty()))
217 pWebSocket = m_pendingConnections.dequeue();
218 return pWebSocket;
219}
220
221/*!
222 \internal
223 */
224void QWebSocketServerPrivate::pauseAccepting()
225{
226 m_pTcpServer->pauseAccepting();
227}
228
229#ifndef QT_NO_NETWORKPROXY
230/*!
231 \internal
232 */
233QNetworkProxy QWebSocketServerPrivate::proxy() const
234{
235 return m_pTcpServer->proxy();
236}
237
238/*!
239 \internal
240 */
241void QWebSocketServerPrivate::setProxy(const QNetworkProxy &networkProxy)
242{
243 m_pTcpServer->setProxy(networkProxy);
244}
245#endif
246/*!
247 \internal
248 */
249void QWebSocketServerPrivate::resumeAccepting()
250{
251 m_pTcpServer->resumeAccepting();
252}
253
254/*!
255 \internal
256 */
257QHostAddress QWebSocketServerPrivate::serverAddress() const
258{
259 return m_pTcpServer->serverAddress();
260}
261
262/*!
263 \internal
264 */
265QWebSocketProtocol::CloseCode QWebSocketServerPrivate::serverError() const
266{
267 return m_error;
268}
269
270/*!
271 \internal
272 */
273quint16 QWebSocketServerPrivate::serverPort() const
274{
275 return m_pTcpServer->serverPort();
276}
277
278/*!
279 \internal
280 */
281void QWebSocketServerPrivate::setMaxPendingConnections(int numConnections)
282{
283 if (m_pTcpServer->maxPendingConnections() <= numConnections)
284 m_pTcpServer->setMaxPendingConnections(numConnections + 1);
285 m_maxPendingConnections = numConnections;
286}
287
288/*!
289 \internal
290 */
291bool QWebSocketServerPrivate::setSocketDescriptor(qintptr socketDescriptor)
292{
293 return m_pTcpServer->setSocketDescriptor(socketDescriptor);
294}
295
296/*!
297 \internal
298 */
299qintptr QWebSocketServerPrivate::socketDescriptor() const
300{
301 return m_pTcpServer->socketDescriptor();
302}
303
304/*!
305 \internal
306 */
307QList<QWebSocketProtocol::Version> QWebSocketServerPrivate::supportedVersions() const
308{
309 QList<QWebSocketProtocol::Version> supportedVersions;
310 supportedVersions << QWebSocketProtocol::currentVersion(); //we only support V13
311 return supportedVersions;
312}
313
314/*!
315 \internal
316 */
317QStringList QWebSocketServerPrivate::supportedProtocols() const
318{
319 QStringList supportedProtocols;
320 return supportedProtocols; //no protocols are currently supported
321}
322
323/*!
324 \internal
325 */
326QStringList QWebSocketServerPrivate::supportedExtensions() const
327{
328 QStringList supportedExtensions;
329 return supportedExtensions; //no extensions are currently supported
330}
331
332/*!
333 \internal
334 */
335void QWebSocketServerPrivate::setServerName(const QString &serverName)
336{
337 if (m_serverName != serverName)
338 m_serverName = serverName;
339}
340
341/*!
342 \internal
343 */
344QString QWebSocketServerPrivate::serverName() const
345{
346 return m_serverName;
347}
348
349/*!
350 \internal
351 */
352QWebSocketServerPrivate::SslMode QWebSocketServerPrivate::secureMode() const
353{
354 return m_secureMode;
355}
356
357#ifndef QT_NO_SSL
358void QWebSocketServerPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
359{
360 if (m_secureMode == SecureMode)
361 qobject_cast<QSslServer *>(object: m_pTcpServer)->setSslConfiguration(sslConfiguration);
362}
363
364QSslConfiguration QWebSocketServerPrivate::sslConfiguration() const
365{
366 if (m_secureMode == SecureMode)
367 return qobject_cast<QSslServer *>(object: m_pTcpServer)->sslConfiguration();
368 else
369 return QSslConfiguration::defaultConfiguration();
370}
371#endif
372
373void QWebSocketServerPrivate::setError(QWebSocketProtocol::CloseCode code, const QString &errorString)
374{
375 if ((m_error != code) || (m_errorString != errorString)) {
376 Q_Q(QWebSocketServer);
377 m_error = code;
378 m_errorString = errorString;
379 Q_EMIT q->serverError(closeCode: code);
380 }
381}
382
383/*!
384 \internal
385 */
386void QWebSocketServerPrivate::onNewConnection()
387{
388 while (m_pTcpServer->hasPendingConnections()) {
389 QTcpSocket *pTcpSocket = m_pTcpServer->nextPendingConnection();
390 if (Q_LIKELY(pTcpSocket) && m_secureMode == NonSecureMode)
391 startHandshakeTimeout(pTcpSocket);
392 handleConnection(pTcpSocket);
393 }
394}
395
396/*!
397 \internal
398 */
399void QWebSocketServerPrivate::onSocketDisconnected()
400{
401 Q_Q(QWebSocketServer);
402 QObject *sender = q->sender();
403 if (Q_LIKELY(sender)) {
404 QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(object: sender);
405 if (Q_LIKELY(pTcpSocket))
406 pTcpSocket->deleteLater();
407 }
408}
409
410/*!
411 \internal
412 */
413void QWebSocketServerPrivate::handshakeReceived()
414{
415 Q_Q(QWebSocketServer);
416 QObject *sender = q->sender();
417 if (Q_UNLIKELY(!sender)) {
418 return;
419 }
420 QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(object: sender);
421 if (Q_UNLIKELY(!pTcpSocket)) {
422 return;
423 }
424 //When using Google Chrome the handshake in received in two parts.
425 //Therefore, the readyRead signal is emitted twice.
426 //This is a guard against the BEAST attack.
427 //See: https://www.imperialviolet.org/2012/01/15/beastfollowup.html
428 //For Safari, the handshake is delivered at once
429 //FIXME: For FireFox, the readyRead signal is never emitted
430 //This is a bug in FireFox (see https://bugzilla.mozilla.org/show_bug.cgi?id=594502)
431
432 // According to RFC822 the body is separated from the headers by a null line (CRLF)
433 const QByteArray& endOfHeaderMarker = QByteArrayLiteral("\r\n\r\n");
434
435 const qint64 byteAvailable = pTcpSocket->bytesAvailable();
436 QByteArray header = pTcpSocket->peek(maxlen: byteAvailable);
437 const int endOfHeaderIndex = header.indexOf(a: endOfHeaderMarker);
438 if (endOfHeaderIndex < 0) {
439 //then we don't have our header complete yet
440 //check that no one is trying to exhaust our virtual memory
441 const qint64 maxHeaderLength = MAX_HEADERLINE_LENGTH * MAX_HEADERLINES + endOfHeaderMarker.size();
442 if (Q_UNLIKELY(byteAvailable > maxHeaderLength)) {
443 pTcpSocket->close();
444 setError(code: QWebSocketProtocol::CloseCodeTooMuchData,
445 errorString: QWebSocketServer::tr(s: "Header is too large."));
446 }
447 return;
448 }
449 const int headerSize = endOfHeaderIndex + endOfHeaderMarker.size();
450
451 disconnect(sender: pTcpSocket, signal: &QTcpSocket::readyRead,
452 receiverPrivate: this, slot: &QWebSocketServerPrivate::handshakeReceived);
453 bool success = false;
454 bool isSecure = (m_secureMode == SecureMode);
455
456 if (Q_UNLIKELY(m_pendingConnections.length() >= maxPendingConnections())) {
457 pTcpSocket->close();
458 setError(code: QWebSocketProtocol::CloseCodeAbnormalDisconnection,
459 errorString: QWebSocketServer::tr(s: "Too many pending connections."));
460 return;
461 }
462
463 //don't read past the header
464 header.resize(size: headerSize);
465 //remove our header from the tcpSocket
466 qint64 skippedSize = pTcpSocket->skip(maxSize: headerSize);
467
468 if (Q_UNLIKELY(skippedSize != headerSize)) {
469 pTcpSocket->close();
470 setError(code: QWebSocketProtocol::CloseCodeProtocolError,
471 errorString: QWebSocketServer::tr(s: "Read handshake request header failed."));
472 return;
473 }
474
475 QWebSocketHandshakeRequest request(pTcpSocket->peerPort(), isSecure);
476 QTextStream textStream(header, QIODevice::ReadOnly);
477 request.readHandshake(textStream, maxHeaderLineLength: MAX_HEADERLINE_LENGTH, maxHeaders: MAX_HEADERLINES);
478
479 if (request.isValid()) {
480 QWebSocketCorsAuthenticator corsAuthenticator(request.origin());
481 Q_EMIT q->originAuthenticationRequired(pAuthenticator: &corsAuthenticator);
482
483 QWebSocketHandshakeResponse response(request,
484 m_serverName,
485 corsAuthenticator.allowed(),
486 supportedVersions(),
487 supportedProtocols(),
488 supportedExtensions());
489
490 if (Q_LIKELY(response.isValid())) {
491 QTextStream httpStream(pTcpSocket);
492 httpStream << response;
493 httpStream.flush();
494
495 if (Q_LIKELY(response.canUpgrade())) {
496 QWebSocket *pWebSocket = QWebSocketPrivate::upgradeFrom(tcpSocket: pTcpSocket,
497 request,
498 response);
499 if (Q_LIKELY(pWebSocket)) {
500 finishHandshakeTimeout(pTcpSocket);
501 addPendingConnection(pWebSocket);
502 Q_EMIT q->newConnection();
503 success = true;
504 } else {
505 setError(code: QWebSocketProtocol::CloseCodeAbnormalDisconnection,
506 errorString: QWebSocketServer::tr(s: "Upgrade to WebSocket failed."));
507 }
508 }
509 else {
510 setError(code: response.error(), errorString: response.errorString());
511 }
512 } else {
513 setError(code: QWebSocketProtocol::CloseCodeProtocolError,
514 errorString: QWebSocketServer::tr(s: "Invalid response received."));
515 }
516 }
517 if (!success) {
518 pTcpSocket->close();
519 }
520}
521
522void QWebSocketServerPrivate::handleConnection(QTcpSocket *pTcpSocket) const
523{
524 if (Q_LIKELY(pTcpSocket)) {
525 // Use a queued connection because a QSslSocket needs the event loop to process incoming
526 // data. If not queued, data is incomplete when handshakeReceived is called.
527 QObjectPrivate::connect(sender: pTcpSocket, signal: &QTcpSocket::readyRead,
528 receiverPrivate: this, slot: &QWebSocketServerPrivate::handshakeReceived,
529 type: Qt::QueuedConnection);
530
531 // We received some data! We must emit now to be sure that handshakeReceived is called
532 // since the data could have been received before the signal and slot was connected.
533 if (pTcpSocket->bytesAvailable()) {
534 Q_EMIT pTcpSocket->readyRead();
535 }
536
537 QObjectPrivate::connect(sender: pTcpSocket, signal: &QTcpSocket::disconnected,
538 receiverPrivate: this, slot: &QWebSocketServerPrivate::onSocketDisconnected);
539 }
540}
541
542void QWebSocketServerPrivate::startHandshakeTimeout(QTcpSocket *pTcpSocket)
543{
544 if (m_handshakeTimeout < 0)
545 return;
546
547 QTimer *handshakeTimer = new QTimer(pTcpSocket);
548 handshakeTimer->setSingleShot(true);
549 handshakeTimer->setObjectName(QStringLiteral("handshakeTimer"));
550 QObject::connect(sender: handshakeTimer, signal: &QTimer::timeout, slot: [=]() {
551 pTcpSocket->close();
552 });
553 handshakeTimer->start(msec: m_handshakeTimeout);
554}
555
556void QWebSocketServerPrivate::finishHandshakeTimeout(QTcpSocket *pTcpSocket)
557{
558 if (QTimer *handshakeTimer = pTcpSocket->findChild<QTimer *>(QStringLiteral("handshakeTimer"))) {
559 handshakeTimer->stop();
560 delete handshakeTimer;
561 }
562}
563
564QT_END_NAMESPACE
565

source code of qtwebsockets/src/websockets/qwebsocketserver_p.cpp