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-2024 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 2.4.4
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  osMemcpy(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  osMemcpy(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
339  if(webSocketCheckStatusCode(statusCode))
340  {
341  webSocket->statusCode = statusCode;
342  }
343  else
344  {
345  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
346  }
347 
348  //The body may contain UTF-8-encoded data
349  if(rxContext->payloadLen > 2)
350  {
351  //Compute the length of the data
352  k = rxContext->payloadLen - 2;
353 
354  //Invalid UTF-8 sequence?
355  if(!webSocketCheckUtf8Stream(&webSocket->utf8Context,
356  (uint8_t *) frame + n + 2, k, k))
357  {
358  //The received data is not consistent with the type of the message
359  webSocket->statusCode = WS_STATUS_CODE_INVALID_PAYLOAD_DATA;
360  }
361  }
362  }
363 
364  //A Close frame has been received
365  webSocket->handshakeContext.closingFrameReceived = TRUE;
366  //Exit immediately
367  return ERROR_END_OF_STREAM;
368  }
369  else if(frame->opcode == WS_FRAME_TYPE_PING)
370  {
371  //Save the Opcode field
373 
374  //Control frames must not be fragmented
375  if(!frame->fin)
376  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
377 
378  //All control frames must have a payload length of 125 bytes or less
379  if(frame->payloadLen > 125)
380  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
381  }
382  else if(frame->opcode == WS_FRAME_TYPE_PONG)
383  {
384  //Save the Opcode field
386 
387  //Control frames must not be fragmented
388  if(!frame->fin)
389  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
390 
391  //All control frames must have a payload length of 125 bytes or less
392  if(frame->payloadLen > 125)
393  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
394  }
395  else
396  {
397  //If an unknown opcode is received, the receiving endpoint must fail
398  //the WebSocket connection
399  webSocket->statusCode = WS_STATUS_CODE_PROTOCOL_ERROR;
400  }
401 
402  //Save the Mask flag
403  rxContext->mask = frame->mask;
404 
405  //Control frame?
407  {
408  //Return frame type
409  if(type != NULL)
410  *type = rxContext->controlFrameType;
411  }
412  else
413  {
414  //Save the FIN flag
415  rxContext->fin = frame->fin;
416 
417  //Return frame type
418  if(type != NULL)
419  *type = rxContext->dataFrameType;
420  }
421 
422  //Check status code
423  if(webSocket->statusCode == WS_STATUS_CODE_NO_STATUS_RCVD)
424  {
425  return NO_ERROR;
426  }
427  else if(webSocket->statusCode == WS_STATUS_CODE_NORMAL_CLOSURE)
428  {
429  return ERROR_END_OF_STREAM;
430  }
431  else if(webSocket->statusCode == WS_STATUS_CODE_PROTOCOL_ERROR)
432  {
433  return ERROR_INVALID_FRAME;
434  }
435  else
436  {
437  return ERROR_FAILURE;
438  }
439 }
440 
441 
442 /**
443  * @brief Format a Close frame
444  * @param[in] webSocket Handle to a WebSocket
445  * @return Error code
446  **/
447 
449 {
450  error_t error;
451  uint8_t *p;
452 
453  //Format Close frame
454  error = webSocketFormatFrameHeader(webSocket,
455  TRUE, WS_FRAME_TYPE_CLOSE, sizeof(uint16_t));
456 
457  //Check status code
458  if(!error)
459  {
460  //1005 is a reserved value and must not be set as a status code in
461  //a Close control frame by an endpoint
462  if(webSocket->statusCode == WS_STATUS_CODE_NO_STATUS_RCVD)
463  webSocket->statusCode = WS_STATUS_CODE_NORMAL_CLOSURE;
464 
465  //Debug message
466  TRACE_DEBUG(" Status Code = %u\r\n", webSocket->statusCode);
467 
468  //Point to end of the WebSocket frame header
469  p = webSocket->txContext.buffer + webSocket->txContext.bufferLen;
470 
471  //Write status code
472  p[0] = MSB(webSocket->statusCode);
473  p[1] = LSB(webSocket->statusCode);
474 
475  //All frames sent from the client to the server are masked
476  if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
477  {
478  //Apply masking
479  p[0] ^= webSocket->txContext.maskingKey[0];
480  p[1] ^= webSocket->txContext.maskingKey[1];
481  }
482 
483  //Adjust the length of the frame
484  webSocket->txContext.bufferLen += sizeof(uint16_t);
485  }
486 
487  //Return status code
488  return error;
489 }
490 
491 #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:53
error_t webSocketFormatCloseFrame(WebSocket *webSocket)
Format a Close frame.
bool_t mask
Defines whether the payload data is masked.
Definition: web_socket.h:404
uint8_t maskingKey[4]
Masking key.
Definition: web_socket.h:405
@ ERROR_UNEXPECTED_MESSAGE
Definition: error.h:194
uint8_t p
Definition: ndp.h:300
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.
Helper functions for WebSockets.
uint8_t type
Definition: coap_common.h:176
@ WS_STATUS_CODE_NO_STATUS_RCVD
Definition: web_socket.h:283
@ ERROR_END_OF_STREAM
Definition: error.h:210
@ ERROR_PRNG_NOT_READY
Definition: error.h:251
WebSocketFrameType controlFrameType
Control frame type.
Definition: web_socket.h:402
WebSocket frame parsing and formatting.
#define LOAD64BE(p)
Definition: cpu_endian.h:246
#define FALSE
Definition: os_port.h:46
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
@ ERROR_INVALID_FRAME
Definition: error.h:86
size_t bufferLen
Length of the data buffer.
Definition: web_socket.h:409
error_t
Error codes.
Definition: error.h:43
Frame encoding/decoding context.
Definition: web_socket.h:399
bool_t webSocketCheckStatusCode(uint16_t statusCode)
Check whether a status code is valid.
WebSocketFrameType
WebSocket frame types.
Definition: web_socket.h:263
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
#define STORE16BE(a, p)
Definition: cpu_endian.h:262
@ 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:401
size_t payloadLen
Payload length.
Definition: web_socket.h:406
#define MSB(x)
Definition: os_port.h:59
@ WS_FRAME_TYPE_PONG
Definition: web_socket.h:269
#define LSB(x)
Definition: os_port.h:55
@ 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:107
#define STORE64BE(a, p)
Definition: cpu_endian.h:322
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:108
WebSocketFrame
Definition: web_socket.h:322
@ 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:408
@ WS_FRAME_TYPE_CONTINUATION
Definition: web_socket.h:264
bool_t fin
Final fragment in a message.
Definition: web_socket.h:403
@ WS_STATUS_CODE_PROTOCOL_ERROR
Definition: web_socket.h:281
WebSocket transport layer.
size_t bufferPos
Current position.
Definition: web_socket.h:410
uint8_t fin
Definition: web_socket.h:317
uint16_t payloadLen
Definition: ipv6.h:281
#define LOAD16BE(p)
Definition: cpu_endian.h:186
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.