1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX WebSocket Component */
16 /** */
17 /** WebSocket Protocol */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_WEBSOCKET_CLIENT_SOURCE_CODE
23
24 /* Force error checking to be disabled in this module */
25
26 #ifndef NX_DISABLE_ERROR_CHECKING
27 #define NX_DISABLE_ERROR_CHECKING
28 #endif
29
30 /* Include necessary system files. */
31
32 #include "tx_api.h"
33 #include "nx_ip.h"
34 #include "nx_packet.h"
35 #include "nx_tcp.h"
36 #include "nx_websocket_client.h"
37
38
39 /* Bring in externs for caller checking code. */
40
41 NX_CALLER_CHECKING_EXTERNS
42
43 #define NX_WEBSOCKET_CRLF "\r\n"
44 #define NX_WEBSOCKET_CRLF_SIZE 2
45
46 #define NX_WEBSOCKET_HEADER_MINIMUM_LENGTH 2
47
48 #define NX_WEBSOCKET_ACCEPT_PREDEFINED_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
49 #define NX_WEBSOCKET_ACCEPT_PREDEFINED_GUID_SIZE (sizeof(NX_WEBSOCKET_ACCEPT_PREDEFINED_GUID) - 1)
50 #define NX_WEBSOCKET_ACCEPT_DIGEST_SIZE 20 /* The length of SHA-1 hash is 20 bytes */
51 #define NX_WEBSOCKET_ACCEPT_KEY_SIZE 28 /* The base64 encode key for 20 bytes digest requires (27 bytes name size + 1 byte pad) = 28 bytes */
52
53 /**************************************************************************/
54 /* */
55 /* FUNCTION RELEASE */
56 /* */
57 /* _nxe_websocket_client_create PORTABLE C */
58 /* 6.2.0 */
59 /* AUTHOR */
60 /* */
61 /* Bo Chen, Microsoft Corporation */
62 /* */
63 /* DESCRIPTION */
64 /* */
65 /* This function checks for errors in the WebSocket instance create */
66 /* function call. */
67 /* */
68 /* INPUT */
69 /* */
70 /* client_ptr Pointer to WebSocket Client */
71 /* client_name Name of this WebSocket */
72 /* ip_ptr Pointer to IP instance */
73 /* pool_ptr Pointer to packet pool */
74 /* */
75 /* OUTPUT */
76 /* */
77 /* status Completion status */
78 /* */
79 /* CALLS */
80 /* */
81 /* _nx_websocket_client_create Actual websocket create call */
82 /* */
83 /* CALLED BY */
84 /* */
85 /* Application Code */
86 /* */
87 /* RELEASE HISTORY */
88 /* */
89 /* DATE NAME DESCRIPTION */
90 /* */
91 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
92 /* */
93 /**************************************************************************/
_nxe_websocket_client_create(NX_WEBSOCKET_CLIENT * client_ptr,UCHAR * client_name,NX_IP * ip_ptr,NX_PACKET_POOL * pool_ptr)94 UINT _nxe_websocket_client_create(NX_WEBSOCKET_CLIENT *client_ptr, UCHAR *client_name, NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr)
95 {
96
97 UINT status;
98
99 /* Check for invalid input pointers. */
100 if ((ip_ptr == NX_NULL) || (ip_ptr -> nx_ip_id != NX_IP_ID) ||
101 (client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id == NX_WEBSOCKET_CLIENT_ID) ||
102 (pool_ptr == NX_NULL) || (pool_ptr -> nx_packet_pool_id != NX_PACKET_POOL_ID))
103 {
104 return(NX_PTR_ERROR);
105 }
106
107 /* Call actual client create function. */
108 status = _nx_websocket_client_create(client_ptr, client_name, ip_ptr, pool_ptr);
109
110 /* Return completion status. */
111 return(status);
112 }
113
114 /**************************************************************************/
115 /* */
116 /* FUNCTION RELEASE */
117 /* */
118 /* _nx_websocket_client_create PORTABLE C */
119 /* 6.2.0 */
120 /* AUTHOR */
121 /* */
122 /* Bo Chen, Microsoft Corporation */
123 /* */
124 /* DESCRIPTION */
125 /* */
126 /* This function creates an instance for WebSocket Client. */
127 /* */
128 /* INPUT */
129 /* */
130 /* client_ptr Pointer to WebSocket Client */
131 /* client_name Name of this WebSocket */
132 /* ip_ptr Pointer to IP instance */
133 /* pool_ptr Pointer to packet pool */
134 /* */
135 /* OUTPUT */
136 /* */
137 /* status Completion status */
138 /* */
139 /* CALLS */
140 /* */
141 /* None */
142 /* */
143 /* CALLED BY */
144 /* */
145 /* Application Code */
146 /* */
147 /* RELEASE HISTORY */
148 /* */
149 /* DATE NAME DESCRIPTION */
150 /* */
151 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
152 /* */
153 /**************************************************************************/
_nx_websocket_client_create(NX_WEBSOCKET_CLIENT * client_ptr,UCHAR * client_name,NX_IP * ip_ptr,NX_PACKET_POOL * pool_ptr)154 UINT _nx_websocket_client_create(NX_WEBSOCKET_CLIENT *client_ptr, UCHAR *client_name, NX_IP *ip_ptr, NX_PACKET_POOL *pool_ptr)
155 {
156
157 UINT status;
158
159 /* Clear the WebSocket structure. */
160 memset((void *)client_ptr, 0, sizeof(NX_WEBSOCKET_CLIENT));
161
162 /* Create WebSocket mutex. */
163 status = tx_mutex_create(&client_ptr -> nx_websocket_client_mutex, (CHAR *)client_name, TX_NO_INHERIT);
164 if (status)
165 {
166 return(status);
167 }
168
169 /* Save the Client name. */
170 client_ptr -> nx_websocket_client_name = client_name;
171
172 /* Save the IP pointer address. */
173 client_ptr -> nx_websocket_client_ip_ptr = ip_ptr;
174
175 /* Save the packet pool pointer. */
176 client_ptr -> nx_websocket_client_packet_pool_ptr = pool_ptr;
177
178 /* Set the Client ID to indicate the WebSocket client thread is ready. */
179 client_ptr -> nx_websocket_client_id = NX_WEBSOCKET_CLIENT_ID;
180
181 /* Update the state. */
182 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_IDLE;
183
184 /* Return successful completion. */
185 return(NX_SUCCESS);
186 }
187
188 /**************************************************************************/
189 /* */
190 /* FUNCTION RELEASE */
191 /* */
192 /* _nxe_websocket_client_delete PORTABLE C */
193 /* 6.2.0 */
194 /* AUTHOR */
195 /* */
196 /* Bo Chen, Microsoft Corporation */
197 /* */
198 /* DESCRIPTION */
199 /* */
200 /* This function checks for errors in the WebSocket instance delete */
201 /* function call. */
202 /* */
203 /* */
204 /* INPUT */
205 /* */
206 /* client_ptr Pointer to WebSocket Client */
207 /* */
208 /* OUTPUT */
209 /* */
210 /* status Completion status */
211 /* */
212 /* CALLS */
213 /* */
214 /* _nx_websocket_client_delete Actual websocket delete call */
215 /* */
216 /* CALLED BY */
217 /* */
218 /* Application Code */
219 /* */
220 /* RELEASE HISTORY */
221 /* */
222 /* DATE NAME DESCRIPTION */
223 /* */
224 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
225 /* */
226 /**************************************************************************/
_nxe_websocket_client_delete(NX_WEBSOCKET_CLIENT * client_ptr)227 UINT _nxe_websocket_client_delete(NX_WEBSOCKET_CLIENT *client_ptr)
228 {
229
230 UINT status;
231
232 /* Check for invalid input pointers. */
233 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID))
234 {
235 return(NX_PTR_ERROR);
236 }
237
238 /* Call actual client delete function. */
239 status = _nx_websocket_client_delete(client_ptr);
240
241 /* Return completion status. */
242 return(status);
243 }
244
245 /**************************************************************************/
246 /* */
247 /* FUNCTION RELEASE */
248 /* */
249 /* _nx_websocket_client_delete PORTABLE C */
250 /* 6.2.0 */
251 /* AUTHOR */
252 /* */
253 /* Bo Chen, Microsoft Corporation */
254 /* */
255 /* DESCRIPTION */
256 /* */
257 /* This function deletes an instance for WebSocket Client. */
258 /* */
259 /* INPUT */
260 /* */
261 /* client_ptr Pointer to WebSocket Client */
262 /* */
263 /* OUTPUT */
264 /* */
265 /* _nx_websocket_client_cleanup Cleanup unused resources */
266 /* */
267 /* CALLS */
268 /* */
269 /* None */
270 /* */
271 /* CALLED BY */
272 /* */
273 /* Application Code */
274 /* */
275 /* RELEASE HISTORY */
276 /* */
277 /* DATE NAME DESCRIPTION */
278 /* */
279 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
280 /* */
281 /**************************************************************************/
_nx_websocket_client_delete(NX_WEBSOCKET_CLIENT * client_ptr)282 UINT _nx_websocket_client_delete(NX_WEBSOCKET_CLIENT *client_ptr)
283 {
284
285 /* Obtain the mutex. */
286 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
287
288 /* Clean up unused resources. */
289 _nx_websocket_client_cleanup(client_ptr);
290
291 /* Set the Client ID to a default value. */
292 client_ptr -> nx_websocket_client_id = 0;
293
294 /* Update the state. */
295 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_INITIALIZE;
296
297 /* Release the mutex, delete the mutex and assign the pointer to NULL. */
298 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
299 tx_mutex_delete(&(client_ptr -> nx_websocket_client_mutex));
300
301 /* Return successful completion. */
302 return(NX_SUCCESS);
303 }
304
305 /**************************************************************************/
306 /* */
307 /* FUNCTION RELEASE */
308 /* */
309 /* _nxe_websocket_client_connect PORTABLE C */
310 /* 6.2.0 */
311 /* AUTHOR */
312 /* */
313 /* Bo Chen, Microsoft Corporation */
314 /* */
315 /* DESCRIPTION */
316 /* */
317 /* This function checks for errors in the WebSocket connect. */
318 /* */
319 /* INPUT */
320 /* */
321 /* client_ptr Pointer to WebSocket Client */
322 /* socket_ptr Pointer to TCP socket */
323 /* host Pointer to host */
324 /* host_length Length of host */
325 /* uri_path Pointer to uri path */
326 /* uri_path_length Length of uri path */
327 /* protocol Pointer to protocol */
328 /* protocol_length Length of protocol */
329 /* wait_option Wait option */
330 /* */
331 /* OUTPUT */
332 /* */
333 /* status Completion status */
334 /* */
335 /* CALLS */
336 /* */
337 /* _nx_websocket_client_connect Actual websocket connect call */
338 /* */
339 /* CALLED BY */
340 /* */
341 /* Application Code */
342 /* */
343 /* RELEASE HISTORY */
344 /* */
345 /* DATE NAME DESCRIPTION */
346 /* */
347 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
348 /* */
349 /**************************************************************************/
_nxe_websocket_client_connect(NX_WEBSOCKET_CLIENT * client_ptr,NX_TCP_SOCKET * socket_ptr,UCHAR * host,UINT host_length,UCHAR * uri_path,UINT uri_path_length,UCHAR * protocol,UINT protocol_length,UINT wait_option)350 UINT _nxe_websocket_client_connect(NX_WEBSOCKET_CLIENT *client_ptr, NX_TCP_SOCKET *socket_ptr,
351 UCHAR *host, UINT host_length,
352 UCHAR *uri_path, UINT uri_path_length,
353 UCHAR *protocol, UINT protocol_length,UINT wait_option)
354 {
355
356 UINT status;
357
358
359 /* Check for invalid input pointers. */
360 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID) ||
361 (socket_ptr == NX_NULL) || (socket_ptr -> nx_tcp_socket_id != NX_TCP_ID) ||
362 (host == NX_NULL) || (host_length == 0) ||
363 (uri_path == NX_NULL) || (uri_path_length == 0) ||
364 (protocol == NX_NULL) || (protocol_length == 0))
365 {
366 return(NX_PTR_ERROR);
367 }
368
369 /* Call actual connect function. */
370 status = _nx_websocket_client_connect(client_ptr, socket_ptr, host, host_length, uri_path, uri_path_length, protocol, protocol_length, wait_option);
371
372 /* Return completion status. */
373 return(status);
374 }
375
376 /**************************************************************************/
377 /* */
378 /* FUNCTION RELEASE */
379 /* */
380 /* _nx_websocket_client_connect PORTABLE C */
381 /* 6.2.0 */
382 /* AUTHOR */
383 /* */
384 /* Bo Chen, Microsoft Corporation */
385 /* */
386 /* DESCRIPTION */
387 /* */
388 /* This function makes a WebSocket connection over TCP socket to the */
389 /* server. */
390 /* Note: Application must establish a TCP connection before. */
391 /* */
392 /* INPUT */
393 /* */
394 /* client_ptr Pointer to WebSocket Client */
395 /* socket_ptr Pointer to TCP socket */
396 /* host Pointer to host */
397 /* host_length Length of host */
398 /* uri_path Pointer to uri path */
399 /* uri_path_length Length of uri path */
400 /* protocol Pointer to protocol */
401 /* protocol_length Length of protocol */
402 /* wait_option Wait option */
403 /* */
404 /* OUTPUT */
405 /* */
406 /* status Completion status */
407 /* */
408 /* CALLS */
409 /* */
410 /* _nx_websocket_client_connect_internal Make websocket connection */
411 /* */
412 /* CALLED BY */
413 /* */
414 /* Application Code */
415 /* */
416 /* RELEASE HISTORY */
417 /* */
418 /* DATE NAME DESCRIPTION */
419 /* */
420 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
421 /* */
422 /**************************************************************************/
_nx_websocket_client_connect(NX_WEBSOCKET_CLIENT * client_ptr,NX_TCP_SOCKET * socket_ptr,UCHAR * host,UINT host_length,UCHAR * resource,UINT resource_length,UCHAR * protocol,UINT protocol_length,UINT wait_option)423 UINT _nx_websocket_client_connect(NX_WEBSOCKET_CLIENT *client_ptr, NX_TCP_SOCKET *socket_ptr,
424 UCHAR *host, UINT host_length,
425 UCHAR *resource, UINT resource_length,
426 UCHAR *protocol, UINT protocol_length,UINT wait_option)
427 {
428
429 UINT status;
430
431
432 /* Obtain the mutex. */
433 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
434
435 /* Check the state. */
436 if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_CONNECTED)
437 {
438
439 /* Release the mutex and return */
440 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
441 return(NX_WEBSOCKET_ALREADY_CONNECTED);
442 }
443 else if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_CONNECTING)
444 {
445
446 /* Release the mutex and return */
447 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
448 return(NX_WEBSOCKET_CONNECTING);
449 }
450 else if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_INITIALIZE)
451 {
452
453 /* Release the mutex and return */
454 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
455 return(NX_WEBSOCKET_INVALID_STATE);
456 }
457
458 /* Save the socket pointer. */
459 client_ptr -> nx_websocket_client_socket_ptr = socket_ptr;
460
461 #ifdef NX_SECURE_ENABLE
462 client_ptr -> nx_websocket_client_use_tls = NX_FALSE;
463 #endif /* NX_SECURE_ENABLE */
464
465 status = _nx_websocket_client_connect_internal(client_ptr, host, host_length, resource, resource_length, protocol, protocol_length, wait_option);
466
467 /* Release the mutex and return */
468 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
469 return(status);
470 }
471
472 /**************************************************************************/
473 /* */
474 /* FUNCTION RELEASE */
475 /* */
476 /* _nx_websocket_client_connect_internal PORTABLE C */
477 /* 6.2.0 */
478 /* AUTHOR */
479 /* */
480 /* Bo Chen, Microsoft Corporation */
481 /* */
482 /* DESCRIPTION */
483 /* */
484 /* This function makes a WebSocket connection over TCP socket or TLS */
485 /* session to the server. */
486 /* */
487 /* INPUT */
488 /* */
489 /* client_ptr Pointer to WebSocket Client */
490 /* host Pointer to host */
491 /* host_length Length of host */
492 /* uri_path Pointer to uri path */
493 /* uri_path_length Length of uri path */
494 /* protocol Pointer to protocol */
495 /* protocol_length Length of protocol */
496 /* wait_option Wait option */
497 /* */
498 /* OUTPUT */
499 /* */
500 /* status Completion status */
501 /* */
502 /* CALLS */
503 /* */
504 /* nx_packet_allocate Allocate a packet */
505 /* nx_packet_release Release the packet */
506 /* nx_secure_tls_packet_allocate Allocate a TLS packet */
507 /* nx_packet_data_append Append data */
508 /* _nx_utility_base64_encode Base64 encode */
509 /* _nx_websocket_client_packet_send Send out websocket packet */
510 /* _nx_websocket_client_packet_receive Receive a websocket packet */
511 /* _nx_websocket_client_connect_response_check */
512 /* Process connect response */
513 /* _nx_websocket_client_cleanup Cleanup resources */
514 /* */
515 /* CALLED BY */
516 /* */
517 /* _nx_websocket_client_connect Make websocket connection */
518 /* _nx_websocket_client_secure_connect Make secure websocket */
519 /* connection */
520 /* */
521 /* RELEASE HISTORY */
522 /* */
523 /* DATE NAME DESCRIPTION */
524 /* */
525 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
526 /* */
527 /**************************************************************************/
_nx_websocket_client_connect_internal(NX_WEBSOCKET_CLIENT * client_ptr,UCHAR * host,UINT host_length,UCHAR * uri_path,UINT uri_path_length,UCHAR * protocol,UINT protocol_length,UINT wait_option)528 UINT _nx_websocket_client_connect_internal(NX_WEBSOCKET_CLIENT *client_ptr,
529 UCHAR *host, UINT host_length,
530 UCHAR *uri_path, UINT uri_path_length,
531 UCHAR *protocol, UINT protocol_length,UINT wait_option)
532 {
533
534 UINT i;
535 UINT status;
536 NX_PACKET *packet_ptr;
537
538
539 /* To guarantee resources are cleaned-up if a re-connect happens. */
540 _nx_websocket_client_cleanup(client_ptr);
541
542 /* Generate GUID for WebSocket Key. */
543 for (i = 0; i < NX_WEBSOCKET_CLIENT_GUID_SIZE; i ++)
544 {
545 client_ptr -> nx_websocket_client_guid[i] = (UCHAR)(NX_RAND());
546 }
547
548 /* Encode the GUID as key. */
549 _nx_utility_base64_encode(client_ptr -> nx_websocket_client_guid, NX_WEBSOCKET_CLIENT_GUID_SIZE,
550 client_ptr -> nx_websocket_client_key, NX_WEBSOCKET_CLIENT_KEY_SIZE,
551 &client_ptr -> nx_websocket_client_key_size);
552
553 /* Release the mutex */
554 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
555
556 /* Allocate a packet. */
557 #ifdef NX_SECURE_ENABLE
558 if (client_ptr -> nx_websocket_client_use_tls)
559 {
560
561 /* Use TLS packet allocate. The TLS packet allocate is able to count for
562 TLS-related header space including crypto initial vector area. */
563 status = nx_secure_tls_packet_allocate(client_ptr -> nx_websocket_client_tls_session_ptr,
564 client_ptr -> nx_websocket_client_packet_pool_ptr,
565 &packet_ptr, TX_WAIT_FOREVER);
566 }
567 else
568 {
569 #endif /* NX_SECURE_ENABLE */
570
571 /* Allocate packet. */
572 if (client_ptr -> nx_websocket_client_socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
573 {
574 status = nx_packet_allocate(client_ptr -> nx_websocket_client_packet_pool_ptr,
575 &packet_ptr,
576 NX_IPv4_TCP_PACKET, wait_option);
577 }
578 else
579 {
580 status = nx_packet_allocate(client_ptr -> nx_websocket_client_packet_pool_ptr,
581 &packet_ptr,
582 NX_IPv6_TCP_PACKET, wait_option);
583 }
584 #ifdef NX_SECURE_ENABLE
585 }
586 #endif /* NX_SECURE_ENABLE */
587
588 /* Check status. */
589 if (status)
590 {
591
592 /* Obtain the mutex again and return error status. */
593 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
594 return(status);
595 }
596
597 /* WebSocket opening handshake message format:
598 GET /mqtt HTTP/1.1\r\n
599 Host: example.com\r\n
600 Upgrade: websocket\r\n
601 connection: Upgrade\r\n
602 Sec-WebSocket-Key: xxxxxxx=\r\n
603 Sec-WebSocket-Protocol: mqtt\r\n
604 Sec-WebSocket-Version: 13\r\n
605 \r\n
606 */
607
608 /* Build the GET request: Get + Request URI + HTTP version. */
609 status = nx_packet_data_append(packet_ptr, "GET ", sizeof("GET ") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
610 status += nx_packet_data_append(packet_ptr, uri_path, uri_path_length, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
611 status += nx_packet_data_append(packet_ptr, " HTTP/1.1", sizeof(" HTTP/1.1") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
612 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
613
614 /* Place the Host in the header. */
615 status += nx_packet_data_append(packet_ptr, "Host: ", sizeof("Host: ") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
616 status += nx_packet_data_append(packet_ptr, host, host_length, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
617 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
618
619 /* Place the Upgrade in the header. */
620 status += nx_packet_data_append(packet_ptr, "Upgrade: websocket", sizeof("Upgrade: websocket") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
621 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
622
623 /* Place the Connection in the header. */
624 status += nx_packet_data_append(packet_ptr, "Connection: Upgrade", sizeof("Connection: Upgrade") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
625 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
626
627 /* Place the Sec-WebSocket-Key in the header. */
628 status += nx_packet_data_append(packet_ptr, "Sec-WebSocket-Key: ", sizeof("Sec-WebSocket-Key: ") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
629 status += nx_packet_data_append(packet_ptr, client_ptr -> nx_websocket_client_key, client_ptr -> nx_websocket_client_key_size, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
630 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
631
632 /* Place the connection in the header. */
633 status += nx_packet_data_append(packet_ptr, "Sec-WebSocket-Protocol: ", sizeof("Sec-WebSocket-Protocol: ") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
634 status += nx_packet_data_append(packet_ptr, protocol, protocol_length, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
635 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
636
637 /* Place the connection in the header. */
638 status += nx_packet_data_append(packet_ptr, "Sec-WebSocket-Version: 13", sizeof("Sec-WebSocket-Version: 13") - 1, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
639 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
640
641 /* Fill the last \r\n. */
642 status += nx_packet_data_append(packet_ptr, NX_WEBSOCKET_CRLF, NX_WEBSOCKET_CRLF_SIZE, client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
643
644 /* Check status. */
645 if (status)
646 {
647
648 /* Obtain the mutex, release the packet and return error status. */
649 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
650 nx_packet_release(packet_ptr);
651 return(NX_WEBSOCKET_DATA_APPEND_FAILURE);
652 }
653
654 /* Send out the packet. */
655 status = _nx_websocket_client_packet_send(client_ptr, packet_ptr, wait_option);
656 if (status)
657 {
658
659 /* Release the packet and return error status. */
660 nx_packet_release(packet_ptr);
661 return(status);
662 }
663
664 /* Obtain the mutex again. */
665 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
666
667 /* Update the subprotocol name and length */
668 client_ptr -> nx_websocket_client_subprotocol = protocol;
669 client_ptr -> nx_websocket_client_subprotocol_length = protocol_length;
670
671 /* Update the state. */
672 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_CONNECTING;
673
674 /* Set the frame flag to be unfragmented and corresponding opcode to be zero */
675 client_ptr -> nx_websocket_client_frame_fragmented = NX_FALSE;
676 client_ptr -> nx_websocket_client_frame_opcode = 0;
677
678 /* Check if using non-blocking mode. */
679 if (wait_option == 0)
680 {
681 return(NX_IN_PROGRESS);
682 }
683
684 while (1)
685 {
686
687 /* Receive response. */
688 status = _nx_websocket_client_packet_receive(client_ptr, &packet_ptr, wait_option);
689 if (status)
690 {
691 return(status);
692 }
693
694 /* Process the response. */
695 status = _nx_websocket_client_connect_response_check(client_ptr, packet_ptr, wait_option);
696
697 /* If status is NX_IN_PROGRESS, continue to receive connect response.
698 Otherwise, break the while loop. */
699 if (status != NX_IN_PROGRESS)
700 {
701 break;
702 }
703 }
704
705 /* Return status. */
706 return(status);
707 }
708
709 #ifdef NX_SECURE_ENABLE
710 /**************************************************************************/
711 /* */
712 /* FUNCTION RELEASE */
713 /* */
714 /* _nxe_websocket_client_secure_connect PORTABLE C */
715 /* 6.2.0 */
716 /* AUTHOR */
717 /* */
718 /* Bo Chen, Microsoft Corporation */
719 /* */
720 /* DESCRIPTION */
721 /* */
722 /* This function checks for errors in the WebSocket secure connect. */
723 /* */
724 /* INPUT */
725 /* */
726 /* client_ptr Pointer to WebSocket Client */
727 /* tls_session Pointer to TLS session */
728 /* host Pointer to host */
729 /* host_length Length of host */
730 /* uri_path Pointer to uri path */
731 /* uri_path_length Length of uri path */
732 /* protocol Pointer to protocol */
733 /* protocol_length Length of protocol */
734 /* wait_option Wait option */
735 /* */
736 /* OUTPUT */
737 /* */
738 /* status Completion status */
739 /* */
740 /* CALLS */
741 /* */
742 /* _nx_websocket_client_secure_connect Actual websocket connect call */
743 /* */
744 /* CALLED BY */
745 /* */
746 /* Application Code */
747 /* */
748 /* RELEASE HISTORY */
749 /* */
750 /* DATE NAME DESCRIPTION */
751 /* */
752 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
753 /* */
754 /**************************************************************************/
_nxe_websocket_client_secure_connect(NX_WEBSOCKET_CLIENT * client_ptr,NX_SECURE_TLS_SESSION * tls_session,UCHAR * host,UINT host_length,UCHAR * uri_path,UINT uri_path_length,UCHAR * protocol,UINT protocol_length,UINT wait_option)755 UINT _nxe_websocket_client_secure_connect(NX_WEBSOCKET_CLIENT *client_ptr, NX_SECURE_TLS_SESSION *tls_session,
756 UCHAR *host, UINT host_length,
757 UCHAR *uri_path, UINT uri_path_length,
758 UCHAR *protocol, UINT protocol_length,UINT wait_option)
759 {
760
761 UINT status;
762
763
764 /* Check for invalid input pointers. */
765 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID) ||
766 (tls_session == NX_NULL) ||
767 (host == NX_NULL) || (host_length == 0) ||
768 (uri_path == NX_NULL) || (uri_path_length == 0) ||
769 (protocol == NX_NULL) || (protocol_length == 0))
770 {
771 return(NX_PTR_ERROR);
772 }
773
774 /* Call actual secure connect function. */
775 status = _nx_websocket_client_secure_connect(client_ptr, tls_session, host, host_length, uri_path, uri_path_length, protocol, protocol_length, wait_option);
776
777 /* Return completion status. */
778 return(status);
779 }
780
781 /**************************************************************************/
782 /* */
783 /* FUNCTION RELEASE */
784 /* */
785 /* _nx_websocket_client_secure_connect PORTABLE C */
786 /* 6.2.0 */
787 /* AUTHOR */
788 /* */
789 /* Bo Chen, Microsoft Corporation */
790 /* */
791 /* DESCRIPTION */
792 /* */
793 /* This function makes a WebSocket connection over TLS session to the */
794 /* server. */
795 /* Note: Application must establish a TLS connection before. */
796 /* */
797 /* INPUT */
798 /* */
799 /* client_ptr Pointer to WebSocket Client */
800 /* tls_session Pointer to TLS session */
801 /* host Pointer to host */
802 /* host_length Length of host */
803 /* uri_path Pointer to uri path */
804 /* uri_path_length Length of uri path */
805 /* protocol Pointer to protocol */
806 /* protocol_length Length of protocol */
807 /* wait_option Wait option */
808 /* */
809 /* OUTPUT */
810 /* */
811 /* status Completion status */
812 /* */
813 /* CALLS */
814 /* */
815 /* _nx_websocket_client_connect_internal Make websocket connection */
816 /* */
817 /* CALLED BY */
818 /* */
819 /* Application Code */
820 /* */
821 /* RELEASE HISTORY */
822 /* */
823 /* DATE NAME DESCRIPTION */
824 /* */
825 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
826 /* */
827 /**************************************************************************/
_nx_websocket_client_secure_connect(NX_WEBSOCKET_CLIENT * client_ptr,NX_SECURE_TLS_SESSION * tls_session,UCHAR * host,UINT host_length,UCHAR * uri_path,UINT uri_path_length,UCHAR * protocol,UINT protocol_length,UINT wait_option)828 UINT _nx_websocket_client_secure_connect(NX_WEBSOCKET_CLIENT *client_ptr, NX_SECURE_TLS_SESSION *tls_session,
829 UCHAR *host, UINT host_length,
830 UCHAR *uri_path, UINT uri_path_length,
831 UCHAR *protocol, UINT protocol_length,UINT wait_option)
832 {
833
834 UINT status;
835
836
837 /* Obtain the mutex. */
838 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
839
840 /* Check the state. */
841 if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_CONNECTED)
842 {
843
844 /* Release the mutex and return */
845 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
846 return(NX_WEBSOCKET_ALREADY_CONNECTED);
847 }
848 else if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_CONNECTING)
849 {
850
851 /* Release the mutex and return */
852 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
853 return(NX_WEBSOCKET_CONNECTING);
854 }
855 else if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_INITIALIZE)
856 {
857
858 /* Release the mutex and return */
859 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
860 return(NX_WEBSOCKET_INVALID_STATE);
861 }
862
863 /* Save the tls session pointer. */
864 client_ptr -> nx_websocket_client_tls_session_ptr = tls_session;
865 client_ptr -> nx_websocket_client_use_tls = NX_TRUE;
866
867 status = _nx_websocket_client_connect_internal(client_ptr, host, host_length, uri_path, uri_path_length, protocol, protocol_length, wait_option);
868
869 /* Release the mutex and return */
870 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
871 return(status);
872 }
873 #endif /* NX_SECURE_ENABLE */
874
875 /**************************************************************************/
876 /* */
877 /* FUNCTION RELEASE */
878 /* */
879 /* _nx_websocket_client_name_compare PORTABLE C */
880 /* 6.2.0 */
881 /* AUTHOR */
882 /* */
883 /* Bo Chen, Microsoft Corporation */
884 /* */
885 /* DESCRIPTION */
886 /* */
887 /* This function compares two pieces of memory case insensitive. */
888 /* */
889 /* INPUT */
890 /* */
891 /* src Pointer to source */
892 /* src_length Length of source */
893 /* dest Pointer to destination */
894 /* dest_length Length of destination */
895 /* */
896 /* OUTPUT */
897 /* */
898 /* status Completion status */
899 /* */
900 /* CALLS */
901 /* */
902 /* None */
903 /* */
904 /* CALLED BY */
905 /* */
906 /* _nx_websocket_client_connect_response_process */
907 /* Process connect response */
908 /* */
909 /* RELEASE HISTORY */
910 /* */
911 /* DATE NAME DESCRIPTION */
912 /* */
913 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
914 /* */
915 /**************************************************************************/
_nx_websocket_client_name_compare(UCHAR * src,ULONG src_length,UCHAR * dest,ULONG dest_length)916 UINT _nx_websocket_client_name_compare(UCHAR *src, ULONG src_length, UCHAR *dest, ULONG dest_length)
917 {
918 UCHAR ch;
919
920 /* Compare the length. */
921 if(src_length != dest_length)
922 {
923 return(NX_WEBSOCKET_ERROR);
924 }
925
926 while(src_length)
927 {
928
929 /* Is src lowercase? */
930 if((*src >= 'a') && (*src <= 'z'))
931 ch = (UCHAR)(*src - 'a' + 'A');
932
933 /* Is src uppercase? */
934 else if((*src >= 'A') && (*src <= 'Z'))
935 ch = (UCHAR)(*src - 'A' + 'a');
936 else
937 ch = *src;
938
939 /* Compare case insensitive. */
940 if((*src != *dest) && (ch != *dest))
941 {
942 return(NX_WEBSOCKET_ERROR);
943 }
944
945 /* Pickup next character. */
946 src_length--;
947 src++;
948 dest++;
949 }
950
951 return(NX_WEBSOCKET_SUCCESS);
952 }
953
954 /**************************************************************************/
955 /* */
956 /* FUNCTION RELEASE */
957 /* */
958 /* _nx_websocket_client_connect_response_process PORTABLE C */
959 /* 6.2.0 */
960 /* AUTHOR */
961 /* */
962 /* Bo Chen, Microsoft Corporation */
963 /* */
964 /* DESCRIPTION */
965 /* */
966 /* This function processes connect response. */
967 /* */
968 /* */
969 /* INPUT */
970 /* */
971 /* client_ptr Pointer to WebSocket Client */
972 /* packet_ptr Pointer to packet */
973 /* */
974 /* OUTPUT */
975 /* */
976 /* status Completion status */
977 /* */
978 /* CALLS */
979 /* */
980 /* _nx_websocket_client_name_compare Compare memory data */
981 /* _nx_sha1_initialize Initialize sha1 */
982 /* _nx_sha1_update Update sha1 */
983 /* _nx_sha1_digest_calculate Calculate sha1 digest */
984 /* _nx_utility_base64_encode Base64 encode */
985 /* */
986 /* CALLED BY */
987 /* */
988 /* _nx_websocket_client_connect_response_check */
989 /* Check connect response */
990 /* */
991 /* RELEASE HISTORY */
992 /* */
993 /* DATE NAME DESCRIPTION */
994 /* */
995 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
996 /* */
997 /**************************************************************************/
_nx_websocket_client_connect_response_process(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET * packet_ptr)998 UINT _nx_websocket_client_connect_response_process(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET *packet_ptr)
999 {
1000 UCHAR *buffer_ptr;
1001 UINT offset;
1002 UCHAR *field_name;
1003 UINT field_name_length;
1004 UCHAR *field_value;
1005 UINT field_value_length;
1006 UCHAR digest[NX_WEBSOCKET_ACCEPT_DIGEST_SIZE];
1007 UCHAR key[NX_WEBSOCKET_ACCEPT_KEY_SIZE + 1];
1008 UINT key_size = 0;
1009 UCHAR upgrade_flag = NX_FALSE;
1010 UCHAR connection_flag = NX_FALSE;
1011 UCHAR protocol_cnt = 0;
1012 UCHAR accept_cnt = 0;
1013
1014 NX_PARAMETER_NOT_USED(client_ptr);
1015
1016 /* WebSocket opening handshake message format:
1017 HTTP/1.1 101 Switching Protocols
1018 Upgrade: websocket\r\n
1019 Server: Microsoft-HTTPAPI/2.0\r\n
1020 Sec-WebSocket-Protocol: mqtt\r\n
1021 Connection: Upgrade\r\n
1022 Sec-WebSocket-Accept: xxxxxxxxxx=\r\n
1023 Date: Mon, 06 Jun 2022 07:46:53 GMT\r\n
1024 \r\n
1025 */
1026
1027 /* Setup pointer and offset. */
1028 buffer_ptr = packet_ptr -> nx_packet_prepend_ptr;
1029 offset = 0;
1030
1031 /* Check the length. */
1032 if ((packet_ptr -> nx_packet_append_ptr - packet_ptr -> nx_packet_prepend_ptr) < 12)
1033 {
1034 return(NX_WEBSOCKET_INVALID_PACKET);
1035 }
1036
1037 /* Check the status code. Must be "101" Switching Protocols. */
1038 if ((buffer_ptr[9] != '1') || (buffer_ptr[10] != '0') || (buffer_ptr[11] != '1'))
1039 {
1040 return(NX_WEBSOCKET_INVALID_STATUS_CODE);
1041 }
1042
1043 /* Skip over the first HTTP line (HTTP/1.1 101 Switching Protocols\r\n). */
1044 offset = 12;
1045 buffer_ptr += 12;
1046 while(((buffer_ptr + 1) < packet_ptr -> nx_packet_append_ptr) &&
1047 (*buffer_ptr != '\r') && (*(buffer_ptr + 1) != '\n'))
1048 {
1049 buffer_ptr++;
1050 offset++;
1051 }
1052
1053 /* Skip over the CR,LF. */
1054 buffer_ptr += 2;
1055 offset += 2;
1056
1057 /* Loop until we find the "cr,lf,cr,lf" token. */
1058 while (((buffer_ptr + 1) < packet_ptr -> nx_packet_append_ptr) && (*buffer_ptr != 0))
1059 {
1060
1061 /* Check for the <cr,lf,cr,lf> token. This signals a blank line, which also
1062 specifies the start of the content. */
1063 if ((*buffer_ptr == '\r') &&
1064 (*(buffer_ptr + 1) == '\n'))
1065 {
1066
1067 /* Adjust the offset. */
1068 offset = offset + 2;
1069 break;
1070 }
1071
1072 /* We haven't seen the <cr,lf,cr,lf> so we are still processing header data.
1073 Extract the field name and it's value. */
1074 field_name = buffer_ptr;
1075 field_name_length = 0;
1076
1077 /* Look for the ':' that separates the field name from its value. */
1078 while ((buffer_ptr < packet_ptr -> nx_packet_append_ptr) && (*buffer_ptr != ':'))
1079 {
1080 buffer_ptr++;
1081 field_name_length++;
1082 }
1083 offset += field_name_length;
1084
1085 /* Skip ':'. */
1086 buffer_ptr++;
1087 offset++;
1088
1089 /* Now skip over white space. */
1090 while ((buffer_ptr < packet_ptr -> nx_packet_append_ptr) && (*buffer_ptr == ' '))
1091 {
1092 buffer_ptr++;
1093 offset++;
1094 }
1095
1096 /* Now get the field value. */
1097 field_value = buffer_ptr;
1098 field_value_length = 0;
1099
1100 /* Loop until we see a <CR, LF>. */
1101 while (((buffer_ptr + 1) < packet_ptr -> nx_packet_append_ptr) && (*buffer_ptr != '\r') && (*(buffer_ptr+1) != '\n'))
1102 {
1103 buffer_ptr++;
1104 field_value_length++;
1105 }
1106 offset += field_value_length;
1107
1108 /* Skip over the CR,LF. */
1109 buffer_ptr += 2;
1110 offset += 2;
1111
1112 /* Check the upgrade. */
1113 if (_nx_websocket_client_name_compare((UCHAR *)field_name, field_name_length, (UCHAR *)"Upgrade", sizeof("Upgrade") - 1) == NX_SUCCESS)
1114 {
1115 if (_nx_websocket_client_name_compare((UCHAR *)field_value, field_value_length, (UCHAR *)"websocket", sizeof("websocket") - 1))
1116 {
1117 return(NX_WEBSOCKET_INVALID_PACKET);
1118 }
1119
1120 upgrade_flag = NX_TRUE;
1121 }
1122 else if (_nx_websocket_client_name_compare((UCHAR *)field_name, field_name_length, (UCHAR *)"Connection", sizeof("Connection") - 1) == NX_SUCCESS)
1123 {
1124 if (_nx_websocket_client_name_compare((UCHAR *)field_value, field_value_length, (UCHAR *)"Upgrade", sizeof("Upgrade") - 1))
1125 {
1126 return(NX_WEBSOCKET_INVALID_PACKET);
1127 }
1128
1129 connection_flag = NX_TRUE;
1130 }
1131 else if (_nx_websocket_client_name_compare((UCHAR *)field_name, field_name_length, (UCHAR *)"Sec-WebSocket-Protocol", sizeof("Sec-WebSocket-Protocol") - 1) == NX_SUCCESS)
1132 {
1133 if (_nx_websocket_client_name_compare((UCHAR *)field_value, field_value_length, client_ptr -> nx_websocket_client_subprotocol, client_ptr -> nx_websocket_client_subprotocol_length))
1134 {
1135 return(NX_WEBSOCKET_INVALID_PACKET);
1136 }
1137
1138 protocol_cnt++;
1139 }
1140 else if (_nx_websocket_client_name_compare((UCHAR *)field_name, field_name_length, (UCHAR *)"Sec-WebSocket-Accept", sizeof("Sec-WebSocket-Accept") - 1) == NX_SUCCESS)
1141 {
1142
1143 /* Calculate the SHA-1 hash of the concatenation of the client key and the Globally Unique Identifier (GUID)
1144 Referenced in RFC 6455, Section 1.3, Page 6 */
1145 _nx_sha1_initialize(&(client_ptr -> nx_websocket_client_sha1));
1146 _nx_sha1_update(&(client_ptr -> nx_websocket_client_sha1), client_ptr->nx_websocket_client_key, client_ptr->nx_websocket_client_key_size);
1147 _nx_sha1_update(&(client_ptr -> nx_websocket_client_sha1), (UCHAR*)NX_WEBSOCKET_ACCEPT_PREDEFINED_GUID, NX_WEBSOCKET_ACCEPT_PREDEFINED_GUID_SIZE);
1148 _nx_sha1_digest_calculate(&(client_ptr -> nx_websocket_client_sha1), digest);
1149
1150 /* Encode the hash and compare it with the field value from the server. */
1151 _nx_utility_base64_encode(digest, NX_WEBSOCKET_ACCEPT_DIGEST_SIZE, key, (NX_WEBSOCKET_ACCEPT_KEY_SIZE + 1), &key_size);
1152 if ((field_value_length != NX_WEBSOCKET_ACCEPT_KEY_SIZE) || (memcmp((void *)field_value, (void *)key, NX_WEBSOCKET_ACCEPT_KEY_SIZE))) /* Use case of memcpy is verified. */
1153 {
1154 return(NX_WEBSOCKET_INVALID_PACKET);
1155 }
1156
1157 accept_cnt++;
1158 }
1159 }
1160
1161 /* Check if the all fields are processed and found as required. */
1162 if ((offset != packet_ptr -> nx_packet_length) ||
1163 (upgrade_flag != NX_TRUE) || (connection_flag != NX_TRUE) ||
1164 (protocol_cnt != 1) || (accept_cnt != 1)) /* Both sec-websocket-protocol field and sec-websocket-accept field are allowed occur once only.
1165 Reference in RFC 6455, Section 11.3.3 and 11.3.4, Page 59-60 */
1166 {
1167 return(NX_WEBSOCKET_INVALID_PACKET);
1168 }
1169
1170 return(NX_SUCCESS);
1171 }
1172
1173 /**************************************************************************/
1174 /* */
1175 /* FUNCTION RELEASE */
1176 /* */
1177 /* _nxe_websocket_client_disconnect PORTABLE C */
1178 /* 6.2.0 */
1179 /* AUTHOR */
1180 /* */
1181 /* Bo Chen, Microsoft Corporation */
1182 /* */
1183 /* DESCRIPTION */
1184 /* */
1185 /* This function checks for errors in the WebSocket disconnect. */
1186 /* */
1187 /* INPUT */
1188 /* */
1189 /* client_ptr Pointer to WebSocket Client */
1190 /* wait_option Wait option */
1191 /* */
1192 /* OUTPUT */
1193 /* */
1194 /* status Completion status */
1195 /* */
1196 /* CALLS */
1197 /* */
1198 /* _nx_websocket_client_disconnect Actual disconnect call */
1199 /* */
1200 /* CALLED BY */
1201 /* */
1202 /* Application Code */
1203 /* */
1204 /* RELEASE HISTORY */
1205 /* */
1206 /* DATE NAME DESCRIPTION */
1207 /* */
1208 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
1209 /* */
1210 /**************************************************************************/
_nxe_websocket_client_disconnect(NX_WEBSOCKET_CLIENT * client_ptr,UINT wait_option)1211 UINT _nxe_websocket_client_disconnect(NX_WEBSOCKET_CLIENT *client_ptr, UINT wait_option)
1212 {
1213
1214 UINT status;
1215
1216
1217 /* Check for invalid input pointers. */
1218 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID))
1219 {
1220 return(NX_PTR_ERROR);
1221 }
1222
1223 /* Call actual disconnect function. */
1224 status = _nx_websocket_client_disconnect(client_ptr, wait_option);
1225
1226 /* Return completion status. */
1227 return(status);
1228 }
1229
1230 /**************************************************************************/
1231 /* */
1232 /* FUNCTION RELEASE */
1233 /* */
1234 /* _nx_websocket_client_disconnect PORTABLE C */
1235 /* 6.2.0 */
1236 /* AUTHOR */
1237 /* */
1238 /* Bo Chen, Microsoft Corporation */
1239 /* */
1240 /* DESCRIPTION */
1241 /* */
1242 /* This function disconnects the WebSocket connection created */
1243 /* previously. */
1244 /* */
1245 /* INPUT */
1246 /* */
1247 /* client_ptr Pointer to WebSocket Client */
1248 /* wait_option Wait option */
1249 /* */
1250 /* OUTPUT */
1251 /* */
1252 /* status Completion status */
1253 /* */
1254 /* CALLS */
1255 /* */
1256 /* _nx_websocket_client_packet_allocate Allocate websocket packet */
1257 /* _nx_websocket_client_send Send websocket packet */
1258 /* nx_packet_release Release websocket packet */
1259 /* */
1260 /* CALLED BY */
1261 /* */
1262 /* Application Code */
1263 /* */
1264 /* RELEASE HISTORY */
1265 /* */
1266 /* DATE NAME DESCRIPTION */
1267 /* */
1268 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
1269 /* */
1270 /**************************************************************************/
_nx_websocket_client_disconnect(NX_WEBSOCKET_CLIENT * client_ptr,UINT wait_option)1271 UINT _nx_websocket_client_disconnect(NX_WEBSOCKET_CLIENT *client_ptr, UINT wait_option)
1272 {
1273
1274 UINT status;
1275 NX_PACKET *packet_ptr;
1276
1277
1278 /* Check the state. */
1279 if ((client_ptr -> nx_websocket_client_state <= NX_WEBSOCKET_CLIENT_STATE_IDLE) || (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_DISCONNECT_SENT))
1280 {
1281
1282 /* Return error status */
1283 return(NX_WEBSOCKET_INVALID_STATE);
1284 }
1285 else if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_CONNECTING)
1286 {
1287
1288 /* Update the state and return */
1289 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_IDLE;
1290 return(NX_WEBSOCKET_NOT_CONNECTED);
1291 }
1292
1293 /* Allocate a packet. */
1294 status = _nx_websocket_client_packet_allocate(client_ptr, &packet_ptr, wait_option);
1295
1296 /* Check the status. */
1297 if (status == NX_SUCCESS)
1298 {
1299
1300 /* Send out the packet. */
1301 status = _nx_websocket_client_send(client_ptr, packet_ptr, NX_WEBSOCKET_OPCODE_CONNECTION_CLOSE, NX_TRUE, wait_option);
1302
1303 /* Check the status. */
1304 if (status)
1305 {
1306 nx_packet_release(packet_ptr);
1307 }
1308 }
1309
1310 /* Obtain the mutex. */
1311 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
1312
1313 /* Check if the CLOSE frame has already been received before */
1314 if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_DISCONNECT_RECEIVED)
1315 {
1316 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_IDLE;
1317 }
1318 else /* i.e. The state is CONNECTED */
1319 {
1320 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_DISCONNECT_SENT;
1321 }
1322
1323 /* Release the mutex and return completion status */
1324 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1325 return(status);
1326 }
1327
1328 /**************************************************************************/
1329 /* */
1330 /* FUNCTION RELEASE */
1331 /* */
1332 /* _nxe_websocket_client_send PORTABLE C */
1333 /* 6.2.0 */
1334 /* AUTHOR */
1335 /* */
1336 /* Bo Chen, Microsoft Corporation */
1337 /* */
1338 /* DESCRIPTION */
1339 /* */
1340 /* This function checks for errors in the WebSocket send. */
1341 /* */
1342 /* INPUT */
1343 /* */
1344 /* client_ptr Pointer to WebSocket Client */
1345 /* packet_ptr Pointer to packet */
1346 /* code Opcode: text or binary frame */
1347 /* is_final Flag: final fragment or not */
1348 /* wait_option Wait option */
1349 /* */
1350 /* OUTPUT */
1351 /* */
1352 /* status Completion status */
1353 /* */
1354 /* CALLS */
1355 /* */
1356 /* _nx_websocket_client_send Actual websocket send call */
1357 /* */
1358 /* CALLED BY */
1359 /* */
1360 /* Application Code */
1361 /* */
1362 /* RELEASE HISTORY */
1363 /* */
1364 /* DATE NAME DESCRIPTION */
1365 /* */
1366 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
1367 /* */
1368 /**************************************************************************/
_nxe_websocket_client_send(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET * packet_ptr,UINT code,UINT is_final,UINT wait_option)1369 UINT _nxe_websocket_client_send(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET *packet_ptr, UINT code, UINT is_final, UINT wait_option)
1370 {
1371
1372 UINT status;
1373
1374
1375 /* Check for invalid input pointers. */
1376 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID) ||
1377 (packet_ptr == NX_NULL))
1378 {
1379 return(NX_PTR_ERROR);
1380 }
1381
1382 /* Call actual send function. */
1383 status = _nx_websocket_client_send(client_ptr, packet_ptr, code, is_final, wait_option);
1384
1385 /* Return completion status. */
1386 return(status);
1387 }
1388
1389 /**************************************************************************/
1390 /* */
1391 /* FUNCTION RELEASE */
1392 /* */
1393 /* _nx_websocket_client_send PORTABLE C */
1394 /* 6.2.0 */
1395 /* AUTHOR */
1396 /* */
1397 /* Bo Chen, Microsoft Corporation */
1398 /* */
1399 /* DESCRIPTION */
1400 /* */
1401 /* This function sends Websocket data frame to server. */
1402 /* */
1403 /* INPUT */
1404 /* */
1405 /* client_ptr Pointer to WebSocket Client */
1406 /* packet_ptr Pointer to packet */
1407 /* code Opcode: text or binary frame */
1408 /* is_final Flag: final fragment or not */
1409 /* wait_option Wait option */
1410 /* */
1411 /* OUTPUT */
1412 /* */
1413 /* status Completion status */
1414 /* */
1415 /* CALLS */
1416 /* */
1417 /* _nx_websocket_client_packet_send Send websocket packet */
1418 /* */
1419 /* CALLED BY */
1420 /* */
1421 /* Application Code */
1422 /* */
1423 /* RELEASE HISTORY */
1424 /* */
1425 /* DATE NAME DESCRIPTION */
1426 /* */
1427 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
1428 /* */
1429 /**************************************************************************/
_nx_websocket_client_send(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET * packet_ptr,UINT code,UINT is_final,UINT wait_option)1430 UINT _nx_websocket_client_send(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET *packet_ptr, UINT code, UINT is_final, UINT wait_option)
1431 {
1432
1433 UINT status;
1434 UCHAR *data_ptr;
1435 NX_PACKET *data_packet;
1436 USHORT message;
1437 ULONG tmp;
1438 UCHAR masking_key[4];
1439 UINT mask_id = 0;
1440 UINT header_size = NX_WEBSOCKET_HEADER_NORMAL_SIZE;
1441
1442
1443 /* Obtain the mutex. */
1444 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
1445
1446 /* Check the state */
1447 if (client_ptr -> nx_websocket_client_state < NX_WEBSOCKET_CLIENT_STATE_CONNECTED)
1448 {
1449
1450 /* Release the mutex and return error status */
1451 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1452 return(NX_WEBSOCKET_NOT_CONNECTED);
1453 }
1454 else if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_DISCONNECT_SENT)
1455 {
1456
1457 /* Release the mutex and return error status */
1458 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1459 return(NX_WEBSOCKET_INVALID_STATE);
1460 }
1461
1462 /* Check the payload length. */
1463 if (packet_ptr -> nx_packet_length > 65535)
1464 {
1465
1466 /* Release the mutex and return error status */
1467 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1468 return(NX_NOT_SUPPORTED);
1469 }
1470 else if (packet_ptr -> nx_packet_length > 125)
1471 {
1472 header_size += 2;
1473 }
1474
1475 /* Check the packet. */
1476 if ((UINT)(packet_ptr -> nx_packet_prepend_ptr - packet_ptr -> nx_packet_data_start) < header_size)
1477 {
1478
1479 /* Release the mutex and return error status */
1480 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1481 return(NX_WEBSOCKET_INVALID_PACKET);
1482 }
1483
1484 /* Adjust the pointer for filling the WebSocket header. */
1485 data_ptr = packet_ptr -> nx_packet_prepend_ptr - header_size;
1486
1487 /* Fill the first byte (FIN + OPCODE) in header. */
1488 if (is_final == NX_TRUE)
1489 {
1490 *data_ptr = (UCHAR)(NX_WEBSOCKET_FIN | code);
1491 }
1492 else
1493 {
1494 *data_ptr = (UCHAR)code;
1495 }
1496 data_ptr++;
1497
1498 /* Fill the next byte for MASK and Payload length. */
1499 *data_ptr = NX_WEBSOCKET_MASK;
1500 if (packet_ptr -> nx_packet_length < 125)
1501 {
1502 *data_ptr |= (UCHAR)packet_ptr -> nx_packet_length;
1503 data_ptr++;
1504 }
1505 else
1506 {
1507 *data_ptr |= NX_WEBSOCKET_PAYLOAD_LEN_16BITS;
1508 data_ptr++;
1509
1510 /* Fill the next two bytes for extended payload length. */
1511 message = (USHORT)packet_ptr -> nx_packet_length;
1512 NX_CHANGE_USHORT_ENDIAN(message);
1513 memcpy(data_ptr, &message, NX_WEBSOCKET_EXTENDED_PAYLOAD_16BITS_SIZE); /* Use case of memcpy is verified. */
1514 data_ptr += NX_WEBSOCKET_EXTENDED_PAYLOAD_16BITS_SIZE;
1515 }
1516
1517 /* Fill the masking key, the masking key is a 32-bit value chosen at random, must mask websocket data from client*/
1518 tmp = (ULONG)NX_RAND();
1519 masking_key[0] = (UCHAR)(tmp >> 24);
1520 masking_key[1] = (UCHAR)(tmp >> 16);
1521 masking_key[2] = (UCHAR)(tmp >> 8);
1522 masking_key[3] = (UCHAR)(tmp);
1523 memcpy(data_ptr, &masking_key, NX_WEBSOCKET_MASKING_KEY_SIZE); /* Use case of memcpy is verified. */
1524 data_ptr += NX_WEBSOCKET_MASKING_KEY_SIZE;
1525
1526 /* Mask all payload data. */
1527 data_packet = packet_ptr;
1528 #ifndef NX_DISABLE_PACKET_CHAIN
1529 while(data_packet)
1530 {
1531 #endif /* NX_DISABLE_PACKET_CHAIN */
1532
1533 data_ptr = data_packet -> nx_packet_prepend_ptr;
1534 while(data_ptr < data_packet -> nx_packet_append_ptr)
1535 {
1536 *data_ptr ^= masking_key[mask_id % 4];
1537 mask_id++;
1538 data_ptr++;
1539 }
1540
1541 #ifndef NX_DISABLE_PACKET_CHAIN
1542 data_packet = data_packet -> nx_packet_next;
1543 }
1544 #endif /* NX_DISABLE_PACKET_CHAIN */
1545
1546 /* Update prepend pointer and packet length to include WebSocket header. */
1547 packet_ptr -> nx_packet_prepend_ptr -= header_size;
1548 packet_ptr -> nx_packet_length += header_size;
1549
1550 /* Release the mutex */
1551 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1552
1553 /* Send out the packet. */
1554 status = _nx_websocket_client_packet_send(client_ptr, packet_ptr, wait_option);
1555
1556 return(status);
1557 }
1558
1559 /**************************************************************************/
1560 /* */
1561 /* FUNCTION RELEASE */
1562 /* */
1563 /* _nxe_websocket_client_receive PORTABLE C */
1564 /* 6.2.0 */
1565 /* AUTHOR */
1566 /* */
1567 /* Bo Chen, Microsoft Corporation */
1568 /* */
1569 /* DESCRIPTION */
1570 /* */
1571 /* This function checks for errors in the WebSocket receive. */
1572 /* */
1573 /* INPUT */
1574 /* */
1575 /* client_ptr Pointer to WebSocket Client */
1576 /* packet_ptr Pointer to packet pointer */
1577 /* code Opcode: text or binary frame */
1578 /* wait_option Wait option */
1579 /* */
1580 /* OUTPUT */
1581 /* */
1582 /* status Completion status */
1583 /* */
1584 /* CALLS */
1585 /* */
1586 /* _nx_websocket_client_receive Actual websocket receive call */
1587 /* */
1588 /* CALLED BY */
1589 /* */
1590 /* Application Code */
1591 /* */
1592 /* RELEASE HISTORY */
1593 /* */
1594 /* DATE NAME DESCRIPTION */
1595 /* */
1596 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
1597 /* */
1598 /**************************************************************************/
_nxe_websocket_client_receive(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET ** packet_ptr,UINT * code,UINT wait_option)1599 UINT _nxe_websocket_client_receive(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET **packet_ptr, UINT *code, UINT wait_option)
1600 {
1601
1602 UINT status;
1603
1604
1605 /* Check for invalid input pointers. */
1606 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID) ||
1607 (packet_ptr == NX_NULL) || (code == NX_NULL))
1608 {
1609 return(NX_PTR_ERROR);
1610 }
1611
1612 /* Call actual process function. */
1613 status = _nx_websocket_client_receive(client_ptr, packet_ptr, code, wait_option);
1614
1615 /* Return completion status. */
1616 return(status);
1617 }
1618
1619 /**************************************************************************/
1620 /* */
1621 /* FUNCTION RELEASE */
1622 /* */
1623 /* _nx_websocket_client_receive PORTABLE C */
1624 /* 6.2.0 */
1625 /* AUTHOR */
1626 /* */
1627 /* Bo Chen, Microsoft Corporation */
1628 /* */
1629 /* DESCRIPTION */
1630 /* */
1631 /* This function receives Websocket data frame from server. */
1632 /* */
1633 /* INPUT */
1634 /* */
1635 /* client_ptr Pointer to WebSocket Client */
1636 /* packet_ptr Pointer to packet pointer */
1637 /* code Opcode: text or binary frame */
1638 /* wait_option Wait option */
1639 /* */
1640 /* OUTPUT */
1641 /* */
1642 /* status Completion status */
1643 /* */
1644 /* CALLS */
1645 /* */
1646 /* _nx_websocket_client_packet_receive Receive websocket packet */
1647 /* _nx_websocket_client_data_process Process data frame */
1648 /* _nx_websocket_client_connect_response_check */
1649 /* Check connect response */
1650 /* */
1651 /* CALLED BY */
1652 /* */
1653 /* Application Code */
1654 /* */
1655 /* RELEASE HISTORY */
1656 /* */
1657 /* DATE NAME DESCRIPTION */
1658 /* */
1659 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
1660 /* */
1661 /**************************************************************************/
_nx_websocket_client_receive(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET ** packet_ptr,UINT * code,UINT wait_option)1662 UINT _nx_websocket_client_receive(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET **packet_ptr, UINT *code, UINT wait_option)
1663 {
1664
1665 UINT status = NX_SUCCESS;
1666
1667
1668 /* Initialize here since packet_ptr will be compared with NX_NULL in following function */
1669 *packet_ptr = NX_NULL;
1670
1671 /* Obtain the mutex. */
1672 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
1673
1674 while (1) /* The while loop ensures parsing all received packets. */
1675 {
1676
1677 /* Check the state */
1678 if ((client_ptr -> nx_websocket_client_state < NX_WEBSOCKET_CLIENT_STATE_CONNECTING) ||
1679 (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_DISCONNECT_RECEIVED))
1680 {
1681
1682 /* Release the mutex and return error status */
1683 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1684 return(NX_WEBSOCKET_INVALID_STATE);
1685 }
1686
1687 /* If the state is NX_WEBSOCKET_CLIENT_STATE_CONNECTING, the received packet should be connect response. */
1688 if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_CONNECTING)
1689 {
1690
1691 /* Receive the data packet */
1692 status = _nx_websocket_client_packet_receive(client_ptr, packet_ptr, wait_option);
1693 if (status != NX_SUCCESS)
1694 {
1695
1696 /* Release the mutex and return status */
1697 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1698 return(status);
1699 }
1700
1701 /* Process the connect response. */
1702 status = _nx_websocket_client_connect_response_check(client_ptr, *packet_ptr, wait_option);
1703
1704 if ((status == NX_SUCCESS) || (status == NX_IN_PROGRESS))
1705 {
1706
1707 /* Continue to receive remaining connect response or application data. */
1708 continue;
1709 }
1710 else
1711 {
1712
1713 /* Release the mutex and return status */
1714 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1715 return(status);
1716 }
1717 }
1718 else
1719 {
1720
1721 /* Check if there is an existing complete frame in the waiting list */
1722 if ((status == NX_SUCCESS) && (client_ptr -> nx_websocket_client_processing_packet != NX_NULL))
1723 {
1724
1725 /* Parse the data packet. */
1726 status = _nx_websocket_client_data_process(client_ptr, packet_ptr, code);
1727 if (status != NX_CONTINUE)
1728 {
1729
1730 /* Release the mutex and return directly if a complete frame is parsed or any error status is found. */
1731 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1732 return(status);
1733 }
1734 }
1735
1736 /* No existing frame, go to receive the data packet */
1737 status = _nx_websocket_client_packet_receive(client_ptr, packet_ptr, wait_option);
1738 if (status != NX_SUCCESS)
1739 {
1740
1741 /* Release the mutex and return status */
1742 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1743 return(status);
1744 }
1745
1746 /* Parse with the new data packet. */
1747 status = _nx_websocket_client_data_process(client_ptr, packet_ptr, code);
1748 if (status != NX_CONTINUE)
1749 {
1750
1751 /* Release the mutex and return directly if a complete frame is parsed or any error status is found. */
1752 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
1753 return(status);
1754 }
1755
1756 /* Due to no application data found, continue to check if there is any pending data packet. */
1757 }
1758 }
1759 }
1760
1761 /**************************************************************************/
1762 /* */
1763 /* FUNCTION RELEASE */
1764 /* */
1765 /* _nx_websocket_client_data_process PORTABLE C */
1766 /* 6.2.0 */
1767 /* AUTHOR */
1768 /* */
1769 /* Bo Chen, Microsoft Corporation */
1770 /* */
1771 /* DESCRIPTION */
1772 /* */
1773 /* This function processes Websocket data frame from server. */
1774 /* */
1775 /* INPUT */
1776 /* */
1777 /* client_ptr Pointer to WebSocket Client */
1778 /* packet_ptr Pointer to packet pointer */
1779 /* code Opcode: text or binary frame */
1780 /* */
1781 /* OUTPUT */
1782 /* */
1783 /* status Completion status */
1784 /* */
1785 /* CALLS */
1786 /* */
1787 /* nx_packet_data_extract_offset Extract data from packet */
1788 /* nx_packet_allocate Allocate a packet */
1789 /* _nx_websocket_client_packet_trim Trim data from packet */
1790 /* _nx_websocket_client_cleanup Cleanup resource */
1791 /* */
1792 /* CALLED BY */
1793 /* */
1794 /* _nx_websocket_client_receive Receive websocket data */
1795 /* */
1796 /* RELEASE HISTORY */
1797 /* */
1798 /* DATE NAME DESCRIPTION */
1799 /* */
1800 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
1801 /* */
1802 /**************************************************************************/
_nx_websocket_client_data_process(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET ** packet_ptr,UINT * code)1803 UINT _nx_websocket_client_data_process(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET **packet_ptr, UINT *code)
1804 {
1805 UINT status;
1806 UCHAR fin_bit = NX_FALSE;
1807 UCHAR opcode = 0;
1808 UCHAR bytes[4];
1809 ULONG payload_length;
1810 ULONG offset = 0;
1811 ULONG bytes_copied;
1812 ULONG packet_length;
1813 NX_PACKET *data_packet;
1814 UCHAR *data_ptr;
1815
1816
1817 /* Is there a packet waiting for processing? */
1818 if (client_ptr -> nx_websocket_client_processing_packet)
1819 {
1820 if (*packet_ptr != NX_NULL)
1821 {
1822
1823 /* Yes. Link received packet to existing one. */
1824 if (client_ptr -> nx_websocket_client_processing_packet -> nx_packet_last)
1825 {
1826 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_last -> nx_packet_next = *packet_ptr;
1827 }
1828 else
1829 {
1830 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_next = *packet_ptr;
1831 }
1832 if ((*packet_ptr) -> nx_packet_last)
1833 {
1834 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_last = (*packet_ptr) -> nx_packet_last;
1835 }
1836 else
1837 {
1838 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_last = *packet_ptr;
1839 }
1840 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_length += (*packet_ptr) -> nx_packet_length;
1841 }
1842
1843 /* Make packet_ptr point to the waiting list */
1844 *packet_ptr = client_ptr -> nx_websocket_client_processing_packet;
1845 }
1846 else
1847 {
1848 if (*packet_ptr == NX_NULL)
1849 {
1850 return(NX_WEBSOCKET_INVALID_PACKET);
1851 }
1852
1853 client_ptr -> nx_websocket_client_processing_packet = *packet_ptr;
1854 }
1855
1856 /* Check if the websocket frame header shall be parsed and found first */
1857 if (client_ptr -> nx_websocket_client_frame_header_found == NX_FALSE)
1858 {
1859
1860 /* Parse the first 2 bytes. */
1861 if (nx_packet_data_extract_offset(*packet_ptr, offset, bytes, NX_WEBSOCKET_HEADER_MINIMUM_LENGTH, &bytes_copied)
1862 || (bytes_copied < NX_WEBSOCKET_HEADER_MINIMUM_LENGTH))
1863 {
1864 return(NX_CONTINUE);
1865 }
1866
1867 /* Update the offset */
1868 offset += NX_WEBSOCKET_HEADER_MINIMUM_LENGTH;
1869
1870 /* Obtain the fin bit and opcode */
1871 fin_bit = bytes[0] & NX_WEBSOCKET_FIN_MASK;
1872 opcode = bytes[0] & NX_WEBSOCKET_OPCODE_MASK;
1873
1874 /* Parse the mask bit and payload length */
1875 if (bytes[1] & NX_WEBSOCKET_MASK)
1876 {
1877 client_ptr -> nx_websocket_client_frame_masked = NX_TRUE;
1878 }
1879 else
1880 {
1881 client_ptr -> nx_websocket_client_frame_masked = NX_FALSE;
1882 }
1883
1884 payload_length = (UCHAR)(bytes[1] & NX_WEBSOCKET_PAYLOAD_LEN_MASK);
1885 if (payload_length < 126)
1886 {
1887
1888 /* No extend payload length; record data payload length directly. */
1889 }
1890 else if (payload_length == 126)
1891 {
1892
1893 /* Extract the 16-bit extended data payload length */
1894 if (nx_packet_data_extract_offset(*packet_ptr, offset, bytes, NX_WEBSOCKET_EXTENDED_PAYLOAD_16BITS_SIZE, &bytes_copied)
1895 || (bytes_copied < NX_WEBSOCKET_EXTENDED_PAYLOAD_16BITS_SIZE))
1896 {
1897 return(NX_CONTINUE);
1898 }
1899
1900 /* Record 16-bit data payload length. */
1901 payload_length = ((((ULONG)bytes[0]) << 8) + (ULONG)bytes[1]);
1902
1903 /* Add the byte count by the payload size */
1904 offset += NX_WEBSOCKET_EXTENDED_PAYLOAD_16BITS_SIZE;
1905 }
1906 else
1907 {
1908
1909 /* Since 64 bits extend payload length is not supported, clean up and return directly. */
1910 _nx_websocket_client_cleanup(client_ptr);
1911 return(NX_NOT_SUPPORTED);
1912 }
1913
1914 /* Parse the masking key */
1915 if (client_ptr -> nx_websocket_client_frame_masked == NX_TRUE)
1916 {
1917 if (nx_packet_data_extract_offset(*packet_ptr, offset, bytes, NX_WEBSOCKET_MASKING_KEY_SIZE, &bytes_copied)
1918 || (bytes_copied < NX_WEBSOCKET_MASKING_KEY_SIZE))
1919 {
1920 return(NX_CONTINUE);
1921 }
1922
1923 /* Get the masking key. */
1924 memcpy(client_ptr -> nx_websocket_client_frame_masking_key, bytes, NX_WEBSOCKET_MASKING_KEY_SIZE); /* Use case of memcpy is verified. */
1925
1926 /* Add the byte count by the masking key size */
1927 offset += NX_WEBSOCKET_MASKING_KEY_SIZE;
1928 }
1929
1930 /* Set the flag to indicate the frame header found, and re-initialize corresponding variables */
1931 client_ptr -> nx_websocket_client_frame_header_found = NX_TRUE;
1932 client_ptr -> nx_websocket_client_frame_data_received = 0;
1933
1934 /* Record the payload length for judging if all payload data received */
1935 client_ptr -> nx_websocket_client_frame_data_length = payload_length;
1936
1937 /* Check the rules apply to fragmentation corresponding to the FIN bit and opcode
1938 RFC 6455, Section 5.4, Page 33-35 */
1939 if (fin_bit == NX_WEBSOCKET_FIN) /* This is the final frame (tip: the first frame may also be the final frame) */
1940 {
1941 if (client_ptr -> nx_websocket_client_frame_fragmented == NX_FALSE) /* A single unfragmented frame shall be received */
1942 {
1943 if (opcode == 0) /* The opcode should not denotes a continuation frame for a single unfragmented frame */
1944 {
1945 _nx_websocket_client_cleanup(client_ptr);
1946 return(NX_INVALID_PACKET);
1947 }
1948
1949 /* Update the header opcode */
1950 client_ptr -> nx_websocket_client_frame_opcode = opcode;
1951 }
1952 else /* This is the termination frame in overall fragmented frames */
1953 {
1954 if (opcode != 0) /* The opcode of the termination frame shall be zero */
1955 {
1956 _nx_websocket_client_cleanup(client_ptr);
1957 return(NX_INVALID_PACKET);
1958 }
1959
1960 /* Set the header flag to be unfragmented for next time to use */
1961 client_ptr -> nx_websocket_client_frame_fragmented = NX_FALSE;
1962 }
1963 }
1964 else /* This is not the final header */
1965 {
1966 if (client_ptr -> nx_websocket_client_frame_fragmented == NX_FALSE) /* This is the beginning frame in fragmented frames */
1967 {
1968
1969 /* The opcode of the beginning frame shall indicate the opcode of overall fragmented frames. Besides,
1970 since control frames cannot be fragmented, the supported frame type shall be text or binary */
1971 if ((opcode != NX_WEBSOCKET_OPCODE_BINARY_FRAME) && (opcode != NX_WEBSOCKET_OPCODE_TEXT_FRAME))
1972 {
1973 _nx_websocket_client_cleanup(client_ptr);
1974 return(NX_INVALID_PACKET);
1975 }
1976
1977 /* Update the frame fragmented flag and the opcode since a beginning frame is received */
1978 client_ptr -> nx_websocket_client_frame_fragmented = NX_TRUE;
1979 client_ptr -> nx_websocket_client_frame_opcode = opcode;
1980 }
1981 else /* This is a continuation frame in overall fragmented frames */
1982 {
1983 if (opcode != 0) /* The opcode of a continuation frame shall be zero */
1984 {
1985 _nx_websocket_client_cleanup(client_ptr);
1986 return(NX_INVALID_PACKET);
1987 }
1988 }
1989 }
1990
1991 /* Trim the WebSocket header */
1992 status = _nx_websocket_client_packet_trim(client_ptr, packet_ptr, offset);
1993 client_ptr -> nx_websocket_client_processing_packet = *packet_ptr;
1994 if (status)
1995 {
1996 if (status == NX_NO_PACKET)
1997 {
1998
1999 /* Try to receive more payload data from TCP/TLS if the packet holds the WebSocket header only */
2000 return(NX_CONTINUE);
2001 }
2002
2003 /* Return error status */
2004 return(status);
2005 }
2006 }
2007
2008 /* Reset payload length and use the variable to count the data length processed by the function call this time */
2009 payload_length = 0;
2010
2011 /* Unmask payload data if there is masking key. */
2012 if (client_ptr -> nx_websocket_client_frame_masked == NX_TRUE)
2013 {
2014 data_packet = (*packet_ptr);
2015 #ifndef NX_DISABLE_PACKET_CHAIN
2016 while (data_packet && client_ptr -> nx_websocket_client_frame_header_found)
2017 {
2018 #endif /* NX_DISABLE_PACKET_CHAIN */
2019
2020 data_ptr = (*packet_ptr) -> nx_packet_prepend_ptr;
2021 while (data_ptr < data_packet -> nx_packet_append_ptr)
2022 {
2023
2024 /* Unmask payload data byte by byte */
2025 *data_ptr ^= client_ptr -> nx_websocket_client_frame_masking_key[client_ptr -> nx_websocket_client_frame_data_received % 4];
2026 data_ptr++;
2027
2028 /* Increase the payload length for the usage in frame process */
2029 payload_length++;
2030
2031 /* Check and jump out if all data payload in the frame have been processed. */
2032 client_ptr -> nx_websocket_client_frame_data_received++;
2033 if (client_ptr -> nx_websocket_client_frame_data_received >= client_ptr -> nx_websocket_client_frame_data_length)
2034 {
2035
2036 /* Reset the frame header flag as not found and break. */
2037 client_ptr -> nx_websocket_client_frame_header_found = NX_FALSE;
2038 break;
2039 }
2040 }
2041
2042 #ifndef NX_DISABLE_PACKET_CHAIN
2043 data_packet = data_packet -> nx_packet_next;
2044 }
2045 #endif /* NX_DISABLE_PACKET_CHAIN */
2046 }
2047
2048 /* Add the payload length if no masking key */
2049 else
2050 {
2051
2052 /* Check and adjust received data length for processing */
2053 payload_length = (*packet_ptr) -> nx_packet_length;
2054 if (payload_length >= (client_ptr -> nx_websocket_client_frame_data_length - client_ptr -> nx_websocket_client_frame_data_received))
2055 {
2056
2057 /* The maximum the payload length for each frame process shall not exceed the remaining frame data to be received */
2058 payload_length = (client_ptr -> nx_websocket_client_frame_data_length - client_ptr -> nx_websocket_client_frame_data_received);
2059
2060 /* Reset the frame header flag as not found. */
2061 client_ptr -> nx_websocket_client_frame_header_found = NX_FALSE;
2062 }
2063
2064 /* Add the length to the total count */
2065 client_ptr -> nx_websocket_client_frame_data_received += payload_length;
2066 }
2067
2068 /* Check the opcode for the received frame, and return corresponding status. */
2069 switch (opcode)
2070 {
2071 case NX_WEBSOCKET_OPCODE_CONTINUATION_FRAME:
2072 case NX_WEBSOCKET_OPCODE_TEXT_FRAME:
2073 case NX_WEBSOCKET_OPCODE_BINARY_FRAME:
2074 {
2075
2076 /* Assign the return opcode by the pre-stored opcode */
2077 *code = client_ptr -> nx_websocket_client_frame_opcode;
2078
2079 /* Update the offset by payload length */
2080 offset = payload_length;
2081
2082 /* For a data frame (i.e. text/binary frame), search and find the end of the complete frame */
2083 data_packet = *packet_ptr;
2084 packet_length = (ULONG)(data_packet -> nx_packet_append_ptr - data_packet -> nx_packet_prepend_ptr);
2085 while (packet_length < offset)
2086 {
2087 offset -= packet_length;
2088
2089 /* Move the current data packet pointer to next and compute the length of next packet */
2090 data_packet = data_packet -> nx_packet_next;
2091 packet_length = (ULONG)(data_packet -> nx_packet_append_ptr - data_packet -> nx_packet_prepend_ptr);
2092 }
2093
2094 /* After subtracting by the offset, packet_length represents the size of remaining data in the single packet to be linked into the waiting list */
2095 packet_length -= offset;
2096
2097 /* Check if the frame end is just in the end of one of the packet(s) */
2098 if (packet_length == 0)
2099 {
2100
2101 /* Put remaining packet(s) into the waiting list */
2102 client_ptr -> nx_websocket_client_processing_packet = data_packet -> nx_packet_next;
2103
2104 /* Check if there is data remaining and determine whether to go on to following logic. */
2105 if (data_packet -> nx_packet_next == NX_NULL)
2106 {
2107
2108 /* The packet is fully parsed, return success directly. */
2109 return(NX_WEBSOCKET_SUCCESS);
2110 }
2111 }
2112 else /* One more frame in the packet */
2113 {
2114
2115 /* Release the mutex. */
2116 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
2117
2118 /* Allocate a packet for the remaining data in the packet. */
2119 status = nx_packet_allocate((*packet_ptr) -> nx_packet_pool_owner, &client_ptr -> nx_websocket_client_processing_packet, NX_RECEIVE_PACKET, NX_WAIT_FOREVER);
2120
2121 /* The return status may be not NX_SUCCESS only there is an unexpected issue from the packet pool (e.g. a delete operation ).
2122 Assert here for the unexpected issue shall not happen in normal status */
2123 NX_ASSERT(status == NX_SUCCESS);
2124
2125 /* Obtain the mutex again. */
2126 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
2127
2128 /* Copy the contents of the remaining part of current packet into the new allocated packet */
2129 memcpy((void *)client_ptr -> nx_websocket_client_processing_packet -> nx_packet_prepend_ptr,
2130 (void *)(data_packet -> nx_packet_prepend_ptr + offset), packet_length); /* Use case of memcpy is verified. */
2131
2132 /* Move the append pointer with by the extended length */
2133 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_append_ptr += packet_length;
2134
2135 /* Link the possibly had remaining packet(s) to the waiting list. The overall packet length will be updated outside this else branch */
2136 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_next = data_packet -> nx_packet_next;
2137 }
2138
2139 /* Update the last packet pointer in the waiting liast */
2140 if (client_ptr -> nx_websocket_client_processing_packet -> nx_packet_next)
2141 {
2142 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_last = (*packet_ptr) -> nx_packet_last;
2143 }
2144 else
2145 {
2146 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_last = NX_NULL;
2147 }
2148
2149 /* Update the overall packet length for the waiting list */
2150 client_ptr -> nx_websocket_client_processing_packet -> nx_packet_length = (*packet_ptr) -> nx_packet_length - payload_length;
2151
2152 /* Disconnect the link between the return packet and the waiting list */
2153 data_packet -> nx_packet_next = NX_NULL;
2154 data_packet -> nx_packet_append_ptr -= packet_length;
2155 (*packet_ptr) -> nx_packet_length = payload_length;
2156
2157 /* Update the last packet pointer of the returned packet */
2158 if ((*packet_ptr) -> nx_packet_next)
2159 {
2160 (*packet_ptr) -> nx_packet_last = data_packet;
2161 }
2162 else
2163 {
2164 (*packet_ptr) -> nx_packet_last = NX_NULL;
2165 }
2166
2167 /* Return success status */
2168 return(NX_WEBSOCKET_SUCCESS);
2169 }
2170
2171 case NX_WEBSOCKET_OPCODE_CONNECTION_CLOSE:
2172 {
2173 /* Make sure the complete control frame is received */
2174 if (client_ptr -> nx_websocket_client_frame_data_received < client_ptr -> nx_websocket_client_frame_data_length)
2175 {
2176 return(NX_CONTINUE);
2177 }
2178
2179 /* A disconnection is informed, notify the application. */
2180 if (client_ptr -> nx_websocket_client_connection_status_callback)
2181 {
2182 client_ptr -> nx_websocket_client_connection_status_callback(client_ptr, client_ptr -> nx_websocket_client_connection_context, NX_WEBSOCKET_DISCONNECTED);
2183 }
2184
2185 /* Check the current state and update the state when the CLOSE frame is found */
2186 if (client_ptr -> nx_websocket_client_state == NX_WEBSOCKET_CLIENT_STATE_DISCONNECT_SENT)
2187 {
2188 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_IDLE;
2189 }
2190 else
2191 {
2192 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_DISCONNECT_RECEIVED;
2193 }
2194
2195 /* Return disconnect received status only, without returning any packet data, since it is required no more data after the Close frame.
2196 There is no need to release the packet_ptr since it points to the same memory region as the waiting list
2197 Referenced in RFC 6455, Section 5.5.1, Page 36 */
2198 _nx_websocket_client_cleanup(client_ptr);
2199 return(NX_WEBSOCKET_DISCONNECTED);
2200 }
2201
2202 case NX_WEBSOCKET_OPCODE_PING:
2203 case NX_WEBSOCKET_OPCODE_PONG:
2204 {
2205
2206 /* Make sure the complete control frame is received */
2207 if (client_ptr -> nx_websocket_client_frame_data_received < client_ptr -> nx_websocket_client_frame_data_length)
2208 {
2209 return(NX_CONTINUE);
2210 }
2211
2212 /* Trim payload data in the frame. */
2213 status = _nx_websocket_client_packet_trim(client_ptr, packet_ptr, client_ptr -> nx_websocket_client_frame_data_received);
2214
2215 /* Update the waiting list */
2216 client_ptr -> nx_websocket_client_processing_packet = *packet_ptr;
2217
2218 /* Check if error status happens */
2219 if ((status != NX_WEBSOCKET_SUCCESS) && (status != NX_NO_PACKET))
2220 {
2221 return(status);
2222 }
2223
2224 /* A PING/PONG frame is parsed and found, continue to check any more data or frame received */
2225 return(NX_CONTINUE);
2226 }
2227
2228 default:
2229 {
2230
2231 /* Clean up and return invalid status */
2232 _nx_websocket_client_cleanup(client_ptr);
2233 return(NX_INVALID_PACKET);
2234 }
2235 }
2236 }
2237
2238 /**************************************************************************/
2239 /* */
2240 /* FUNCTION RELEASE */
2241 /* */
2242 /* _nx_websocket_client_packet_trim PORTABLE C */
2243 /* 6.2.0 */
2244 /* AUTHOR */
2245 /* */
2246 /* Bo Chen, Microsoft Corporation */
2247 /* */
2248 /* DESCRIPTION */
2249 /* */
2250 /* This function trims extra bytes from the data packet. */
2251 /* */
2252 /* INPUT */
2253 /* */
2254 /* client_ptr Pointer to WebSocket Client */
2255 /* packet_ptr Pointer to packet pointer */
2256 /* trim_size Number of bytes to remove */
2257 /* */
2258 /* OUTPUT */
2259 /* */
2260 /* status Completion status */
2261 /* */
2262 /* CALLS */
2263 /* */
2264 /* nx_packet_release Release the packet */
2265 /* */
2266 /* CALLED BY */
2267 /* */
2268 /* _nx_websocket_client_data_process Process data frame */
2269 /* */
2270 /* RELEASE HISTORY */
2271 /* */
2272 /* DATE NAME DESCRIPTION */
2273 /* */
2274 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2275 /* */
2276 /**************************************************************************/
_nx_websocket_client_packet_trim(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET ** packet_ptr,ULONG trim_size)2277 UINT _nx_websocket_client_packet_trim(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET **packet_ptr, ULONG trim_size)
2278 {
2279
2280 ULONG packet_length;
2281 NX_PACKET *head_packet_ptr = *packet_ptr;
2282 NX_PACKET *previous_packet_ptr = NX_NULL;
2283
2284 NX_PARAMETER_NOT_USED(client_ptr);
2285
2286 /* The trim size shall be less than or equal to the packet length */
2287 if ((*packet_ptr) -> nx_packet_length < trim_size)
2288 {
2289 return(NX_PACKET_OFFSET_ERROR);
2290 }
2291 packet_length = (*packet_ptr) -> nx_packet_length - trim_size;
2292
2293 /* Search and find the trim point */
2294 while ((ULONG)((*packet_ptr) -> nx_packet_append_ptr - (*packet_ptr) -> nx_packet_prepend_ptr) <= trim_size)
2295 {
2296 trim_size -= (ULONG)((*packet_ptr) -> nx_packet_append_ptr - (*packet_ptr) -> nx_packet_prepend_ptr);
2297
2298 previous_packet_ptr = *packet_ptr;
2299 *packet_ptr = (*packet_ptr) -> nx_packet_next;
2300
2301 if (*packet_ptr == NX_NULL)
2302 {
2303 break;
2304 }
2305 }
2306
2307 /* Disconnect the link if the trim point is not in the head packet */
2308 if (previous_packet_ptr != NX_NULL)
2309 {
2310 previous_packet_ptr -> nx_packet_next = NX_NULL;
2311
2312 /* Set the nx_packet_last pointer due to the packet header changed */
2313 if (*packet_ptr)
2314 {
2315 (*packet_ptr) -> nx_packet_last = head_packet_ptr -> nx_packet_last;
2316 }
2317
2318 nx_packet_release(head_packet_ptr);
2319 }
2320
2321 if (*packet_ptr)
2322 {
2323
2324 /* Adjust current packet */
2325 (*packet_ptr) -> nx_packet_prepend_ptr += trim_size;
2326 (*packet_ptr) -> nx_packet_length = packet_length;
2327
2328 return(NX_WEBSOCKET_SUCCESS);
2329 }
2330
2331 /* Return this value to tell the caller that the whole packet is trimmed */
2332 return(NX_NO_PACKET);
2333 }
2334
2335 /**************************************************************************/
2336 /* */
2337 /* FUNCTION RELEASE */
2338 /* */
2339 /* _nxe_websocket_client_packet_allocate PORTABLE C */
2340 /* 6.2.0 */
2341 /* AUTHOR */
2342 /* */
2343 /* Bo Chen, Microsoft Corporation */
2344 /* */
2345 /* DESCRIPTION */
2346 /* */
2347 /* This function checks for errors in the WebSocket packet allocate. */
2348 /* */
2349 /* INPUT */
2350 /* */
2351 /* client_ptr Pointer to WebSocket Client */
2352 /* packet_ptr Pointer to packet pointer */
2353 /* wait_option Wait option */
2354 /* */
2355 /* OUTPUT */
2356 /* */
2357 /* status Completion status */
2358 /* */
2359 /* CALLS */
2360 /* */
2361 /* _nx_websocket_client_packet_allocate Actual websocket allocate call*/
2362 /* */
2363 /* CALLED BY */
2364 /* */
2365 /* Application Code */
2366 /* */
2367 /* RELEASE HISTORY */
2368 /* */
2369 /* DATE NAME DESCRIPTION */
2370 /* */
2371 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2372 /* */
2373 /**************************************************************************/
_nxe_websocket_client_packet_allocate(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET ** packet_ptr,ULONG wait_option)2374 UINT _nxe_websocket_client_packet_allocate(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET **packet_ptr, ULONG wait_option)
2375 {
2376
2377 UINT status;
2378
2379
2380 /* Check for invalid input pointers */
2381 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID) || (packet_ptr == NX_NULL))
2382 {
2383 return(NX_PTR_ERROR);
2384 }
2385
2386 /* Call actual process function. */
2387 status = _nx_websocket_client_packet_allocate(client_ptr, packet_ptr, wait_option);
2388
2389 /* Return completion status. */
2390 return(status);
2391 }
2392
2393 /**************************************************************************/
2394 /* */
2395 /* FUNCTION RELEASE */
2396 /* */
2397 /* _nx_websocket_client_packet_allocate PORTABLE C */
2398 /* 6.2.0 */
2399 /* AUTHOR */
2400 /* */
2401 /* Bo Chen, Microsoft Corporation */
2402 /* */
2403 /* DESCRIPTION */
2404 /* */
2405 /* This function allocates a Websocket packet. */
2406 /* */
2407 /* INPUT */
2408 /* */
2409 /* client_ptr Pointer to WebSocket Client */
2410 /* packet_ptr Pointer to packet pointer */
2411 /* wait_option Wait option */
2412 /* */
2413 /* OUTPUT */
2414 /* */
2415 /* status Completion status */
2416 /* */
2417 /* CALLS */
2418 /* */
2419 /* nx_packet_allocate Allocate a packet */
2420 /* nx_packet_release Release packet */
2421 /* nx_secure_tls_packet_allocate Allocate a TLS packet */
2422 /* */
2423 /* CALLED BY */
2424 /* */
2425 /* Application Code */
2426 /* */
2427 /* RELEASE HISTORY */
2428 /* */
2429 /* DATE NAME DESCRIPTION */
2430 /* */
2431 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2432 /* */
2433 /**************************************************************************/
_nx_websocket_client_packet_allocate(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET ** packet_ptr,ULONG wait_option)2434 UINT _nx_websocket_client_packet_allocate(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET **packet_ptr, ULONG wait_option)
2435 {
2436
2437 UINT status;
2438
2439
2440 #ifdef NX_SECURE_ENABLE
2441 if (client_ptr -> nx_websocket_client_use_tls)
2442 {
2443
2444 /* Use TLS packet allocate. The TLS packet allocate is able to count for
2445 TLS-related header space including crypto initial vector area. */
2446 status = nx_secure_tls_packet_allocate(client_ptr -> nx_websocket_client_tls_session_ptr,
2447 client_ptr -> nx_websocket_client_packet_pool_ptr,
2448 packet_ptr, TX_WAIT_FOREVER);
2449 }
2450 else
2451 {
2452 #endif /* NX_SECURE_ENABLE */
2453
2454 /* Allocate packet. */
2455 if (client_ptr -> nx_websocket_client_socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
2456 {
2457 status = nx_packet_allocate(client_ptr -> nx_websocket_client_packet_pool_ptr,
2458 packet_ptr,
2459 NX_IPv4_TCP_PACKET, wait_option);
2460 }
2461 else
2462 {
2463 status = nx_packet_allocate(client_ptr -> nx_websocket_client_packet_pool_ptr,
2464 packet_ptr,
2465 NX_IPv6_TCP_PACKET, wait_option);
2466 }
2467 #ifdef NX_SECURE_ENABLE
2468 }
2469 #endif /* NX_SECURE_ENABLE */
2470
2471 if (status == NX_SUCCESS)
2472 {
2473
2474 /* Check the buffer size for the basic data header of websocket. */
2475 if (((ULONG)(((*packet_ptr) -> nx_packet_data_end) - ((*packet_ptr) -> nx_packet_prepend_ptr))) < NX_WEBSOCKET_HEADER_SIZE)
2476 {
2477
2478 /* Packet buffer is too small. */
2479 nx_packet_release(*packet_ptr);
2480 return(NX_WEBSOCKET_INVALID_PACKET);
2481 }
2482
2483 /* Adjust the pointers. */
2484 ((*packet_ptr) -> nx_packet_prepend_ptr) += NX_WEBSOCKET_HEADER_SIZE;
2485 ((*packet_ptr) -> nx_packet_append_ptr) += NX_WEBSOCKET_HEADER_SIZE;
2486 }
2487
2488 /* Return completion status. */
2489 return(status);
2490 }
2491
2492 /**************************************************************************/
2493 /* */
2494 /* FUNCTION RELEASE */
2495 /* */
2496 /* _nx_websocket_client_packet_send PORTABLE C */
2497 /* 6.2.0 */
2498 /* AUTHOR */
2499 /* */
2500 /* Bo Chen, Microsoft Corporation */
2501 /* */
2502 /* DESCRIPTION */
2503 /* */
2504 /* This function sends Websocket packet. */
2505 /* */
2506 /* INPUT */
2507 /* */
2508 /* client_ptr Pointer to WebSocket Client */
2509 /* packet_ptr Pointer to packet */
2510 /* wait_option Wait option */
2511 /* */
2512 /* OUTPUT */
2513 /* */
2514 /* status Completion status */
2515 /* */
2516 /* CALLS */
2517 /* */
2518 /* nx_tcp_socket_send Send tcp packet */
2519 /* nx_secure_tls_session_send Send tls packet */
2520 /* */
2521 /* CALLED BY */
2522 /* */
2523 /* _nx_websocket_client_connect_internal Make websocket connection */
2524 /* _nx_websocket_client_send Send websocket data frame */
2525 /* */
2526 /* RELEASE HISTORY */
2527 /* */
2528 /* DATE NAME DESCRIPTION */
2529 /* */
2530 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2531 /* */
2532 /**************************************************************************/
_nx_websocket_client_packet_send(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET * packet_ptr,ULONG wait_option)2533 UINT _nx_websocket_client_packet_send(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET *packet_ptr, ULONG wait_option)
2534 {
2535
2536 UINT status;
2537
2538 #ifdef NX_SECURE_ENABLE
2539 if (client_ptr -> nx_websocket_client_use_tls)
2540 {
2541 status = nx_secure_tls_session_send(client_ptr -> nx_websocket_client_tls_session_ptr, packet_ptr, wait_option);
2542 }
2543 else
2544 {
2545 status = nx_tcp_socket_send(client_ptr -> nx_websocket_client_socket_ptr, packet_ptr, wait_option);
2546 }
2547 #else
2548 status = nx_tcp_socket_send(client_ptr -> nx_websocket_client_socket_ptr, packet_ptr, wait_option);
2549 #endif /* NX_SECURE_ENABLE */
2550
2551 return(status);
2552 }
2553
2554 /**************************************************************************/
2555 /* */
2556 /* FUNCTION RELEASE */
2557 /* */
2558 /* _nx_websocket_client_packet_receive PORTABLE C */
2559 /* 6.2.0 */
2560 /* AUTHOR */
2561 /* */
2562 /* Bo Chen, Microsoft Corporation */
2563 /* */
2564 /* DESCRIPTION */
2565 /* */
2566 /* This function receives Websocket packet. */
2567 /* */
2568 /* INPUT */
2569 /* */
2570 /* client_ptr Pointer to WebSocket Client */
2571 /* packet_ptr Pointer to packet pointer */
2572 /* wait_option Wait option */
2573 /* */
2574 /* OUTPUT */
2575 /* */
2576 /* status Completion status */
2577 /* */
2578 /* CALLS */
2579 /* */
2580 /* nx_tcp_socket_receive Receive tcp packet */
2581 /* nx_secure_tls_session_receive Receive tls packet */
2582 /* */
2583 /* CALLED BY */
2584 /* */
2585 /* _nx_websocket_client_connect_internal Make websocket connection */
2586 /* _nx_websocket_client_receive Receive websocket data frame */
2587 /* */
2588 /* RELEASE HISTORY */
2589 /* */
2590 /* DATE NAME DESCRIPTION */
2591 /* */
2592 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2593 /* */
2594 /**************************************************************************/
_nx_websocket_client_packet_receive(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET ** packet_ptr,ULONG wait_option)2595 UINT _nx_websocket_client_packet_receive(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET **packet_ptr, ULONG wait_option)
2596 {
2597
2598 UINT status;
2599
2600
2601 /* Release the mutex first */
2602 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
2603
2604 #ifdef NX_SECURE_ENABLE
2605 if (client_ptr -> nx_websocket_client_use_tls)
2606 {
2607 status = nx_secure_tls_session_receive(client_ptr -> nx_websocket_client_tls_session_ptr, packet_ptr, wait_option);
2608 }
2609 else
2610 {
2611 status = nx_tcp_socket_receive(client_ptr -> nx_websocket_client_socket_ptr, packet_ptr, wait_option);
2612 }
2613 #else
2614 status = nx_tcp_socket_receive(client_ptr -> nx_websocket_client_socket_ptr, packet_ptr, wait_option);
2615 #endif /* NX_SECURE_ENABLE */
2616
2617 /* Obtain the mutex again. */
2618 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
2619
2620 return(status);
2621 }
2622
2623 /**************************************************************************/
2624 /* */
2625 /* FUNCTION RELEASE */
2626 /* */
2627 /* _nx_websocket_client_connect_response_check PORTABLE C */
2628 /* 6.2.0 */
2629 /* AUTHOR */
2630 /* */
2631 /* Bo Chen, Microsoft Corporation */
2632 /* */
2633 /* DESCRIPTION */
2634 /* */
2635 /* This function checks if a whole connect response is received, then */
2636 /* process the response. */
2637 /* */
2638 /* INPUT */
2639 /* */
2640 /* client_ptr Pointer to WebSocket Client */
2641 /* packet_ptr Pointer to packet */
2642 /* wait_option Wait option */
2643 /* */
2644 /* OUTPUT */
2645 /* */
2646 /* status Completion status */
2647 /* */
2648 /* CALLS */
2649 /* */
2650 /* nx_packet_data_append Append data into packet */
2651 /* nx_packet_release Release packet */
2652 /* _nx_websocket_client_connect_response_process */
2653 /* Process connect response */
2654 /* _nx_websocket_client_cleanup Cleanup resource */
2655 /* */
2656 /* CALLED BY */
2657 /* */
2658 /* _nx_websocket_client_connect_internal Make websocket connection */
2659 /* _nx_websocket_client_receive Receive websocket data frame */
2660 /* */
2661 /* RELEASE HISTORY */
2662 /* */
2663 /* DATE NAME DESCRIPTION */
2664 /* */
2665 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2666 /* */
2667 /**************************************************************************/
_nx_websocket_client_connect_response_check(NX_WEBSOCKET_CLIENT * client_ptr,NX_PACKET * packet_ptr,UINT wait_option)2668 UINT _nx_websocket_client_connect_response_check(NX_WEBSOCKET_CLIENT *client_ptr, NX_PACKET *packet_ptr, UINT wait_option)
2669 {
2670 CHAR *buffer_ptr;
2671 UINT status = NX_SUCCESS;
2672 UINT crlf_found = 0;
2673 NX_PACKET *tmp_ptr;
2674
2675
2676 if (client_ptr -> nx_websocket_client_processing_packet == NX_NULL)
2677 {
2678 client_ptr -> nx_websocket_client_processing_packet = packet_ptr;
2679 }
2680 else
2681 {
2682
2683 /* Its contents now need to be placed in the head packet. */
2684 tmp_ptr = packet_ptr;
2685
2686 #ifndef NX_DISABLE_PACKET_CHAIN
2687 while (tmp_ptr)
2688 #endif /* NX_DISABLE_PACKET_CHAIN */
2689 {
2690
2691 /* Release the mutex */
2692 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
2693
2694 /* Copy the contents of the current packet into the head packet. */
2695 status = nx_packet_data_append(client_ptr -> nx_websocket_client_processing_packet,
2696 (VOID *) tmp_ptr -> nx_packet_prepend_ptr,
2697 (ULONG)(tmp_ptr -> nx_packet_append_ptr - tmp_ptr -> nx_packet_prepend_ptr),
2698 client_ptr -> nx_websocket_client_packet_pool_ptr, wait_option);
2699
2700 /* Obtain the mutex. */
2701 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
2702
2703 /* Determine if an error occurred or an unexpected packet chain happens since the connect response shall not exceed the maximum length of one packet */
2704 if ((status != NX_SUCCESS) || (client_ptr -> nx_websocket_client_processing_packet -> nx_packet_next))
2705 {
2706
2707 /* Reset the state to idle */
2708 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_IDLE;
2709
2710 /* Clean up unused resources. */
2711 _nx_websocket_client_cleanup(client_ptr);
2712
2713 /* Release the new packet and return. */
2714 nx_packet_release(packet_ptr);
2715 return(status);
2716 }
2717 #ifndef NX_DISABLE_PACKET_CHAIN
2718 else
2719 {
2720 tmp_ptr = tmp_ptr -> nx_packet_next;
2721 }
2722 #endif /* NX_DISABLE_PACKET_CHAIN */
2723 }
2724
2725 /* Release the new packet. */
2726 nx_packet_release(packet_ptr);
2727 }
2728
2729 crlf_found = 0;
2730 tmp_ptr = client_ptr -> nx_websocket_client_processing_packet;
2731
2732 while (1)
2733 {
2734
2735 /* Build a pointer to the buffer area. */
2736 buffer_ptr = (CHAR *) tmp_ptr -> nx_packet_prepend_ptr;
2737
2738 /* See if there is a blank line present in the buffer. */
2739 /* Search the buffer for a cr/lf pair. */
2740 while ((buffer_ptr < (CHAR *) tmp_ptr -> nx_packet_append_ptr) &&
2741 (crlf_found < 4))
2742 {
2743 if (!(crlf_found & 1) && (*buffer_ptr == (CHAR)13))
2744 {
2745
2746 /* Found CR. */
2747 crlf_found++;
2748 }
2749 else if((crlf_found & 1) && (*buffer_ptr == (CHAR)10))
2750 {
2751
2752 /* Found LF. */
2753 crlf_found++;
2754 }
2755 else
2756 {
2757
2758 /* Reset the CRLF marker. */
2759 crlf_found = 0;
2760 }
2761
2762 /* Move the buffer pointer up. */
2763 buffer_ptr++;
2764 }
2765
2766 if (crlf_found == 4)
2767 {
2768
2769 /* Yes, we have found the end of the HTTP response header. */
2770 break;
2771 }
2772
2773 #ifndef NX_DISABLE_PACKET_CHAIN
2774
2775 if (tmp_ptr -> nx_packet_next != NX_NULL)
2776 {
2777
2778 /* Get the next packet in the chain. */
2779 tmp_ptr = tmp_ptr -> nx_packet_next;
2780 }
2781 else
2782 #endif
2783 {
2784 return(NX_IN_PROGRESS);
2785 }
2786 }
2787
2788 /* Process the response. */
2789 status = _nx_websocket_client_connect_response_process(client_ptr, client_ptr -> nx_websocket_client_processing_packet);
2790 if (status == NX_SUCCESS)
2791 {
2792
2793 /* Update the state. */
2794 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_CONNECTED;
2795
2796 /* If connection is established, notify application. */
2797 if (client_ptr -> nx_websocket_client_connection_status_callback)
2798 {
2799 client_ptr -> nx_websocket_client_connection_status_callback(client_ptr, client_ptr -> nx_websocket_client_connection_context, NX_SUCCESS);
2800 }
2801 }
2802 else
2803 {
2804 client_ptr -> nx_websocket_client_state = NX_WEBSOCKET_CLIENT_STATE_IDLE;
2805 }
2806
2807 /* Clean up unused resources. */
2808 _nx_websocket_client_cleanup(client_ptr);
2809
2810 return (status);
2811 }
2812
2813 /**************************************************************************/
2814 /* */
2815 /* FUNCTION RELEASE */
2816 /* */
2817 /* _nxe_websocket_client_connection_status_callback_set */
2818 /* PORTABLE C */
2819 /* 6.2.0 */
2820 /* AUTHOR */
2821 /* */
2822 /* Bo Chen, Microsoft Corporation */
2823 /* */
2824 /* DESCRIPTION */
2825 /* */
2826 /* This function checks for errors in the WebSocket callback set. */
2827 /* */
2828 /* INPUT */
2829 /* */
2830 /* client_ptr Pointer to WebSocket Client */
2831 /* context Context for callback */
2832 /* connection_status_callback Routine to call when connect */
2833 /* or disconnect occurs */
2834 /* */
2835 /* OUTPUT */
2836 /* */
2837 /* status Completion status */
2838 /* */
2839 /* CALLS */
2840 /* */
2841 /* _nx_websocket_client_connection_status_callback_set */
2842 /* Actual websocket callback set */
2843 /* */
2844 /* CALLED BY */
2845 /* */
2846 /* Application Code */
2847 /* */
2848 /* RELEASE HISTORY */
2849 /* */
2850 /* DATE NAME DESCRIPTION */
2851 /* */
2852 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2853 /* */
2854 /**************************************************************************/
_nxe_websocket_client_connection_status_callback_set(NX_WEBSOCKET_CLIENT * client_ptr,VOID * context,VOID (* connection_status_callback)(NX_WEBSOCKET_CLIENT *,VOID *,UINT))2855 UINT _nxe_websocket_client_connection_status_callback_set(NX_WEBSOCKET_CLIENT *client_ptr, VOID *context,
2856 VOID (*connection_status_callback)(NX_WEBSOCKET_CLIENT *, VOID *, UINT))
2857 {
2858 UINT status;
2859
2860
2861 /* Check for invalid input pointers. */
2862 if ((client_ptr == NX_NULL) || (client_ptr -> nx_websocket_client_id != NX_WEBSOCKET_CLIENT_ID))
2863 {
2864 return(NX_PTR_ERROR);
2865 }
2866
2867 /* Call actual process function. */
2868 status = _nx_websocket_client_connection_status_callback_set(client_ptr, context, connection_status_callback);
2869
2870 /* Return completion status. */
2871 return(status);
2872 }
2873
2874 /**************************************************************************/
2875 /* */
2876 /* FUNCTION RELEASE */
2877 /* */
2878 /* _nx_websocket_client_connection_status_callback_set PORTABLE C */
2879 /* 6.2.0 */
2880 /* AUTHOR */
2881 /* */
2882 /* Bo Chen, Microsoft Corporation */
2883 /* */
2884 /* DESCRIPTION */
2885 /* */
2886 /* This function sets Websocket connection callback. */
2887 /* */
2888 /* INPUT */
2889 /* */
2890 /* client_ptr Pointer to WebSocket Client */
2891 /* context Context for callback */
2892 /* connection_status_callback Routine to call when connect */
2893 /* or disconnect occurs */
2894 /* */
2895 /* OUTPUT */
2896 /* */
2897 /* status Completion status */
2898 /* */
2899 /* CALLS */
2900 /* */
2901 /* None */
2902 /* */
2903 /* CALLED BY */
2904 /* */
2905 /* Application Code */
2906 /* */
2907 /* RELEASE HISTORY */
2908 /* */
2909 /* DATE NAME DESCRIPTION */
2910 /* */
2911 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2912 /* */
2913 /**************************************************************************/
_nx_websocket_client_connection_status_callback_set(NX_WEBSOCKET_CLIENT * client_ptr,VOID * context,VOID (* connection_status_callback)(NX_WEBSOCKET_CLIENT *,VOID *,UINT))2914 UINT _nx_websocket_client_connection_status_callback_set(NX_WEBSOCKET_CLIENT *client_ptr, VOID *context,
2915 VOID (*connection_status_callback)(NX_WEBSOCKET_CLIENT *, VOID *, UINT))
2916 {
2917
2918 /* Obtain the mutex. */
2919 tx_mutex_get(&(client_ptr -> nx_websocket_client_mutex), NX_WAIT_FOREVER);
2920
2921 /* Set the context which will be passed to connection status callback. */
2922 client_ptr -> nx_websocket_client_connection_context = context;
2923
2924 /* Set the connection status callback. */
2925 client_ptr -> nx_websocket_client_connection_status_callback = connection_status_callback;
2926
2927 /* Release the mutex */
2928 tx_mutex_put(&(client_ptr -> nx_websocket_client_mutex));
2929
2930 return(NX_SUCCESS);
2931 }
2932
2933 /**************************************************************************/
2934 /* */
2935 /* FUNCTION RELEASE */
2936 /* */
2937 /* _nx_websocket_client_cleanup PORTABLE C */
2938 /* 6.2.0 */
2939 /* AUTHOR */
2940 /* */
2941 /* Bo Chen, Microsoft Corporation */
2942 /* */
2943 /* DESCRIPTION */
2944 /* */
2945 /* This function cleanups resources. */
2946 /* */
2947 /* INPUT */
2948 /* */
2949 /* client_ptr Pointer to WebSocket Client */
2950 /* */
2951 /* OUTPUT */
2952 /* */
2953 /* status Completion status */
2954 /* */
2955 /* CALLS */
2956 /* */
2957 /* None */
2958 /* */
2959 /* CALLED BY */
2960 /* */
2961 /* _nx_websocket_client_delete Delete websocket instance */
2962 /* _nx_websocket_client_data_process Process data frame */
2963 /* _nx_websocket_client_connect_response_check */
2964 /* Check connect response */
2965 /* _nx_websocket_client_connect_internal Make websocket connection */
2966 /* */
2967 /* RELEASE HISTORY */
2968 /* */
2969 /* DATE NAME DESCRIPTION */
2970 /* */
2971 /* 10-31-2022 Bo Chen Initial Version 6.2.0 */
2972 /* */
2973 /**************************************************************************/
_nx_websocket_client_cleanup(NX_WEBSOCKET_CLIENT * client_ptr)2974 void _nx_websocket_client_cleanup(NX_WEBSOCKET_CLIENT *client_ptr)
2975 {
2976
2977 /* Reset the flag for frame header found */
2978 client_ptr -> nx_websocket_client_frame_header_found = NX_FALSE;
2979
2980 /* Release the waiting list. */
2981 if (client_ptr -> nx_websocket_client_processing_packet)
2982 {
2983 nx_packet_release(client_ptr -> nx_websocket_client_processing_packet);
2984 client_ptr -> nx_websocket_client_processing_packet = NX_NULL;
2985 }
2986 }
2987