Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
web_socket_misc.c
00001 /** 00002 * @file web_socket_misc.c 00003 * @brief Helper functions for WebSockets 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include <stdlib.h> 00034 #include "core/net.h" 00035 #include "web_socket/web_socket.h" 00036 #include "web_socket/web_socket_auth.h" 00037 #include "web_socket/web_socket_frame.h" 00038 #include "web_socket/web_socket_transport.h" 00039 #include "web_socket/web_socket_misc.h" 00040 #include "base64.h" 00041 #include "sha1.h" 00042 #include "str.h" 00043 #include "debug.h" 00044 00045 //Check TCP/IP stack configuration 00046 #if (WEB_SOCKET_SUPPORT == ENABLED) 00047 00048 //WebSocket GUID 00049 const char_t webSocketGuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 00050 00051 00052 /** 00053 * @brief HTTP status codes 00054 **/ 00055 00056 static const WebSocketStatusCodeDesc statusCodeList[] = 00057 { 00058 //Success 00059 {200, "OK"}, 00060 {201, "Created"}, 00061 {202, "Accepted"}, 00062 {204, "No Content"}, 00063 //Redirection 00064 {301, "Moved Permanently"}, 00065 {302, "Found"}, 00066 {304, "Not Modified"}, 00067 //Client error 00068 {400, "Bad Request"}, 00069 {401, "Unauthorized"}, 00070 {403, "Forbidden"}, 00071 {404, "Not Found"}, 00072 //Server error 00073 {500, "Internal Server Error"}, 00074 {501, "Not Implemented"}, 00075 {502, "Bad Gateway"}, 00076 {503, "Service Unavailable"} 00077 }; 00078 00079 00080 /** 00081 * @brief Update WebSocket state 00082 * @param[in] webSocket Handle to a WebSocket 00083 * @param[in] newState New state to switch to 00084 **/ 00085 00086 void webSocketChangeState(WebSocket *webSocket, WebSocketState newState) 00087 { 00088 //Switch to the new state 00089 webSocket->state = newState; 00090 //Save current time; 00091 webSocket->timestamp = osGetSystemTime(); 00092 00093 //Reset sub-state 00094 webSocket->txContext.state = WS_SUB_STATE_INIT; 00095 webSocket->rxContext.state = WS_SUB_STATE_INIT; 00096 } 00097 00098 00099 /** 00100 * @brief Parse client or server handshake 00101 * @param[in] webSocket Handle to a WebSocket 00102 * @return Error code 00103 **/ 00104 00105 error_t webSocketParseHandshake(WebSocket *webSocket) 00106 { 00107 error_t error; 00108 size_t n; 00109 WebSocketFrameContext *rxContext; 00110 00111 //Point to the RX context 00112 rxContext = &webSocket->rxContext; 00113 00114 //Initialize status code 00115 error = NO_ERROR; 00116 00117 //Wait for the handshake to complete 00118 while(1) 00119 { 00120 //Client or server operation? 00121 if(webSocket->endpoint == WS_ENDPOINT_CLIENT) 00122 { 00123 if(webSocket->state == WS_STATE_OPEN) 00124 break; 00125 } 00126 else 00127 { 00128 if(webSocket->state == WS_STATE_SERVER_HANDSHAKE) 00129 break; 00130 } 00131 00132 //Check current sub-state 00133 if(rxContext->state == WS_SUB_STATE_INIT) 00134 { 00135 //Initialize status code 00136 webSocket->statusCode = WS_STATUS_CODE_NO_STATUS_RCVD; 00137 00138 //Initialize FIN flag 00139 rxContext->fin = TRUE; 00140 00141 //Flush the receive buffer 00142 rxContext->bufferPos = 0; 00143 rxContext->bufferLen = 0; 00144 00145 //Initialize variables 00146 webSocket->handshakeContext.statusCode = 0; 00147 webSocket->handshakeContext.upgradeWebSocket = FALSE; 00148 webSocket->handshakeContext.connectionUpgrade = FALSE; 00149 webSocket->handshakeContext.connectionClose = FALSE; 00150 webSocket->handshakeContext.contentLength = 0; 00151 webSocket->handshakeContext.closingFrameSent = FALSE; 00152 webSocket->handshakeContext.closingFrameReceived = FALSE; 00153 00154 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00155 webSocket->authContext.requiredAuthMode = WS_AUTH_MODE_NONE; 00156 #endif 00157 00158 #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00159 strcpy(webSocket->authContext.nonce, ""); 00160 strcpy(webSocket->authContext.opaque, ""); 00161 webSocket->authContext.stale = FALSE; 00162 #endif 00163 00164 //Client or server operation? 00165 if(webSocket->endpoint == WS_ENDPOINT_CLIENT) 00166 { 00167 //Clear server key 00168 strcpy(webSocket->handshakeContext.serverKey, ""); 00169 00170 //Debug message 00171 TRACE_DEBUG("WebSocket: server handshake\r\n"); 00172 } 00173 else 00174 { 00175 //Clear client key 00176 strcpy(webSocket->handshakeContext.clientKey, ""); 00177 00178 //Debug message 00179 TRACE_DEBUG("WebSocket: client handshake\r\n"); 00180 } 00181 00182 //Decode the leading line 00183 rxContext->state = WS_SUB_STATE_HANDSHAKE_LEADING_LINE; 00184 } 00185 else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LEADING_LINE) 00186 { 00187 //Check whether more data is required 00188 if(rxContext->bufferLen < 2) 00189 { 00190 //Limit the number of characters to read at a time 00191 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen; 00192 00193 //Read data until a CLRF character is encountered 00194 error = webSocketReceiveData(webSocket, rxContext->buffer + 00195 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF); 00196 00197 //Update the length of the buffer 00198 rxContext->bufferLen += n; 00199 } 00200 else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1)) 00201 { 00202 //Report an error 00203 error = ERROR_INVALID_REQUEST; 00204 } 00205 else if(strncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2, "\r\n", 2)) 00206 { 00207 //Limit the number of characters to read at a time 00208 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen; 00209 00210 //Read data until a CLRF character is encountered 00211 error = webSocketReceiveData(webSocket, rxContext->buffer + 00212 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF); 00213 00214 //Update the length of the buffer 00215 rxContext->bufferLen += n; 00216 } 00217 else 00218 { 00219 //Properly terminate the string with a NULL character 00220 rxContext->buffer[rxContext->bufferLen] = '\0'; 00221 00222 //Client or server operation? 00223 if(webSocket->endpoint == WS_ENDPOINT_CLIENT) 00224 { 00225 //The leading line from the server follows the Status-Line format 00226 error = webSocketParseStatusLine(webSocket, (char_t *) rxContext->buffer); 00227 } 00228 else 00229 { 00230 //The leading line from the client follows the Request-Line format 00231 error = webSocketParseRequestLine(webSocket, (char_t *) rxContext->buffer); 00232 } 00233 00234 //Flush the receive buffer 00235 rxContext->bufferPos = 0; 00236 rxContext->bufferLen = 0; 00237 00238 //Parse the header fields of the handshake 00239 rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD; 00240 } 00241 } 00242 else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_HEADER_FIELD) 00243 { 00244 //Check whether more data is required 00245 if(rxContext->bufferLen < 2) 00246 { 00247 //Limit the number of characters to read at a time 00248 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen; 00249 00250 //Read data until a CLRF character is encountered 00251 error = webSocketReceiveData(webSocket, rxContext->buffer + 00252 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF); 00253 00254 //Update the length of the buffer 00255 rxContext->bufferLen += n; 00256 } 00257 else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1)) 00258 { 00259 //Report an error 00260 error = ERROR_INVALID_REQUEST; 00261 } 00262 else if(strncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2, "\r\n", 2)) 00263 { 00264 //Limit the number of characters to read at a time 00265 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen; 00266 00267 //Read data until a CLRF character is encountered 00268 error = webSocketReceiveData(webSocket, rxContext->buffer + 00269 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF); 00270 00271 //Update the length of the buffer 00272 rxContext->bufferLen += n; 00273 } 00274 else 00275 { 00276 //Properly terminate the string with a NULL character 00277 rxContext->buffer[rxContext->bufferLen] = '\0'; 00278 00279 //An empty line indicates the end of the header fields 00280 if(!strcmp((char_t *) rxContext->buffer, "\r\n")) 00281 { 00282 //Client or server operation? 00283 if(webSocket->endpoint == WS_ENDPOINT_CLIENT) 00284 { 00285 //Verify server's handshake 00286 error = webSocketVerifyServerHandshake(webSocket); 00287 } 00288 else 00289 { 00290 //Verify client's handshake 00291 error = webSocketVerifyClientHandshake(webSocket); 00292 } 00293 } 00294 else 00295 { 00296 //Read the next character to detect if the CRLF is immediately 00297 //followed by a LWSP character 00298 rxContext->state = WS_SUB_STATE_HANDSHAKE_LWSP; 00299 } 00300 } 00301 } 00302 else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LWSP) 00303 { 00304 char_t nextChar; 00305 00306 //Read the next character 00307 error = webSocketReceiveData(webSocket, &nextChar, sizeof(char_t), &n, 0); 00308 00309 //Successful read operation? 00310 if(!error && n == sizeof(char_t)) 00311 { 00312 //LWSP character found? 00313 if(nextChar == ' ' || nextChar == '\t') 00314 { 00315 //Unfolding is accomplished by regarding CRLF immediately 00316 //followed by a LWSP as equivalent to the LWSP character 00317 if(rxContext->bufferLen >= 2) 00318 { 00319 //Remove trailing CRLF sequence 00320 rxContext->bufferLen -= 2; 00321 } 00322 00323 //The header field spans multiple line 00324 rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD; 00325 } 00326 else 00327 { 00328 //Parse header field 00329 error = webSocketParseHeaderField(webSocket, (char_t *) rxContext->buffer); 00330 00331 //Restore the very first character of the header field 00332 rxContext->buffer[0] = nextChar; 00333 //Adjust the length of the receive buffer 00334 rxContext->bufferLen = sizeof(char_t); 00335 00336 //Decode the next header field 00337 rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD; 00338 } 00339 } 00340 } 00341 else 00342 { 00343 //Invalid state 00344 error = ERROR_WRONG_STATE; 00345 } 00346 00347 //Any error to report? 00348 if(error) 00349 break; 00350 } 00351 00352 //Return status code 00353 return error; 00354 } 00355 00356 00357 /** 00358 * @brief Parse the Request-Line of the client's handshake 00359 * @param[in] webSocket Handle to a WebSocket 00360 * @param[in] line NULL-terminated string that contains the Request-Line 00361 * @return Error code 00362 **/ 00363 00364 error_t webSocketParseRequestLine(WebSocket *webSocket, char_t *line) 00365 { 00366 error_t error; 00367 char_t *token; 00368 char_t *p; 00369 char_t *s; 00370 00371 //Debug message 00372 TRACE_DEBUG("%s", line); 00373 00374 //The Request-Line begins with a method token 00375 token = strtok_r(line, " \r\n", &p); 00376 //Unable to retrieve the method? 00377 if(token == NULL) 00378 return ERROR_INVALID_REQUEST; 00379 00380 //The method of the request must be GET 00381 if(strcasecmp(token, "GET")) 00382 return ERROR_INVALID_REQUEST; 00383 00384 //The Request-URI is following the method token 00385 token = strtok_r(NULL, " \r\n", &p); 00386 //Unable to retrieve the Request-URI? 00387 if(token == NULL) 00388 return ERROR_INVALID_REQUEST; 00389 00390 //Check whether a query string is present 00391 s = strchr(token, '?'); 00392 00393 //Query string found? 00394 if(s != NULL) 00395 { 00396 //Split the string 00397 *s = '\0'; 00398 00399 //Save the Request-URI 00400 error = webSocketDecodePercentEncodedString(token, 00401 webSocket->uri, WEB_SOCKET_URI_MAX_LEN); 00402 //Any error to report? 00403 if(error) 00404 return ERROR_INVALID_REQUEST; 00405 00406 //Check the length of the query string 00407 if(strlen(s + 1) > WEB_SOCKET_QUERY_STRING_MAX_LEN) 00408 return ERROR_INVALID_REQUEST; 00409 00410 //Save the query string 00411 strcpy(webSocket->queryString, s + 1); 00412 } 00413 else 00414 { 00415 //Save the Request-URI 00416 error = webSocketDecodePercentEncodedString(token, 00417 webSocket->uri, WEB_SOCKET_URI_MAX_LEN); 00418 //Any error to report? 00419 if(error) 00420 return ERROR_INVALID_REQUEST; 00421 00422 //No query string 00423 webSocket->queryString[0] = '\0'; 00424 } 00425 00426 //The protocol version is following the Request-URI 00427 token = strtok_r(NULL, " \r\n", &p); 00428 00429 //HTTP version 0.9? 00430 if(token == NULL) 00431 { 00432 //Save version number 00433 webSocket->handshakeContext.version = WS_HTTP_VERSION_0_9; 00434 //Persistent connections are not supported 00435 webSocket->handshakeContext.connectionClose = TRUE; 00436 } 00437 //HTTP version 1.0? 00438 else if(!strcasecmp(token, "HTTP/1.0")) 00439 { 00440 //Save version number 00441 webSocket->handshakeContext.version = WS_HTTP_VERSION_1_0; 00442 //By default connections are not persistent 00443 webSocket->handshakeContext.connectionClose = TRUE; 00444 } 00445 //HTTP version 1.1? 00446 else if(!strcasecmp(token, "HTTP/1.1")) 00447 { 00448 //Save version number 00449 webSocket->handshakeContext.version = WS_HTTP_VERSION_1_1; 00450 //HTTP 1.1 makes persistent connections the default 00451 webSocket->handshakeContext.connectionClose = FALSE; 00452 } 00453 //HTTP version not supported? 00454 else 00455 { 00456 //Report an error 00457 return ERROR_INVALID_REQUEST; 00458 } 00459 00460 //Successful processing 00461 return NO_ERROR; 00462 } 00463 00464 00465 /** 00466 * @brief Parse the Status-Line of the server's handshake 00467 * @param[in] webSocket Handle to a WebSocket 00468 * @param[in] line NULL-terminated string that contains the Status-Line 00469 * @return Error code 00470 **/ 00471 00472 error_t webSocketParseStatusLine(WebSocket *webSocket, char_t *line) 00473 { 00474 char_t *p; 00475 char_t *token; 00476 00477 //Debug message 00478 TRACE_DEBUG("%s", line); 00479 00480 //Retrieve the HTTP-Version field 00481 token = strtok_r(line, " ", &p); 00482 //Any parsing error? 00483 if(token == NULL) 00484 return ERROR_INVALID_SYNTAX; 00485 00486 //Retrieve the Status-Code field 00487 token = strtok_r(NULL, " ", &p); 00488 //Any parsing error? 00489 if(token == NULL) 00490 return ERROR_INVALID_SYNTAX; 00491 00492 //Convert the status code 00493 webSocket->handshakeContext.statusCode = strtoul(token, &p, 10); 00494 //Any parsing error? 00495 if(*p != '\0') 00496 return ERROR_INVALID_SYNTAX; 00497 00498 //Successful processing 00499 return NO_ERROR; 00500 } 00501 00502 00503 /** 00504 * @brief Parse a header field 00505 * @param[in] webSocket Handle to a WebSocket 00506 * @param[in] line NULL-terminated string that contains the header field 00507 * @return Error code 00508 **/ 00509 00510 error_t webSocketParseHeaderField(WebSocket *webSocket, char_t *line) 00511 { 00512 char_t *separator; 00513 char_t *name; 00514 char_t *value; 00515 WebSocketHandshakeContext *handshakeContext; 00516 00517 //Point to the handshake context 00518 handshakeContext = &webSocket->handshakeContext; 00519 00520 //Debug message 00521 TRACE_DEBUG("%s", line); 00522 00523 //Check whether a separator is present 00524 separator = strchr(line, ':'); 00525 00526 //Separator found? 00527 if(separator != NULL) 00528 { 00529 //Split the line 00530 *separator = '\0'; 00531 00532 //Get field name and value 00533 name = strTrimWhitespace(line); 00534 value = strTrimWhitespace(separator + 1); 00535 00536 //Upgrade header field found? 00537 if(!strcasecmp(name, "Upgrade")) 00538 { 00539 if(!strcasecmp(value, "websocket")) 00540 handshakeContext->upgradeWebSocket = TRUE; 00541 00542 } 00543 //Connection header field found? 00544 else if(!strcasecmp(name, "Connection")) 00545 { 00546 //Parse Connection header field 00547 webSocketParseConnectionField(webSocket, value); 00548 } 00549 //Sec-WebSocket-Key header field found? 00550 else if(!strcasecmp(name, "Sec-WebSocket-Key")) 00551 { 00552 //Server operation? 00553 if(webSocket->endpoint == WS_ENDPOINT_SERVER) 00554 { 00555 //Save the contents of the Sec-WebSocket-Key header field 00556 strSafeCopy(handshakeContext->clientKey, value, 00557 WEB_SOCKET_CLIENT_KEY_SIZE + 1); 00558 } 00559 } 00560 //Sec-WebSocket-Accept header field found? 00561 else if(!strcasecmp(name, "Sec-WebSocket-Accept")) 00562 { 00563 //Client operation? 00564 if(webSocket->endpoint == WS_ENDPOINT_CLIENT) 00565 { 00566 //Save the contents of the Sec-WebSocket-Accept header field 00567 strSafeCopy(handshakeContext->serverKey, value, 00568 WEB_SOCKET_SERVER_KEY_SIZE + 1); 00569 } 00570 } 00571 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00572 //WWW-Authenticate header field found? 00573 else if(!strcasecmp(name, "WWW-Authenticate")) 00574 { 00575 //Parse WWW-Authenticate header field 00576 webSocketParseAuthenticateField(webSocket, value); 00577 } 00578 #endif 00579 //Content-Length header field found? 00580 else if(!strcasecmp(name, "Content-Length")) 00581 { 00582 handshakeContext->contentLength = strtoul(value, NULL, 10); 00583 } 00584 } 00585 00586 //Successful processing 00587 return NO_ERROR; 00588 } 00589 00590 00591 /** 00592 * @brief Parse Connection header field 00593 * @param[in] webSocket Handle to a WebSocket 00594 * @param[in] value NULL-terminated string that contains the value of header field 00595 **/ 00596 00597 void webSocketParseConnectionField(WebSocket *webSocket, char_t *value) 00598 { 00599 char_t *p; 00600 char_t *token; 00601 00602 //Get the first value of the list 00603 token = strtok_r(value, ",", &p); 00604 00605 //Parse the comma-separated list 00606 while(token != NULL) 00607 { 00608 //Trim whitespace characters 00609 value = strTrimWhitespace(token); 00610 00611 //Check current value 00612 if(!strcasecmp(value, "keep-alive")) 00613 { 00614 //The connection is persistent 00615 webSocket->handshakeContext.connectionClose = FALSE; 00616 } 00617 else if(!strcasecmp(value, "close")) 00618 { 00619 //The connection will be closed after completion of the response 00620 webSocket->handshakeContext.connectionClose = TRUE; 00621 } 00622 else if(!strcasecmp(value, "upgrade")) 00623 { 00624 //Upgrade the connection 00625 webSocket->handshakeContext.connectionUpgrade = TRUE; 00626 } 00627 00628 //Get next value 00629 token = strtok_r(NULL, ",", &p); 00630 } 00631 } 00632 00633 00634 /** 00635 * @brief Format client's handshake 00636 * @param[in] webSocket Handle to a WebSocket 00637 * @param[in] serverPort TCP port number used to establish the connection 00638 * @return Error code 00639 **/ 00640 00641 error_t webSocketFormatClientHandshake(WebSocket *webSocket, uint16_t serverPort) 00642 { 00643 char_t *p; 00644 WebSocketFrameContext *txContext; 00645 00646 //Point to the TX context 00647 txContext = &webSocket->txContext; 00648 //Point to the buffer where to format the client's handshake 00649 p = (char_t *) txContext->buffer; 00650 00651 //The Request-Line begins with a method token, followed by the 00652 //Request-URI and the protocol version, and ending with CRLF 00653 p += sprintf(p, "GET %s HTTP/1.1\r\n", webSocket->uri); 00654 00655 //Add Host header field 00656 if(webSocket->host[0] != '\0') 00657 { 00658 //The Host header field specifies the Internet host and port number of 00659 //the resource being requested 00660 p += sprintf(p, "Host: %s:%d\r\n", webSocket->host, serverPort); 00661 } 00662 else 00663 { 00664 //If the requested URI does not include a host name for the service being 00665 //requested, then the Host header field must be given with an empty value 00666 p += sprintf(p, "Host:\r\n"); 00667 } 00668 00669 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED) 00670 //Check whether authentication is required 00671 if(webSocket->authContext.selectedAuthMode != WS_AUTH_MODE_NONE) 00672 { 00673 //Add Authorization header field 00674 p += webSocketAddAuthorizationField(webSocket, p); 00675 } 00676 #endif 00677 00678 //Add Origin header field 00679 if(webSocket->origin[0] != '\0') 00680 p += sprintf(p, "Origin: %s\r\n", webSocket->origin); 00681 else 00682 p += sprintf(p, "Origin: null\r\n"); 00683 00684 //Add Upgrade header field 00685 p += sprintf(p, "Upgrade: websocket\r\n"); 00686 //Add Connection header field 00687 p += sprintf(p, "Connection: Upgrade\r\n"); 00688 00689 //Add Sec-WebSocket-Protocol header field 00690 if(webSocket->subProtocol[0] != '\0') 00691 p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol); 00692 00693 //Add Sec-WebSocket-Key header field 00694 p += sprintf(p, "Sec-WebSocket-Key: %s\r\n", 00695 webSocket->handshakeContext.clientKey); 00696 00697 //Add Sec-WebSocket-Version header field 00698 p += sprintf(p, "Sec-WebSocket-Version: 13\r\n"); 00699 //An empty line indicates the end of the header fields 00700 p += sprintf(p, "\r\n"); 00701 00702 //Debug message 00703 TRACE_DEBUG("\r\n"); 00704 TRACE_DEBUG("WebSocket: client handshake\r\n"); 00705 TRACE_DEBUG("%s", txContext->buffer); 00706 00707 //Rewind to the beginning of the buffer 00708 txContext->bufferPos = 0; 00709 //Update the number of data buffered but not yet sent 00710 txContext->bufferLen = strlen((char_t *) txContext->buffer); 00711 00712 //Successful processing 00713 return NO_ERROR; 00714 } 00715 00716 00717 /** 00718 * @brief Format server's handshake 00719 * @param[in] webSocket Handle to a WebSocket 00720 * @return Error code 00721 **/ 00722 00723 error_t webSocketFormatServerHandshake(WebSocket *webSocket) 00724 { 00725 char_t *p; 00726 WebSocketFrameContext *txContext; 00727 00728 //Point to the TX context 00729 txContext = &webSocket->txContext; 00730 //Point to the buffer where to format the client's handshake 00731 p = (char_t *) txContext->buffer; 00732 00733 //The first line is an HTTP Status-Line, with the status code 101 00734 p += sprintf(p, "HTTP/1.1 101 Switching Protocols\r\n"); 00735 00736 //Add Upgrade header field 00737 p += sprintf(p, "Upgrade: websocket\r\n"); 00738 //Add Connection header field 00739 p += sprintf(p, "Connection: Upgrade\r\n"); 00740 00741 //Add Sec-WebSocket-Protocol header field 00742 if(webSocket->subProtocol[0] != '\0') 00743 p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol); 00744 00745 //Add Sec-WebSocket-Accept header field 00746 p += sprintf(p, "Sec-WebSocket-Accept: %s\r\n", 00747 webSocket->handshakeContext.serverKey); 00748 00749 //An empty line indicates the end of the header fields 00750 p += sprintf(p, "\r\n"); 00751 00752 //Debug message 00753 TRACE_DEBUG("\r\n"); 00754 TRACE_DEBUG("WebSocket: server handshake\r\n"); 00755 TRACE_DEBUG("%s", txContext->buffer); 00756 00757 //Rewind to the beginning of the buffer 00758 txContext->bufferPos = 0; 00759 //Update the number of data buffered but not yet sent 00760 txContext->bufferLen = strlen((char_t *) txContext->buffer); 00761 00762 //Successful processing 00763 return NO_ERROR; 00764 } 00765 00766 00767 /** 00768 * @brief Format HTTP error response 00769 * @param[in] webSocket Handle to a WebSocket 00770 * @param[in] statusCode HTTP status code 00771 * @param[in] message User message 00772 * @return Error code 00773 **/ 00774 00775 error_t webSocketFormatErrorResponse(WebSocket *webSocket, 00776 uint_t statusCode, const char_t *message) 00777 { 00778 uint_t i; 00779 size_t length; 00780 char_t *p; 00781 WebSocketFrameContext *txContext; 00782 00783 //HTML response template 00784 static const char_t template[] = 00785 "<!doctype html>\r\n" 00786 "<html>\r\n" 00787 "<head><title>Error %03d</title></head>\r\n" 00788 "<body>\r\n" 00789 "<h2>Error %03d</h2>\r\n" 00790 "<p>%s</p>\r\n" 00791 "</body>\r\n" 00792 "</html>\r\n"; 00793 00794 //Point to the TX context 00795 txContext = &webSocket->txContext; 00796 //Point to the buffer where to format the client's handshake 00797 p = (char_t *) txContext->buffer; 00798 00799 //The first line of a response message is the Status-Line, consisting 00800 //of the protocol version followed by a numeric status code and its 00801 //associated textual phrase 00802 p += sprintf(p, "HTTP/%u.%u %u ", MSB(webSocket->handshakeContext.version), 00803 LSB(webSocket->handshakeContext.version), statusCode); 00804 00805 //Retrieve the Reason-Phrase that corresponds to the Status-Code 00806 for(i = 0; i < arraysize(statusCodeList); i++) 00807 { 00808 //Check the status code 00809 if(statusCodeList[i].value == statusCode) 00810 { 00811 //Append the textual phrase to the Status-Line 00812 p += sprintf(p, statusCodeList[i].message); 00813 //Break the loop and continue processing 00814 break; 00815 } 00816 } 00817 00818 //Properly terminate the Status-Line 00819 p += sprintf(p, "\r\n"); 00820 00821 //Content type 00822 p += sprintf(p, "Content-Type: %s\r\n", "text/html"); 00823 00824 //Compute the length of the response 00825 length = strlen(template) + strlen(message) - 4; 00826 //Set Content-Length field 00827 p += sprintf(p, "Content-Length: %" PRIuSIZE "\r\n", length); 00828 00829 //Terminate the header with an empty line 00830 p += sprintf(p, "\r\n"); 00831 00832 //Format HTML response 00833 p += sprintf(p, template, statusCode, statusCode, message); 00834 00835 //Rewind to the beginning of the buffer 00836 txContext->bufferPos = 0; 00837 //Update the number of data buffered but not yet sent 00838 txContext->bufferLen = strlen((char_t *) txContext->buffer); 00839 00840 //Successful processing 00841 return NO_ERROR; 00842 } 00843 00844 00845 /** 00846 * @brief Verify client's handshake 00847 * @param[in] webSocket Handle to a WebSocket 00848 * @return Error code 00849 **/ 00850 00851 error_t webSocketVerifyClientHandshake(WebSocket *webSocket) 00852 { 00853 error_t error; 00854 WebSocketHandshakeContext *handshakeContext; 00855 00856 //Debug message 00857 TRACE_DEBUG("WebSocket: verifying client handshake\r\n"); 00858 00859 //Point to the handshake context 00860 handshakeContext = &webSocket->handshakeContext; 00861 00862 //The HTTP version must be at least 1.1 00863 if(handshakeContext->version < WS_HTTP_VERSION_1_1) 00864 return ERROR_INVALID_REQUEST; 00865 00866 //The request must contain an Upgrade header field whose value 00867 //must include the "websocket" keyword 00868 if(!handshakeContext->upgradeWebSocket) 00869 return ERROR_INVALID_REQUEST; 00870 00871 //The request must contain a Connection header field whose value 00872 //must include the "Upgrade" token 00873 if(!handshakeContext->connectionUpgrade) 00874 return ERROR_INVALID_REQUEST; 00875 00876 //The request must include a header field with the name Sec-WebSocket-Key 00877 if(handshakeContext->clientKey[0] == 0) 00878 return ERROR_INVALID_REQUEST; 00879 00880 //Check the Sec-WebSocket-Key header field 00881 error = webSocketVerifyClientKey(webSocket); 00882 //Verification failed? 00883 if(error) 00884 return error; 00885 00886 //Generate the server part of the handshake 00887 webSocketChangeState(webSocket, WS_STATE_SERVER_HANDSHAKE); 00888 00889 //Successful processing 00890 return NO_ERROR; 00891 } 00892 00893 00894 /** 00895 * @brief Verify server's handshake 00896 * @param[in] webSocket Handle to a WebSocket 00897 * @return Error code 00898 **/ 00899 00900 error_t webSocketVerifyServerHandshake(WebSocket *webSocket) 00901 { 00902 error_t error; 00903 WebSocketHandshakeContext *handshakeContext; 00904 00905 //Debug message 00906 TRACE_DEBUG("WebSocket: verifying server handshake\r\n"); 00907 00908 //Point to the handshake context 00909 handshakeContext = &webSocket->handshakeContext; 00910 00911 //If the status code received from the server is not 101, the client 00912 //handles the response per HTTP procedures 00913 if(handshakeContext->statusCode == 401) 00914 { 00915 //Authorization required 00916 return ERROR_AUTH_REQUIRED; 00917 } 00918 else if(handshakeContext->statusCode != 101) 00919 { 00920 //Unknown status code 00921 return ERROR_INVALID_STATUS; 00922 } 00923 00924 //If the response lacks an Upgrade header field or the Upgrade header field 00925 //contains a value that is not an ASCII case-insensitive match for the 00926 //value "websocket", the client must fail the WebSocket connection 00927 if(!handshakeContext->upgradeWebSocket) 00928 return ERROR_INVALID_SYNTAX; 00929 00930 //If the response lacks a Connection header field or the Connection header 00931 //field doesn't contain a token that is an ASCII case-insensitive match for 00932 //the value "Upgrade", the client must fail the WebSocket connection 00933 if(!handshakeContext->connectionUpgrade) 00934 return ERROR_INVALID_SYNTAX; 00935 00936 //If the response lacks a Sec-WebSocket-Accept header field, the client 00937 //must fail the WebSocket connection 00938 if(strlen(handshakeContext->serverKey) == 0) 00939 return ERROR_INVALID_SYNTAX; 00940 00941 //Check the Sec-WebSocket-Accept header field 00942 error = webSocketVerifyServerKey(webSocket); 00943 //Verification failed? 00944 if(error) 00945 return error; 00946 00947 //If the server's response is validated as provided for above, it is 00948 //said that the WebSocket connection is established and that the 00949 //WebSocket connection is in the OPEN state 00950 webSocketChangeState(webSocket, WS_STATE_OPEN); 00951 00952 //Successful processing 00953 return NO_ERROR; 00954 } 00955 00956 00957 /** 00958 * @brief Generate client's key 00959 * @param[in] webSocket Handle to a WebSocket 00960 * @return Error code 00961 **/ 00962 00963 error_t webSocketGenerateClientKey(WebSocket *webSocket) 00964 { 00965 error_t error; 00966 size_t n; 00967 uint8_t nonce[16]; 00968 WebSocketHandshakeContext *handshakeContext; 00969 00970 //Debug message 00971 TRACE_DEBUG("WebSocket: Generating client's key...\r\n"); 00972 00973 //Point to the handshake context 00974 handshakeContext = &webSocket->handshakeContext; 00975 00976 //Make sure that the RNG callback function has been registered 00977 if(webSockRandCallback == NULL) 00978 { 00979 //A cryptographically strong random number generator 00980 //must be used to generate the nonce 00981 return ERROR_PRNG_NOT_READY; 00982 } 00983 00984 //A nonce must be selected randomly for each connection 00985 error = webSockRandCallback(nonce, sizeof(nonce)); 00986 //Any error to report? 00987 if(error) 00988 return error; 00989 00990 //Encode the client's key 00991 base64Encode(nonce, sizeof(nonce), handshakeContext->clientKey, &n); 00992 00993 //Debug message 00994 TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey); 00995 00996 //Successful processing 00997 return NO_ERROR; 00998 } 00999 01000 01001 /** 01002 * @brief Generate server's key 01003 * @param[in] webSocket Handle to a WebSocket 01004 * @return Error code 01005 **/ 01006 01007 error_t webSocketGenerateServerKey(WebSocket *webSocket) 01008 { 01009 size_t n; 01010 WebSocketHandshakeContext *handshakeContext; 01011 Sha1Context sha1Context; 01012 01013 //Debug message 01014 TRACE_DEBUG("WebSocket: Generating server's key...\r\n"); 01015 01016 //Point to the handshake context 01017 handshakeContext = &webSocket->handshakeContext; 01018 01019 //Retrieve the length of the client key 01020 n = strlen(handshakeContext->clientKey); 01021 01022 //Concatenate the Sec-WebSocket-Key with the GUID string and digest 01023 //the resulting string using SHA-1 01024 sha1Init(&sha1Context); 01025 sha1Update(&sha1Context, handshakeContext->clientKey, n); 01026 sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid)); 01027 sha1Final(&sha1Context, NULL); 01028 01029 //Encode the result using Base64 01030 base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE, 01031 handshakeContext->serverKey, &n); 01032 01033 //Debug message 01034 TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey); 01035 01036 //Successful processing 01037 return NO_ERROR; 01038 } 01039 01040 01041 /** 01042 * @brief Verify client's key 01043 * @param[in] webSocket Handle to a WebSocket 01044 * @return Error code 01045 **/ 01046 01047 error_t webSocketVerifyClientKey(WebSocket *webSocket) 01048 { 01049 error_t error; 01050 size_t n; 01051 char_t *buffer; 01052 WebSocketHandshakeContext *handshakeContext; 01053 01054 //Debug message 01055 TRACE_DEBUG("WebSocket: Verifying client's key...\r\n"); 01056 01057 //Temporary buffer 01058 buffer = (char_t *) webSocket->txContext.buffer; 01059 01060 //Point to the handshake context 01061 handshakeContext = &webSocket->handshakeContext; 01062 01063 //Retrieve the length of the client's key 01064 n = strlen(handshakeContext->clientKey); 01065 01066 //The value of the Sec-WebSocket-Key header field must be a 16-byte 01067 //value that has been Base64-encoded 01068 error = base64Decode(handshakeContext->clientKey, n, buffer, &n); 01069 //Decoding failed? 01070 if(error) 01071 return ERROR_INVALID_KEY; 01072 01073 //Check the length of the resulting value 01074 if(n != 16) 01075 return ERROR_INVALID_KEY; 01076 01077 //Successful verification 01078 return NO_ERROR; 01079 } 01080 01081 01082 /** 01083 * @brief Verify server's key 01084 * @param[in] webSocket Handle to a WebSocket 01085 * @return Error code 01086 **/ 01087 01088 error_t webSocketVerifyServerKey(WebSocket *webSocket) 01089 { 01090 size_t n; 01091 char_t *buffer; 01092 WebSocketHandshakeContext *handshakeContext; 01093 Sha1Context sha1Context; 01094 01095 //Debug message 01096 TRACE_DEBUG("WebSocket: Verifying server's key...\r\n"); 01097 01098 //Temporary buffer 01099 buffer = (char_t *) webSocket->txContext.buffer; 01100 01101 //Point to the handshake context 01102 handshakeContext = &webSocket->handshakeContext; 01103 01104 //Retrieve the length of the client's key 01105 n = strlen(handshakeContext->clientKey); 01106 01107 //Concatenate the Sec-WebSocket-Key with the GUID string and digest 01108 //the resulting string using SHA-1 01109 sha1Init(&sha1Context); 01110 sha1Update(&sha1Context, handshakeContext->clientKey, n); 01111 sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid)); 01112 sha1Final(&sha1Context, NULL); 01113 01114 //Encode the result using Base64 01115 base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE, buffer, &n); 01116 01117 //Debug message 01118 TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey); 01119 TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey); 01120 TRACE_DEBUG(" Calculated key: %s\r\n", webSocket->txContext.buffer); 01121 01122 //Check whether the server's key is valid 01123 if(strcmp(handshakeContext->serverKey, buffer)) 01124 return ERROR_INVALID_KEY; 01125 01126 //Successful verification 01127 return NO_ERROR; 01128 } 01129 01130 01131 /** 01132 * @brief Check whether a status code is valid 01133 * @param[in] statusCode Status code 01134 * @return The function returns TRUE is the specified status code is 01135 * valid. Otherwise, FALSE is returned 01136 **/ 01137 01138 bool_t webSocketCheckStatusCode(uint16_t statusCode) 01139 { 01140 bool_t valid; 01141 01142 //Check status code 01143 if(statusCode == WS_STATUS_CODE_NORMAL_CLOSURE || 01144 statusCode == WS_STATUS_CODE_GOING_AWAY || 01145 statusCode == WS_STATUS_CODE_PROTOCOL_ERROR || 01146 statusCode == WS_STATUS_CODE_UNSUPPORTED_DATA || 01147 statusCode == WS_STATUS_CODE_INVALID_PAYLOAD_DATA || 01148 statusCode == WS_STATUS_CODE_POLICY_VIOLATION || 01149 statusCode == WS_STATUS_CODE_MESSAGE_TOO_BIG || 01150 statusCode == WS_STATUS_CODE_MANDATORY_EXT || 01151 statusCode == WS_STATUS_CODE_INTERNAL_ERROR) 01152 { 01153 valid = TRUE; 01154 } 01155 else if(statusCode >= 3000) 01156 { 01157 valid = TRUE; 01158 } 01159 else 01160 { 01161 valid = FALSE; 01162 } 01163 01164 //The function returns TRUE is the specified status code is valid 01165 return valid; 01166 } 01167 01168 01169 /** 01170 * @brief Decode a percent-encoded string 01171 * @param[in] input NULL-terminated string to be decoded 01172 * @param[out] output NULL-terminated string resulting from the decoding process 01173 * @param[in] outputSize Size of the output buffer in bytes 01174 * @return Error code 01175 **/ 01176 01177 error_t webSocketDecodePercentEncodedString(const char_t *input, 01178 char_t *output, size_t outputSize) 01179 { 01180 size_t i; 01181 char_t buffer[3]; 01182 01183 //Check parameters 01184 if(input == NULL || output == NULL) 01185 return ERROR_INVALID_PARAMETER; 01186 01187 //Decode the percent-encoded string 01188 for(i = 0; *input != '\0' && i < outputSize; i++) 01189 { 01190 //Check current character 01191 if(*input == '+') 01192 { 01193 //Replace '+' characters with spaces 01194 output[i] = ' '; 01195 //Advance data pointer 01196 input++; 01197 } 01198 else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0') 01199 { 01200 //Process percent-encoded characters 01201 buffer[0] = input[1]; 01202 buffer[1] = input[2]; 01203 buffer[2] = '\0'; 01204 //String to integer conversion 01205 output[i] = (uint8_t) strtoul(buffer, NULL, 16); 01206 //Advance data pointer 01207 input += 3; 01208 } 01209 else 01210 { 01211 //Copy any other characters 01212 output[i] = *input; 01213 //Advance data pointer 01214 input++; 01215 } 01216 } 01217 01218 //Check whether the output buffer runs out of space 01219 if(i >= outputSize) 01220 return ERROR_FAILURE; 01221 01222 //Properly terminate the resulting string 01223 output[i] = '\0'; 01224 //Successful processing 01225 return NO_ERROR; 01226 } 01227 01228 01229 /** 01230 * @brief Check whether a an UTF-8 stream is valid 01231 * @param[in] context UTF-8 decoding context 01232 * @param[in] data Pointer to the chunk of data to be processed 01233 * @param[in] length Data chunk length 01234 * @param[in] remaining number of remaining bytes in the UTF-8 stream 01235 * @return The function returns TRUE is the specified UTF-8 stream is 01236 * valid. Otherwise, FALSE is returned 01237 **/ 01238 01239 bool_t webSocketCheckUtf8Stream(WebSocketUtf8Context *context, 01240 const uint8_t *data, size_t length, size_t remaining) 01241 { 01242 size_t i; 01243 bool_t valid; 01244 01245 //Initialize flag 01246 valid = TRUE; 01247 01248 //Interpret the byte stream as UTF-8 01249 for(i = 0; i < length && valid; i++) 01250 { 01251 //Leading or continuation byte? 01252 if(context->utf8CharIndex == 0) 01253 { 01254 //7-bit code point? 01255 if((data[i] & 0x80) == 0x00) 01256 { 01257 //The code point consist of a single byte 01258 context->utf8CharSize = 1; 01259 //Decode the first byte of the sequence 01260 context->utf8CodePoint = data[i] & 0x7F; 01261 } 01262 //11-bit code point? 01263 else if((data[i] & 0xE0) == 0xC0) 01264 { 01265 //The code point consist of a 2 bytes 01266 context->utf8CharSize = 2; 01267 //Decode the first byte of the sequence 01268 context->utf8CodePoint = (data[i] & 0x1F) << 6; 01269 } 01270 //16-bit code point? 01271 else if((data[i] & 0xF0) == 0xE0) 01272 { 01273 //The code point consist of a 3 bytes 01274 context->utf8CharSize = 3; 01275 //Decode the first byte of the sequence 01276 context->utf8CodePoint = (data[i] & 0x0F) << 12; 01277 } 01278 //21-bit code point? 01279 else if((data[i] & 0xF8) == 0xF0) 01280 { 01281 //The code point consist of a 3 bytes 01282 context->utf8CharSize = 4; 01283 //Decode the first byte of the sequence 01284 context->utf8CodePoint = (data[i] & 0x07) << 18; 01285 } 01286 else 01287 { 01288 //The UTF-8 stream is not valid 01289 valid = FALSE; 01290 } 01291 01292 //This test only applies to frames that are not fragmented 01293 if(length <= remaining) 01294 { 01295 //Make sure the UTF-8 stream is properly terminated 01296 if((i + context->utf8CharSize) > remaining) 01297 { 01298 //The UTF-8 stream is not valid 01299 valid = FALSE; 01300 } 01301 } 01302 01303 //Decode the next byte of the sequence 01304 context->utf8CharIndex = context->utf8CharSize - 1; 01305 } 01306 else 01307 { 01308 //Continuation bytes all have 10 in the high-order position 01309 if((data[i] & 0xC0) == 0x80) 01310 { 01311 //Decode the multi-byte sequence 01312 context->utf8CharIndex--; 01313 //All continuation bytes contain exactly 6 bits from the code point 01314 context->utf8CodePoint |= (data[i] & 0x3F) << (context->utf8CharIndex * 6); 01315 01316 //The correct encoding of a code point use only the minimum number 01317 //of bytes required to hold the significant bits of the code point 01318 if(context->utf8CharSize == 2) 01319 { 01320 //Overlong encoding is not supported 01321 if((context->utf8CodePoint & ~0x7F) == 0) 01322 valid = FALSE; 01323 } 01324 if(context->utf8CharSize == 3 && context->utf8CharIndex < 2) 01325 { 01326 //Overlong encoding is not supported 01327 if((context->utf8CodePoint & ~0x7FF) == 0) 01328 valid = FALSE; 01329 } 01330 if(context->utf8CharSize == 4 && context->utf8CharIndex < 3) 01331 { 01332 //Overlong encoding is not supported 01333 if((context->utf8CodePoint & ~0xFFFF) == 0) 01334 valid = FALSE; 01335 } 01336 01337 //According to the UTF-8 definition (RFC 3629) the high and low 01338 //surrogate halves used by UTF-16 (U+D800 through U+DFFF) are not 01339 //legal Unicode values, and their UTF-8 encoding should be treated 01340 //as an invalid byte sequence 01341 if(context->utf8CodePoint >= 0xD800 && context->utf8CodePoint < 0xE000) 01342 valid = FALSE; 01343 01344 //Code points greater than U+10FFFF are not valid 01345 if(context->utf8CodePoint >= 0x110000) 01346 valid = FALSE; 01347 } 01348 else 01349 { 01350 //The start byte is not followed by enough continuation bytes 01351 valid = FALSE; 01352 } 01353 } 01354 } 01355 01356 //The function returns TRUE is the specified UTF-8 stream is valid 01357 return valid; 01358 } 01359 01360 #endif 01361
Generated on Tue Jul 12 2022 17:10:17 by
