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/*!
41 \class QWebSocketFrame
42 The class QWebSocketFrame is responsible for reading, validating and
43 interpreting frames from a WebSocket.
44 It reads data from a QIODevice, validates it against RFC 6455, and parses it into a
45 frame (data, control).
46 Whenever an error is detected, isValid() returns false.
47
48 \note The QWebSocketFrame class does not look at valid sequences of frames.
49 It processes frames one at a time.
50 \note It is the QWebSocketDataProcessor that takes the sequence into account.
51
52 \sa QWebSocketDataProcessor
53 \internal
54 */
55
56#include "qwebsocketframe_p.h"
57#include "qwebsocketprotocol_p.h"
58
59#include <QtCore/QtEndian>
60#include <QtCore/QDebug>
61
62QT_BEGIN_NAMESPACE
63
64/*!
65 \internal
66 */
67void QWebSocketFrame::setMaxAllowedFrameSize(quint64 maxAllowedFrameSize)
68{
69 if (maxAllowedFrameSize <= maxFrameSize())
70 m_maxAllowedFrameSize = maxAllowedFrameSize;
71}
72
73/*!
74 \internal
75 */
76quint64 QWebSocketFrame::maxAllowedFrameSize() const
77{
78 return m_maxAllowedFrameSize;
79}
80
81/*!
82 \internal
83 */
84quint64 QWebSocketFrame::maxFrameSize()
85{
86 return MAX_FRAME_SIZE_IN_BYTES;
87}
88
89/*!
90 \internal
91 */
92QWebSocketProtocol::CloseCode QWebSocketFrame::closeCode() const
93{
94 return isDone() ? m_closeCode : QWebSocketProtocol::CloseCodeGoingAway;
95}
96
97/*!
98 \internal
99 */
100QString QWebSocketFrame::closeReason() const
101{
102 return isDone() ? m_closeReason : tr(sourceText: "Waiting for more data from socket.");
103}
104
105/*!
106 \internal
107 */
108bool QWebSocketFrame::isFinalFrame() const
109{
110 return m_isFinalFrame;
111}
112
113/*!
114 \internal
115 */
116bool QWebSocketFrame::isControlFrame() const
117{
118 return (m_opCode & 0x08) == 0x08;
119}
120
121/*!
122 \internal
123 */
124bool QWebSocketFrame::isDataFrame() const
125{
126 return !isControlFrame();
127}
128
129/*!
130 \internal
131 */
132bool QWebSocketFrame::isContinuationFrame() const
133{
134 return isDataFrame() && (m_opCode == QWebSocketProtocol::OpCodeContinue);
135}
136
137/*!
138 \internal
139 */
140bool QWebSocketFrame::hasMask() const
141{
142 return m_mask != 0;
143}
144
145/*!
146 \internal
147 */
148quint32 QWebSocketFrame::mask() const
149{
150 return m_mask;
151}
152
153/*!
154 \internal
155 */
156QWebSocketProtocol::OpCode QWebSocketFrame::opCode() const
157{
158 return m_opCode;
159}
160
161/*!
162 \internal
163 */
164QByteArray QWebSocketFrame::payload() const
165{
166 return m_payload;
167}
168
169/*!
170 Resets all member variables, and invalidates the object.
171
172 \internal
173 */
174void QWebSocketFrame::clear()
175{
176 m_closeCode = QWebSocketProtocol::CloseCodeNormal;
177 m_closeReason.clear();
178 m_isFinalFrame = true;
179 m_mask = 0;
180 m_rsv1 = false;
181 m_rsv2 = false;
182 m_rsv3 = false;
183 m_opCode = QWebSocketProtocol::OpCodeReservedC;
184 m_length = 0;
185 m_payload.clear();
186 m_isValid = false;
187 m_processingState = PS_READ_HEADER;
188}
189
190/*!
191 \internal
192 */
193bool QWebSocketFrame::isValid() const
194{
195 return isDone() && m_isValid;
196}
197
198/*!
199 \internal
200 */
201bool QWebSocketFrame::isDone() const
202{
203 return m_processingState == PS_DISPATCH_RESULT;
204}
205
206/*!
207 \internal
208 */
209void QWebSocketFrame::readFrame(QIODevice *pIoDevice)
210{
211 while (true)
212 {
213 switch (m_processingState) {
214 case PS_READ_HEADER:
215 m_processingState = readFrameHeader(pIoDevice);
216 if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
217 m_processingState = PS_READ_HEADER;
218 return;
219 }
220 break;
221
222 case PS_READ_PAYLOAD_LENGTH:
223 m_processingState = readFramePayloadLength(pIoDevice);
224 if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
225 m_processingState = PS_READ_PAYLOAD_LENGTH;
226 return;
227 }
228 break;
229
230 case PS_READ_MASK:
231 m_processingState = readFrameMask(pIoDevice);
232 if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
233 m_processingState = PS_READ_MASK;
234 return;
235 }
236 break;
237
238 case PS_READ_PAYLOAD:
239 m_processingState = readFramePayload(pIoDevice);
240 if (m_processingState == PS_WAIT_FOR_MORE_DATA) {
241 m_processingState = PS_READ_PAYLOAD;
242 return;
243 }
244 break;
245
246 case PS_DISPATCH_RESULT:
247 return;
248
249 default:
250 Q_UNREACHABLE();
251 return;
252 }
253 }
254}
255
256/*!
257 \internal
258 */
259QWebSocketFrame::ProcessingState QWebSocketFrame::readFrameHeader(QIODevice *pIoDevice)
260{
261 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
262 // FIN, RSV1-3, Opcode
263 char header[2] = {0};
264 if (Q_UNLIKELY(pIoDevice->read(header, 2) < 2)) {
265 setError(code: QWebSocketProtocol::CloseCodeGoingAway,
266 closeReason: tr(sourceText: "Error occurred while reading header from the network: %1")
267 .arg(a: pIoDevice->errorString()));
268 return PS_DISPATCH_RESULT;
269 }
270 m_isFinalFrame = (header[0] & 0x80) != 0;
271 m_rsv1 = (header[0] & 0x40);
272 m_rsv2 = (header[0] & 0x20);
273 m_rsv3 = (header[0] & 0x10);
274 m_opCode = static_cast<QWebSocketProtocol::OpCode>(header[0] & 0x0F);
275
276 // Mask
277 // Use zero as mask value to mean there's no mask to read.
278 // When the mask value is read, it over-writes this non-zero value.
279 m_mask = header[1] & 0x80;
280 // PayloadLength
281 m_length = (header[1] & 0x7F);
282
283 if (!checkValidity())
284 return PS_DISPATCH_RESULT;
285
286 switch (m_length) {
287 case 126:
288 case 127:
289 return PS_READ_PAYLOAD_LENGTH;
290 default:
291 return hasMask() ? PS_READ_MASK : PS_READ_PAYLOAD;
292 }
293 }
294 return PS_WAIT_FOR_MORE_DATA;
295}
296
297/*!
298 \internal
299 */
300QWebSocketFrame::ProcessingState QWebSocketFrame::readFramePayloadLength(QIODevice *pIoDevice)
301{
302 // see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
303 // in all cases, the minimal number of bytes MUST be used to encode the length,
304 // for example, the length of a 124-byte-long string can't be encoded as the
305 // sequence 126, 0, 124"
306 switch (m_length) {
307 case 126:
308 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 2)) {
309 uchar length[2] = {0};
310 if (Q_UNLIKELY(pIoDevice->read(reinterpret_cast<char *>(length), 2) < 2)) {
311 setError(code: QWebSocketProtocol::CloseCodeGoingAway,
312 closeReason: tr(sourceText: "Error occurred while reading from the network: %1")
313 .arg(a: pIoDevice->errorString()));
314 return PS_DISPATCH_RESULT;
315 }
316 m_length = qFromBigEndian<quint16>(src: reinterpret_cast<const uchar *>(length));
317 if (Q_UNLIKELY(m_length < 126)) {
318
319 setError(code: QWebSocketProtocol::CloseCodeProtocolError,
320 closeReason: tr(sourceText: "Lengths smaller than 126 must be expressed as one byte."));
321 return PS_DISPATCH_RESULT;
322 }
323 return hasMask() ? PS_READ_MASK : PS_READ_PAYLOAD;
324 }
325 break;
326 case 127:
327 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 8)) {
328 uchar length[8] = {0};
329 if (Q_UNLIKELY(pIoDevice->read(reinterpret_cast<char *>(length), 8) < 8)) {
330 setError(code: QWebSocketProtocol::CloseCodeAbnormalDisconnection,
331 closeReason: tr(sourceText: "Something went wrong during reading from the network."));
332 return PS_DISPATCH_RESULT;
333 }
334 // Most significant bit must be set to 0 as
335 // per http://tools.ietf.org/html/rfc6455#section-5.2
336 m_length = qFromBigEndian<quint64>(src: length);
337 if (Q_UNLIKELY(m_length & (quint64(1) << 63))) {
338 setError(code: QWebSocketProtocol::CloseCodeProtocolError,
339 closeReason: tr(sourceText: "Highest bit of payload length is not 0."));
340 return PS_DISPATCH_RESULT;
341 }
342 if (Q_UNLIKELY(m_length <= 0xFFFFu)) {
343 setError(code: QWebSocketProtocol::CloseCodeProtocolError,
344 closeReason: tr(sourceText: "Lengths smaller than 65536 (2^16) must be expressed as 2 bytes."));
345 return PS_DISPATCH_RESULT;
346 }
347 return hasMask() ? PS_READ_MASK : PS_READ_PAYLOAD;
348 }
349 break;
350 default:
351 Q_UNREACHABLE();
352 break;
353 }
354 return PS_WAIT_FOR_MORE_DATA;
355}
356
357/*!
358 \internal
359 */
360QWebSocketFrame::ProcessingState QWebSocketFrame::readFrameMask(QIODevice *pIoDevice)
361{
362 if (Q_LIKELY(pIoDevice->bytesAvailable() >= 4)) {
363 if (Q_UNLIKELY(pIoDevice->read(reinterpret_cast<char *>(&m_mask), sizeof(m_mask)) < 4)) {
364 setError(code: QWebSocketProtocol::CloseCodeGoingAway,
365 closeReason: tr(sourceText: "Error while reading from the network: %1.").arg(a: pIoDevice->errorString()));
366 return PS_DISPATCH_RESULT;
367 }
368 m_mask = qFromBigEndian(source: m_mask);
369 return PS_READ_PAYLOAD;
370 }
371 return PS_WAIT_FOR_MORE_DATA;
372}
373
374/*!
375 \internal
376 */
377QWebSocketFrame::ProcessingState QWebSocketFrame::readFramePayload(QIODevice *pIoDevice)
378{
379 if (!m_length)
380 return PS_DISPATCH_RESULT;
381
382 if (Q_UNLIKELY(m_length > maxAllowedFrameSize())) {
383 setError(code: QWebSocketProtocol::CloseCodeTooMuchData, closeReason: tr(sourceText: "Maximum framesize exceeded."));
384 return PS_DISPATCH_RESULT;
385 }
386 if (quint64(pIoDevice->bytesAvailable()) >= m_length) {
387 m_payload = pIoDevice->read(maxlen: int(m_length));
388 // m_length can be safely cast to an integer,
389 // because MAX_FRAME_SIZE_IN_BYTES = MAX_INT
390 if (Q_UNLIKELY(m_payload.length() != int(m_length))) {
391 // some error occurred; refer to the Qt documentation of QIODevice::read()
392 setError(code: QWebSocketProtocol::CloseCodeAbnormalDisconnection,
393 closeReason: tr(sourceText: "Some serious error occurred while reading from the network."));
394 } else if (hasMask()) {
395 QWebSocketProtocol::mask(payload: &m_payload, maskingKey: mask());
396 }
397 return PS_DISPATCH_RESULT;
398 }
399 return PS_WAIT_FOR_MORE_DATA;
400}
401
402/*!
403 \internal
404 */
405void QWebSocketFrame::setError(QWebSocketProtocol::CloseCode code, const QString &closeReason)
406{
407 clear();
408 m_closeCode = code;
409 m_closeReason = closeReason;
410 m_isValid = false;
411}
412
413/*!
414 \internal
415 */
416bool QWebSocketFrame::checkValidity()
417{
418 if (Q_UNLIKELY(m_rsv1 || m_rsv2 || m_rsv3)) {
419 setError(code: QWebSocketProtocol::CloseCodeProtocolError, closeReason: tr(sourceText: "Rsv field is non-zero"));
420 } else if (Q_UNLIKELY(QWebSocketProtocol::isOpCodeReserved(m_opCode))) {
421 setError(code: QWebSocketProtocol::CloseCodeProtocolError, closeReason: tr(sourceText: "Used reserved opcode"));
422 } else if (isControlFrame()) {
423 if (Q_UNLIKELY(m_length > 125)) {
424 setError(code: QWebSocketProtocol::CloseCodeProtocolError,
425 closeReason: tr(sourceText: "Control frame is larger than 125 bytes"));
426 } else if (Q_UNLIKELY(!m_isFinalFrame)) {
427 setError(code: QWebSocketProtocol::CloseCodeProtocolError,
428 closeReason: tr(sourceText: "Control frames cannot be fragmented"));
429 } else {
430 m_isValid = true;
431 }
432 } else {
433 m_isValid = true;
434 }
435 return m_isValid;
436}
437
438QT_END_NAMESPACE
439

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