web_socket_frame.c
Go to the documentation of this file.
1 /**
2  * @file web_socket_frame.c
3  * @brief WebSocket frame parsing and formatting
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 1.9.6
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "web_socket/web_socket.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (WEB_SOCKET_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Format WebSocket frame header
48  * @param[in] webSocket Handle to a WebSocket
49  * @param[in] fin FIN flag
50  * @param[in] type Frame type
51  * @param[in] payloadLen Length of the payload data
52  * @return Error code
53  **/
54 
57 {
58  error_t error;
59  WebSocketFrameContext *txContext;
60  WebSocketFrame *frame;
61 
62  //Point to the TX context
63  txContext = &webSocket->txContext;
64 
65  //Flush the transmit buffer
66  txContext->bufferPos = 0;
67  txContext->bufferLen = 0;
68 
69  //The endpoint must encapsulate the data in a WebSocket frame
70  frame = (WebSocketFrame *) txContext->buffer;
71 
72  //The frame needs to be formatted according to the WebSocket framing
73  //format
74  frame->fin = fin;
75  frame->reserved = 0;
76  frame->opcode = type;
77 
78  //All frames sent from the client to the server are masked by a 32-bit
79  //value that is contained within the frame
80  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
81  {
82  //All frames sent from client to server have the Mask bit set to 1
83  frame->mask = TRUE;
84 
85  //Make sure that the RNG callback function has been registered
86  if(webSockRandCallback != NULL)
87  {
88  //Generate a random masking key
89  error = webSockRandCallback(txContext->maskingKey, sizeof(uint32_t));
90  //Any error to report?
91  if(error)
92  return error;
93  }
94  else
95  {
96  //A cryptographically strong random number generator
97  //must be used to generate the masking key
98  return ERROR_PRNG_NOT_READY;
99  }
100  }
101  else
102  {
103  //Clear the Mask bit
104  frame->mask = FALSE;
105  }
106 
107  //Size of the frame header
108  txContext->bufferLen = sizeof(WebSocketFrame);
109 
110  //Compute the number of application data to be transmitted
111  txContext->payloadLen = payloadLen;
112 
113  //Check the length of the payload
114  if(payloadLen <= 125)
115  {
116  //Payload length
117  frame->payloadLen = payloadLen;
118  }
119  else if(payloadLen <= 65535)
120  {
121  //If the Payload Length field is set to 126, then the following
122  //2 bytes are interpreted as a 16-bit unsigned integer
123  frame->payloadLen = 126;
124 
125  //Save the length of the payload data
126  STORE16BE(payloadLen, frame->extPayloadLen);
127 
128  //Adjust the length of the frame header
129  txContext->bufferLen += sizeof(uint16_t);
130  }
131  else
132  {
133  //If the Payload Length field is set to 127, then the following
134  //8 bytes are interpreted as a 64-bit unsigned integer
135  frame->payloadLen = 127;
136 
137  //Save the length of the payload data
138  STORE64BE(payloadLen, frame->extPayloadLen);
139 
140  //Adjust the length of the frame header
141  txContext->bufferLen += sizeof(uint64_t);
142  }
143 
144  //Debug message
145  TRACE_DEBUG("WebSocket: Sending frame\r\n");
146  TRACE_DEBUG(" FIN = %u\r\n", frame->fin);
147  TRACE_DEBUG(" Reserved = %u\r\n", frame->reserved);
148  TRACE_DEBUG(" Opcode = %u\r\n", frame->opcode);
149  TRACE_DEBUG(" Mask = %u\r\n", frame->mask);
150  TRACE_DEBUG(" Payload Length = %u\r\n", txContext->payloadLen);
151 
152  //The Masking Key field is present the mask bit is set to 1
153  if(frame->mask)
154  {
155  //Debug message
156  TRACE_DEBUG_ARRAY(" Masking Key = ", txContext->maskingKey, sizeof(uint32_t));
157 
158  //Copy the masking key
159  memcpy(txContext->buffer + txContext->bufferLen,
160  txContext->maskingKey, sizeof(uint32_t));
161 
162  //Adjust the length of the frame header
163  txContext->bufferLen += sizeof(uint32_t);
164  }
165 
166  //Successful processing
167  return NO_ERROR;
168 }
169 
170 
171 /**
172  * @brief Parse WebSocket frame header
173  * @param[in] webSocket Handle to a WebSocket
174  * @param[in] frame Pointer to the WebSocket frame header
175  * @param[out] type Frame type
176  * @return Error code
177  **/
178 
180  const WebSocketFrame *frame, WebSocketFrameType *type)
181 {
182  size_t j;
183  size_t k;
184  size_t n;
185  uint16_t statusCode;
186  WebSocketFrameContext *rxContext;
187 
188  //Point to the RX context
189  rxContext = &webSocket->rxContext;
190 
191  //Point to the Extended Payload Length
192  n = sizeof(WebSocketFrame);
193 
194  //Check frame type
195  if(type != NULL)
196  {
198  {
199  if(frame->opcode != WS_FRAME_TYPE_CONTINUATION &&
200  frame->opcode != *type)
201  {
203  }
204  }
205  }
206 
207  //Check the Payload Length field
208  if(frame->payloadLen == 126)
209  {
210  //If the Payload Length field is set to 126, then the following
211  //2 bytes are interpreted as a 16-bit unsigned integer
212  rxContext->payloadLen = LOAD16BE(frame->extPayloadLen);
213 
214  //Point to the next field
215  n += sizeof(uint16_t);
216  }
217  else if(frame->payloadLen == 127)
218  {
219  //If the Payload Length field is set to 127, then the following
220  //8 bytes are interpreted as a 64-bit unsigned integer
221  rxContext->payloadLen = LOAD64BE(frame->extPayloadLen);
222 
223  //Point to the next field
224  n += sizeof(uint64_t);
225  }
226  else
227  {
228  //Retrieve the length of the payload data
229  rxContext->payloadLen = frame->payloadLen;
230  }
231 
232  //Debug message
233  TRACE_DEBUG("WebSocket: frame received...\r\n");
234  TRACE_DEBUG(" FIN = %u\r\n", frame->fin);
235  TRACE_DEBUG(" Reserved = %u\r\n", frame->reserved);
236  TRACE_DEBUG(" Opcode = %u\r\n", frame->opcode);
237  TRACE_DEBUG(" Mask = %u\r\n", frame->mask);
238  TRACE_DEBUG(" Payload Length = %u\r\n", rxContext->payloadLen);
239 
240  //Check whether the payload data is masked
241  if(frame->mask)
242  {
243  //Save the masking key
244  memcpy(rxContext->maskingKey, (uint8_t *) frame + n, sizeof(uint32_t));
245  //Debug message
246  TRACE_DEBUG_ARRAY(" Masking Key = ", rxContext->maskingKey, sizeof(uint32_t));
247 
248  //Point to the payload data
249  n += sizeof(uint32_t);
250  }
251 
252  //Text or Close frame received?
253  if(frame->opcode == WS_FRAME_TYPE_TEXT ||
254  frame->opcode == WS_FRAME_TYPE_CLOSE)
255  {
256  //Reinitialize UTF-8 decoding context
257  webSocket->utf8Context.utf8CharSize = 0;
258  webSocket->utf8Context.utf8CharIndex = 0;
259  webSocket->utf8Context.utf8CodePoint = 0;
260  }
261 
262  //If the RSV field is a nonzero value and none of the negotiated extensions
263  //defines the meaning of such a nonzero value, the receiving endpoint must
264  //fail the WebSocket connection
265  if(frame->reserved != 0)
266  {
267  //Report a protocol error
268  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
269  //Terminate the WebSocket connection
270  return ERROR_INVALID_FRAME;
271  }
272 
273  //The Opcode field defines the interpretation of the payload data
274  if(frame->opcode == WS_FRAME_TYPE_CONTINUATION)
275  {
276  //A Continuation frame cannot be the first frame of a fragmented message
277  if(rxContext->fin)
278  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
279 
281  }
282  else if(frame->opcode == WS_FRAME_TYPE_TEXT)
283  {
284  //The Opcode must be 0 in subsequent fragmented frames
285  if(!rxContext->fin)
286  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
287 
288  //Save the Opcode field
289  rxContext->dataFrameType = WS_FRAME_TYPE_TEXT;
291  }
292  else if(frame->opcode == WS_FRAME_TYPE_BINARY)
293  {
294  //The Opcode must be 0 in subsequent fragmented frames
295  if(!rxContext->fin)
296  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
297 
298  //Save the Opcode field
299  rxContext->dataFrameType = WS_FRAME_TYPE_BINARY;
301  }
302  else if(frame->opcode == WS_FRAME_TYPE_CLOSE)
303  {
304  //Check the length of the payload data
305  if(rxContext->payloadLen == 0)
306  {
307  //The Close frame does not contain any body
308  webSocket->statusCode = WS_STATUS_CODE_NORMAL_CLOSURE;
309  }
310  else if(rxContext->payloadLen == 1)
311  {
312  //Report a protocol error
313  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
314  }
315  else
316  {
317  //All frames sent from the client to the server are masked
318  if(frame->mask)
319  {
320  //Unmask the data
321  for(j = 0; j < rxContext->payloadLen; j++)
322  {
323  //Index of the masking key to be applied
324  k = j % 4;
325  //Convert masked data into unmasked data
326  *((uint8_t *) frame + n + j) ^= rxContext->maskingKey[k];
327  }
328  }
329 
330  //If there is a body, the first two bytes of the body must be
331  //a 2-byte unsigned integer representing a status code
332  statusCode = LOAD16BE((uint8_t *) frame + n);
333 
334  //Debug message
335  TRACE_DEBUG(" Status Code = %u\r\n", statusCode);
336 
337  //When sending a Close frame in response, the endpoint typically
338  //echos the status code it received
340  webSocket->statusCode = statusCode;
341  else
342  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
343 
344  //The body may contain UTF-8-encoded data
345  if(rxContext->payloadLen > 2)
346  {
347  //Compute the length of the data
348  k = rxContext->payloadLen - 2;
349 
350  //Invalid UTF-8 sequence?
351  if(!webSocketCheckUtf8Stream(&webSocket->utf8Context,
352  (uint8_t *) frame + n + 2, k, k))
353  {
354  //The received data is not consistent with the type of the message
355  webSocket->statusCode = WS_STATUS_CODE_INVALID_PAYLOAD_DATA;
356  }
357  }
358  }
359 
360  //A Close frame has been received
361  webSocket->handshakeContext.closingFrameReceived = TRUE;
362  //Exit immediately
363  return ERROR_END_OF_STREAM;
364  }
365  else if(frame->opcode == WS_FRAME_TYPE_PING)
366  {
367  //Save the Opcode field
369 
370  //Control frames must not be fragmented
371  if(!frame->fin)
372  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
373 
374  //All control frames must have a payload length of 125 bytes or less
375  if(frame->payloadLen > 125)
376  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
377  }
378  else if(frame->opcode == WS_FRAME_TYPE_PONG)
379  {
380  //Save the Opcode field
382 
383  //Control frames must not be fragmented
384  if(!frame->fin)
385  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
386 
387  //All control frames must have a payload length of 125 bytes or less
388  if(frame->payloadLen > 125)
389  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
390  }
391  else
392  {
393  //If an unknown opcode is received, the receiving endpoint must fail
394  //the WebSocket connection
395  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
396  }
397 
398  //Save the Mask flag
399  rxContext->mask = frame->mask;
400 
401  //Control frame?
403  {
404  //Return frame type
405  if(type != NULL)
406  *type = rxContext->controlFrameType;
407  }
408  else
409  {
410  //Save the FIN flag
411  rxContext->fin = frame->fin;
412 
413  //Return frame type
414  if(type != NULL)
415  *type = rxContext->dataFrameType;
416  }
417 
418  //Check status code
419  if(webSocket->statusCode == WS_STATUS_CODE_NO_STATUS_RCVD)
420  return NO_ERROR;
421  else if(webSocket->statusCode == WS_STATUS_CODE_NORMAL_CLOSURE)
422  return ERROR_END_OF_STREAM;
423  else if(webSocket->statusCode == WS_STATUS_CODE_PROTOCOL_ERROR)
424  return ERROR_INVALID_FRAME;
425  else
426  return ERROR_FAILURE;
427 }
428 
429 
430 /**
431  * @brief Format a Close frame
432  * @param[in] webSocket Handle to a WebSocket
433  * @return Error code
434  **/
435 
437 {
438  error_t error;
439  uint8_t *p;
440 
441  //Format Close frame
442  error = webSocketFormatFrameHeader(webSocket,
443  TRUE, WS_FRAME_TYPE_CLOSE, sizeof(uint16_t));
444 
445  //Check status code
446  if(!error)
447  {
448  //1005 is a reserved value and must not be set as a status code in
449  //a Close control frame by an endpoint
450  if(webSocket->statusCode == WS_STATUS_CODE_NO_STATUS_RCVD)
451  webSocket->statusCode = WS_STATUS_CODE_NORMAL_CLOSURE;
452 
453  //Debug message
454  TRACE_DEBUG(" Status Code = %u\r\n", webSocket->statusCode);
455 
456  //Point to end of the WebSocket frame header
457  p = webSocket->txContext.buffer + webSocket->txContext.bufferLen;
458 
459  //Write status code
460  p[0] = MSB(webSocket->statusCode);
461  p[1] = LSB(webSocket->statusCode);
462 
463  //All frames sent from the client to the server are masked
464  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
465  {
466  //Apply masking
467  p[0] ^= webSocket->txContext.maskingKey[0];
468  p[1] ^= webSocket->txContext.maskingKey[1];
469  }
470 
471  //Adjust the length of the frame
472  webSocket->txContext.bufferLen += sizeof(uint16_t);
473  }
474 
475  //Return status code
476  return error;
477 }
478 
479 #endif
error_t webSocketFormatFrameHeader(WebSocket *webSocket, bool_t fin, WebSocketFrameType type, size_t payloadLen)
Format WebSocket frame header.
int bool_t
Definition: compiler_port.h:49
error_t webSocketFormatCloseFrame(WebSocket *webSocket)
Format a Close frame.
bool_t mask
Defines whether the payload data is masked.
Definition: web_socket.h:400
uint8_t maskingKey[4]
Masking key.
Definition: web_socket.h:401
@ ERROR_UNEXPECTED_MESSAGE
Definition: error.h:192
uint8_t p
Definition: ndp.h:298
WebSocket API (client and server)
#define TRUE
Definition: os_port.h:50
bool_t webSocketCheckUtf8Stream(WebSocketUtf8Context *context, const uint8_t *data, size_t length, size_t remaining)
Check whether a an UTF-8 stream is valid.
__start_packed struct @300 WebSocketFrame
WebSocket frame.
Helper functions for WebSockets.
@ WS_STATUS_CODE_NO_STATUS_RCVD
Definition: web_socket.h:283
@ ERROR_END_OF_STREAM
Definition: error.h:208
@ ERROR_PRNG_NOT_READY
Definition: error.h:245
WebSocketFrameType controlFrameType
Control frame type.
Definition: web_socket.h:398
WebSocket frame parsing and formatting.
#define LOAD64BE(p)
Definition: cpu_endian.h:230
#define FALSE
Definition: os_port.h:46
@ ERROR_INVALID_FRAME
Definition: error.h:85
char_t type
size_t bufferLen
Length of the data buffer.
Definition: web_socket.h:405
error_t
Error codes.
Definition: error.h:42
Frame encoding/decoding context.
Definition: web_socket.h:394
bool_t webSocketCheckStatusCode(uint16_t statusCode)
Check whether a status code is valid.
uint16_t statusCode
WebSocketFrameType
WebSocket frame types.
Definition: web_socket.h:262
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
#define STORE16BE(a, p)
Definition: cpu_endian.h:246
@ WS_FRAME_TYPE_PING
Definition: web_socket.h:268
WebSocketRandCallback webSockRandCallback
Definition: web_socket.c:52
@ WS_FRAME_TYPE_TEXT
Definition: web_socket.h:265
WebSocketFrameType dataFrameType
FSM state.
Definition: web_socket.h:397
size_t payloadLen
Payload length.
Definition: web_socket.h:402
#define MSB(x)
Definition: os_port.h:58
@ WS_FRAME_TYPE_PONG
Definition: web_socket.h:269
#define LSB(x)
Definition: os_port.h:54
@ WS_ENDPOINT_CLIENT
Definition: web_socket.h:191
#define WebSocket
Definition: web_socket.h:177
error_t webSocketParseFrameHeader(WebSocket *webSocket, const WebSocketFrame *frame, WebSocketFrameType *type)
Parse WebSocket frame header.
@ WS_FRAME_TYPE_CLOSE
Definition: web_socket.h:267
#define TRACE_DEBUG(...)
Definition: debug.h:106
#define STORE64BE(a, p)
Definition: cpu_endian.h:306
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:107
@ WS_STATUS_CODE_INVALID_PAYLOAD_DATA
Definition: web_socket.h:285
uint8_t n
uint8_t buffer[WEB_SOCKET_BUFFER_SIZE]
Data buffer.
Definition: web_socket.h:404
@ WS_FRAME_TYPE_CONTINUATION
Definition: web_socket.h:264
bool_t fin
Final fragment in a message.
Definition: web_socket.h:399
@ WS_STATUS_CODE_PROTOCOL_ERROR
Definition: web_socket.h:281
WebSocket transport layer.
size_t bufferPos
Current position.
Definition: web_socket.h:406
uint8_t fin
Definition: web_socket.h:315
uint8_t payloadLen
Definition: ipv6.h:343
#define LOAD16BE(p)
Definition: cpu_endian.h:170
TCP/IP stack core.
@ WS_STATUS_CODE_NORMAL_CLOSURE
Definition: web_socket.h:279
@ WS_FRAME_TYPE_BINARY
Definition: web_socket.h:266
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.