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