Sergey Pastor / 1

Dependents:   Nucleo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers web_socket_misc.c Source File

web_socket_misc.c

Go to the documentation of this file.
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