Torc  0.1
torcwebsocketreader.cpp
Go to the documentation of this file.
1 /* Class TorcWebSocketReader
2 *
3 * This file is part of the Torc project.
4 *
5 * Copyright (C) Mark Kendall 2018
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
20 * USA.
21 */
22 
23 // Qt
24 #include <QtEndian>
25 
26 // Torc
27 #include "torclogging.h"
28 #include "torcwebsocketreader.h"
29 
30 // utf8
31 #include "utf8/core.h"
32 #include "utf8/checked.h"
33 #include "utf8/unchecked.h"
34 
37 {
38  switch (Code)
39  {
40  case OpContinuation: return QStringLiteral("Continuation");
41  case OpText: return QStringLiteral("Text");
42  case OpBinary: return QStringLiteral("Binary");
43  case OpClose: return QStringLiteral("Close");
44  case OpPing: return QStringLiteral("Ping");
45  case OpPong: return QStringLiteral("Pong");
46  default: break;
47  }
48 
49  return QStringLiteral("Reserved");
50 }
51 
54 {
55  switch (Code)
56  {
57  case CloseNormal: return QStringLiteral("Normal");
58  case CloseGoingAway: return QStringLiteral("GoingAway");
59  case CloseProtocolError: return QStringLiteral("ProtocolError");
60  case CloseUnsupportedDataType: return QStringLiteral("UnsupportedDataType");
61  case CloseReserved1004: return QStringLiteral("Reserved");
62  case CloseStatusCodeMissing: return QStringLiteral("StatusCodeMissing");
63  case CloseAbnormal: return QStringLiteral("Abnormal");
64  case CloseInconsistentData: return QStringLiteral("InconsistentData");
65  case ClosePolicyViolation: return QStringLiteral("PolicyViolation");
66  case CloseMessageTooBig: return QStringLiteral("MessageTooBig");
67  case CloseMissingExtension: return QStringLiteral("MissingExtension");
68  case CloseUnexpectedError: return QStringLiteral("UnexpectedError");
69  case CloseTLSHandshakeError: return QStringLiteral("TLSHandshakeError");
70  default: break;
71  }
72 
73  return QStringLiteral("Unknown");
74 }
75 
77 {
78  switch (Protocol)
79  {
81  return OpText;
83  return OpText;
84  default: break;
85  }
86 
87  return OpText;
88 }
89 
91 QString TorcWebSocketReader::SubProtocolsToString(WSSubProtocols Protocols)
92 {
93  QStringList list;
94 
95  if (Protocols.testFlag(SubProtocolJSONRPC)) list.append(TORC_JSON_RPC.toLatin1());
96 
97  return list.join(',');
98 }
99 
101 TorcWebSocketReader::WSSubProtocols TorcWebSocketReader::SubProtocolsFromString(const QString &Protocols)
102 {
103  WSSubProtocols protocols = SubProtocolNone;
104 
105  if (Protocols.contains(TORC_JSON_RPC.toLatin1(), Qt::CaseInsensitive)) protocols |= SubProtocolJSONRPC;
106 
107  return protocols;
108 }
109 
111 QList<TorcWebSocketReader::WSSubProtocol> TorcWebSocketReader::SubProtocolsFromPrioritisedString(const QString &Protocols)
112 {
113  QList<WSSubProtocol> results;
114  results.reserve(1);
115  QStringList protocols = Protocols.split(',');
116  for (int i = 0; i < protocols.size(); ++i)
117  if (!QString::compare(protocols[i].trimmed(), TORC_JSON_RPC.toLatin1(), Qt::CaseInsensitive))
118  results.append(SubProtocolJSONRPC);
119  return results;
120 }
121 TorcWebSocketReader::TorcWebSocketReader(QTcpSocket &Socket, WSSubProtocol Protocol, bool ServerSide)
122  : m_socket(Socket),
123  m_serverSide(ServerSide),
124  m_closeSent(false),
125  m_closeReceived(false),
126  m_echoTest(false),
127  m_subProtocol(Protocol),
128  m_subProtocolFrameFormat(FormatForSubProtocol(Protocol)),
129  m_readState(ReadHeader),
130  m_frameOpCode(OpContinuation),
131  m_frameFinalFragment(false),
132  m_frameMasked(false),
133  m_haveBufferedPayload(false),
134  m_bufferedPayload(),
135  m_bufferedPayloadOpCode(OpContinuation),
136  m_framePayloadLength(0),
137  m_framePayloadReadPosition(0),
138  m_frameMask(QByteArray(4, 0)),
139  m_framePayload(QByteArray())
140 {
141 }
142 
143 const QByteArray& TorcWebSocketReader::GetPayload(void)
144 {
145  return m_haveBufferedPayload ? m_bufferedPayload : m_framePayload;
146 }
147 
149 {
150  m_haveBufferedPayload = false;
151  m_bufferedPayload = QByteArray();
152  m_readState = ReadHeader;
153  m_framePayload = QByteArray();
154  m_framePayloadReadPosition = 0;
155  m_framePayloadLength = 0;
156 }
157 
159 {
160  return m_closeSent;
161 }
162 
164 {
165  m_echoTest = true;
166 }
167 
169 {
170  m_subProtocol = Protocol;
171  m_subProtocolFrameFormat = FormatForSubProtocol(Protocol);
172 }
173 
174 void TorcWebSocketReader::InitiateClose(CloseCode Close, const QString &Reason)
175 {
176  if (!m_closeSent)
177  {
178  QByteArray payload;
179  payload.append((Close >> 8) & 0xff);
180  payload.append(Close & 0xff);
181  payload.append(Reason.toUtf8());
182  SendFrame(OpClose, payload);
183  m_closeSent = true;
184  }
185 }
186 
187 void TorcWebSocketReader::SendFrame(OpCode Code, QByteArray &Payload)
188 {
189  // don't send if OpClose has already been sent or OpClose received and
190  // we're sending anything other than the echoed OpClose
191  if (m_closeSent || (m_closeReceived && Code != OpClose))
192  return;
193 
194  QByteArray frame;
195  QByteArray mask;
196  QByteArray size;
197 
198  // no fragmentation yet - so this is always the final fragment
199  frame.append(Code | 0x80);
200 
201  quint8 byte = 0;
202 
203  // is this masked
204  if (!m_serverSide)
205  {
206  for (int i = 0; i < 4; ++i)
207  mask.append(qrand() % 0x100);
208 
209  byte |= 0x80;
210  }
211 
212  // generate correct size
213  quint64 length = Payload.size();
214 
215  if (length < 126)
216  {
217  byte |= length;
218  }
219  else if (length <= 0xffff)
220  {
221  byte |= 126;
222  size.append((length >> 8) & 0xff);
223  size.append(length & 0xff);
224  }
225  else if (length <= 0x7fffffff)
226  {
227  byte |= 127;
228  size.append((length >> 56) & 0xff);
229  size.append((length >> 48) & 0xff);
230  size.append((length >> 40) & 0xff);
231  size.append((length >> 32) & 0xff);
232  size.append((length >> 24) & 0xff);
233  size.append((length >> 16) & 0xff);
234  size.append((length >> 8 ) & 0xff);
235  size.append((length ) & 0xff);
236  }
237  else
238  {
239  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Infeasibly large payload!"));
240  return;
241  }
242 
243  frame.append(byte);
244  if (!size.isEmpty())
245  frame.append(size);
246 
247  if (!m_serverSide)
248  {
249  frame.append(mask);
250  for (int i = 0; i < Payload.size(); ++i)
251  Payload[i] = Payload[i] ^ mask[i % 4];
252  }
253 
254  if (m_socket.write(frame) == frame.size())
255  {
256  if ((!Payload.isEmpty() && m_socket.write(Payload) == Payload.size()) || Payload.isEmpty())
257  {
258  LOG(VB_NETWORK, LOG_DEBUG, QStringLiteral("Sent frame (Final), OpCode: '%1' Masked: %2 Length: %3")
259  .arg(OpCodeToString(Code)).arg(!m_serverSide).arg(Payload.size()));
260  return;
261  }
262  }
263 
264  if (Code != OpClose)
265  InitiateClose(CloseUnexpectedError, QStringLiteral("Send error"));
266 }
267 
268 void TorcWebSocketReader::HandlePing(QByteArray &Payload)
269 {
270  // ignore pings once in close down
271  if (m_closeReceived || m_closeSent)
272  return;
273 
274  SendFrame(OpPong, Payload);
275 }
276 
277 void TorcWebSocketReader::HandlePong(QByteArray &Payload)
278 {
279  // TODO validate known pings
280  (void)Payload;
281 }
282 
287 void TorcWebSocketReader::HandleCloseRequest(QByteArray &Close)
288 {
289  CloseCode newclosecode = CloseNormal;
290  int closecode = CloseNormal;
291  QString reason;
292 
293  if (Close.size() < 1)
294  {
295  QByteArray newpayload;
296  newpayload.append((closecode >> 8) & 0xff);
297  newpayload.append(closecode & 0xff);
298  Close = newpayload;
299  }
300  // payload size 1 is invalid
301  else if (Close.size() == 1)
302  {
303  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Invalid close payload size (<2)"));
304  newclosecode = CloseProtocolError;
305  }
306  // check close code if present
307  else if (Close.size() > 1)
308  {
309  closecode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(Close.data()));
310 
311  if (closecode < CloseNormal || closecode > 4999 || closecode == CloseReserved1004 ||
312  closecode == CloseStatusCodeMissing || closecode == CloseAbnormal || closecode == CloseTLSHandshakeError)
313  {
314  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Invalid close code"));
315  newclosecode = CloseProtocolError;
316  }
317  }
318 
319  // check close reason if present
320  if (Close.size() > 2 && newclosecode == CloseNormal)
321  {
322  if (!utf8::is_valid(Close.data() + 2, Close.data() + Close.size()))
323  {
324  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Invalid UTF8 in close payload"));
325  newclosecode = CloseInconsistentData;
326  }
327  else
328  {
329  reason = QString::fromUtf8(Close.data() + 2, Close.size() -2);
330  }
331  }
332 
333  if (newclosecode != CloseNormal)
334  {
335  QByteArray newpayload;
336  newpayload.append((newclosecode >> 8) & 0xff);
337  newpayload.append(newclosecode & 0xff);
338  Close = newpayload;
339  }
340  else
341  {
342  LOG(VB_NETWORK, LOG_INFO, QStringLiteral("Received Close: %1 ('%2')").arg(CloseCodeToString((CloseCode)closecode), reason));
343  }
344 
345  m_closeReceived = true;
346 
347  if (!m_closeSent)
348  {
349  // echo back the payload and close request
350  SendFrame(OpClose, Close);
351  m_closeSent = true;
352  }
353 }
354 
356 {
357  if (m_socket.state() != QAbstractSocket::ConnectedState)
358  return false;
359 
360  // I'm not sure what this was trying to do in the old TorcWebSocket::ReadyRead code
361  // || (m_readState == ReadPayload && m_framePayloadLength == 0)
362  switch (m_readState)
363  {
364  case ReadHeader:
365  {
366  // we need at least 2 bytes to start reading
367  if (m_socket.bytesAvailable() < 2)
368  return false;
369 
370  char header[2];
371  if (m_socket.read(header, 2) != 2)
372  {
373  InitiateClose(CloseUnexpectedError, QStringLiteral("Read error"));
374  return false;
375  }
376 
377  m_frameFinalFragment = (header[0] & 0x80) != 0;
378  m_frameOpCode = static_cast<OpCode>(header[0] & 0x0F);
379  m_frameMasked = (header[1] & 0x80) != 0;
380  quint8 length = (header[1] & 0x7F);
381  bool reservedbits = (header[0] & 0x70) != 0;
382 
383  // validate the header against current state and specification
384  CloseCode error = CloseNormal;
385  QString reason;
386 
387  // invalid use of reserved bits
388  if (reservedbits)
389  {
390  reason = QStringLiteral("Invalid use of reserved bits");
391  error = CloseProtocolError;
392  }
393 
394  // control frames can only have payloads of up to 125 bytes
395  else if ((m_frameOpCode & 0x8) && length > 125)
396  {
397  reason = QStringLiteral("Control frame payload too large");
398  error = CloseProtocolError;
399 
400  // need to acknowledge when an OpClose is received
401  if (OpClose == m_frameOpCode)
402  m_closeReceived = true;
403  }
404 
405  // if this is a server, clients must be sending masked frames and vice versa
406  else if (m_serverSide != m_frameMasked)
407  {
408  reason = QStringLiteral("Masking error");
409  error = CloseProtocolError;
410  }
411 
412  // we should never receive a reserved opcode
413  else if (m_frameOpCode != OpText && m_frameOpCode != OpBinary && m_frameOpCode != OpContinuation &&
414  m_frameOpCode != OpPing && m_frameOpCode != OpPong && m_frameOpCode != OpClose)
415  {
416  reason = QStringLiteral("Invalid use of reserved opcode");
417  error = CloseProtocolError;
418  }
419 
420  // control frames cannot be fragmented
421  else if (!m_frameFinalFragment && (m_frameOpCode == OpPing || m_frameOpCode == OpPong || m_frameOpCode == OpClose))
422  {
423  reason = QStringLiteral("Fragmented control frame");
424  error = CloseProtocolError;
425  }
426 
427  // a continuation frame must have an opening frame
428  else if (!m_haveBufferedPayload && m_frameOpCode == OpContinuation)
429  {
430  reason = QStringLiteral("Fragmentation error");
431  error = CloseProtocolError;
432  }
433 
434  // only continuation frames or control frames are expected once in the middle of a frame
435  else if (m_haveBufferedPayload && !(m_frameOpCode == OpContinuation || m_frameOpCode == OpPing || m_frameOpCode == OpPong || m_frameOpCode == OpClose))
436  {
437  reason = QStringLiteral("Fragmentation error");
438  error = CloseProtocolError;
439  }
440 
441  // ensure OpCode matches that expected by SubProtocol
442  else if ((!m_echoTest && m_subProtocol != SubProtocolNone) &&
443  (m_frameOpCode == OpText || m_frameOpCode == OpBinary) &&
444  m_frameOpCode != m_subProtocolFrameFormat)
445  {
446  reason = QStringLiteral("Received incorrect frame type for subprotocol");
447  error = CloseUnsupportedDataType;
448  }
449 
450  if (error != CloseNormal)
451  {
452  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Read error: %1 (%2)").arg(CloseCodeToString(error), reason));
453  InitiateClose(error, reason);
454  return false;
455  }
456 
457  if (126 == length)
458  {
459  m_readState = Read16BitLength;
460  break;
461  }
462 
463  if (127 == length)
464  {
465  m_readState = Read64BitLength;
466  break;
467  }
468 
469  m_framePayloadLength = length;
470  m_readState = m_frameMasked ? ReadMask : ReadPayload;
471  }
472  break;
473 
474  case Read16BitLength:
475  {
476  if (m_socket.bytesAvailable() < 2)
477  return false;
478 
479  uchar length[2];
480 
481  if (m_socket.read(reinterpret_cast<char *>(length), 2) != 2)
482  {
483  InitiateClose(CloseUnexpectedError, QStringLiteral("Read error"));
484  return false;
485  }
486 
487  m_framePayloadLength = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
488  m_readState = m_frameMasked ? ReadMask : ReadPayload;
489  }
490  break;
491 
492  case Read64BitLength:
493  {
494  if (m_socket.bytesAvailable() < 8)
495  return false;
496 
497  uchar length[8];
498  if (m_socket.read(reinterpret_cast<char *>(length), 8) != 8)
499  {
500  InitiateClose(CloseUnexpectedError, QStringLiteral("Read error"));
501  return false;
502  }
503 
504  m_framePayloadLength = qFromBigEndian<quint64>(length) & ~(1LL << 63);
505  m_readState = m_frameMasked ? ReadMask : ReadPayload;
506  }
507  break;
508 
509  case ReadMask:
510  {
511  if (m_socket.bytesAvailable() < 4)
512  return false;
513 
514  if (m_socket.read(const_cast<char *>(m_frameMask.constData()), 4) != 4)
515  {
516  InitiateClose(CloseUnexpectedError, QStringLiteral("Read error"));
517  return false;
518  }
519 
520  m_readState = ReadPayload;
521  }
522  break;
523 
524  case ReadPayload:
525  {
526  // allocate the payload buffer if needed
527  if (m_framePayloadReadPosition == 0)
528  m_framePayload = QByteArray(m_framePayloadLength, 0);
529 
530  qint64 needed = m_framePayloadLength - m_framePayloadReadPosition;
531 
532  // payload length may be zero
533  if (needed > 0)
534  {
535  qint64 red = qMin(m_socket.bytesAvailable(), needed);
536 
537  if (m_socket.read(const_cast<char*>(m_framePayload.constData() + m_framePayloadReadPosition), red) != red)
538  {
539  InitiateClose(CloseUnexpectedError, QStringLiteral("Read error"));
540  return false;
541  }
542 
543  m_framePayloadReadPosition += red;
544  }
545 
546  // finished
547  if (m_framePayloadReadPosition == m_framePayloadLength)
548  {
549  LOG(VB_NETWORK, LOG_DEBUG, QStringLiteral("Frame, Final: %1 OpCode: '%2' Masked: %3 Length: %4")
550  .arg(m_frameFinalFragment).arg(OpCodeToString(m_frameOpCode)).arg(m_frameMasked).arg(m_framePayloadLength));
551 
552  // unmask payload
553  if (m_frameMasked)
554  for (int i = 0; i < m_framePayload.size(); ++i)
555  m_framePayload[i] = (m_framePayload[i] ^ m_frameMask[i % 4]);
556 
557  // start buffering fragmented payloads
558  if (!m_frameFinalFragment && (m_frameOpCode == OpText || m_frameOpCode == OpBinary))
559  {
560  // header checks should prevent this
561  if (m_haveBufferedPayload)
562  {
563  m_bufferedPayload = QByteArray();
564  LOG(VB_GENERAL, LOG_WARNING, QStringLiteral("Already have payload buffer - clearing"));
565  }
566 
567  m_haveBufferedPayload = true;
568  m_bufferedPayload = QByteArray(m_framePayload);
569  m_bufferedPayloadOpCode = m_frameOpCode;
570  }
571  else if (m_frameOpCode == OpContinuation)
572  {
573  if (m_haveBufferedPayload)
574  m_bufferedPayload.append(m_framePayload);
575  else
576  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Continuation frame but no buffered payload"));
577  }
578 
579  // finished
580  if (m_frameFinalFragment)
581  {
582  if (m_frameOpCode == OpPong)
583  {
584  HandlePong(m_framePayload);
585  }
586  else if (m_frameOpCode == OpPing)
587  {
588  HandlePing(m_framePayload);
589  }
590  else if (m_frameOpCode == OpClose)
591  {
592  HandleCloseRequest(m_framePayload);
593  }
594  else
595  {
596  bool invalidtext = false;
597 
598  // validate and debug UTF8 text
599  if (!m_haveBufferedPayload && m_frameOpCode == OpText && !m_framePayload.isEmpty())
600  {
601  if (!utf8::is_valid(m_framePayload.data(), m_framePayload.data() + m_framePayload.size()))
602  {
603  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Invalid UTF8"));
604  invalidtext = true;
605  }
606  else
607  {
608  LOG(VB_NETWORK, LOG_DEBUG, QStringLiteral("'%1'").arg(QString::fromUtf8(m_framePayload)));
609  }
610  }
611  else if (m_haveBufferedPayload && m_bufferedPayloadOpCode == OpText)
612  {
613  if (!utf8::is_valid(m_bufferedPayload.data(), m_bufferedPayload.data() + m_bufferedPayload.size()))
614  {
615  LOG(VB_GENERAL, LOG_ERR, QStringLiteral("Invalid UTF8"));
616  invalidtext = true;
617  }
618  else
619  {
620  LOG(VB_NETWORK, LOG_DEBUG, QStringLiteral("'%1'").arg(QString::fromUtf8(m_bufferedPayload)));
621  }
622  }
623 
624  if (invalidtext)
625  {
626  InitiateClose(CloseInconsistentData, QStringLiteral("Invalid UTF-8 text"));
627  return false;
628  }
629  else
630  {
631  // echo test for AutoBahn test suite
632  if (m_echoTest)
633  {
634  SendFrame(m_haveBufferedPayload ? m_bufferedPayloadOpCode : m_frameOpCode,
635  m_haveBufferedPayload ? m_bufferedPayload : m_framePayload);
636  }
637  else
638  {
639  // we only return true when the parent needs to handle the payload
640  // and MUST then call reset()
641  return true;
642  }
643  }
644  m_haveBufferedPayload = false;
645  m_bufferedPayload = QByteArray();
646  }
647  }
648 
649  // reset frame and readstate
650  m_readState = ReadHeader;
651  m_framePayload = QByteArray();
652  m_framePayloadReadPosition = 0;
653  m_framePayloadLength = 0;
654  }
655  else if (m_framePayload.size() > (qint64)m_framePayloadLength)
656  {
657  // this shouldn't happen
658  InitiateClose(CloseUnexpectedError, QStringLiteral("Read error"));
659  return false;
660  }
661  }
662  break;
663  }
664 
665  return false;
666 }
void SetSubProtocol(WSSubProtocol Protocol)
static QList< WSSubProtocol > SubProtocolsFromPrioritisedString(const QString &Protocols)
Parse a prioritised list of supported WebSocket sub-protocols.
const QByteArray & GetPayload(void)
static QString SubProtocolsToString(WSSubProtocols Protocols)
Convert SubProtocols to HTTP readable string.
void SendFrame(OpCode Code, QByteArray &Payload)
TorcWebSocketReader(QTcpSocket &Socket, WSSubProtocol Protocol, bool ServerSide)
static QString CloseCodeToString(CloseCode Code)
Convert CloseCode to human readable string.
static WSSubProtocols SubProtocolsFromString(const QString &Protocols)
Parse supported WSSubProtocols from Protocols.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: torclogging.h:20
void InitiateClose(CloseCode Close, const QString &Reason)
static OpCode FormatForSubProtocol(WSSubProtocol Protocol)
static QString OpCodeToString(OpCode Code)
Convert OpCode to human readable string.
#define TORC_JSON_RPC