Webserver+3d print

Dependents:   Nucleo

Committer:
Sergunb
Date:
Sat Feb 04 18:15:49 2017 +0000
Revision:
0:8918a71cdbe9
nothing else

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Sergunb 0:8918a71cdbe9 1 /**
Sergunb 0:8918a71cdbe9 2 * @file web_socket_misc.c
Sergunb 0:8918a71cdbe9 3 * @brief Helper functions for WebSockets
Sergunb 0:8918a71cdbe9 4 *
Sergunb 0:8918a71cdbe9 5 * @section License
Sergunb 0:8918a71cdbe9 6 *
Sergunb 0:8918a71cdbe9 7 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
Sergunb 0:8918a71cdbe9 8 *
Sergunb 0:8918a71cdbe9 9 * This file is part of CycloneTCP Open.
Sergunb 0:8918a71cdbe9 10 *
Sergunb 0:8918a71cdbe9 11 * This program is free software; you can redistribute it and/or
Sergunb 0:8918a71cdbe9 12 * modify it under the terms of the GNU General Public License
Sergunb 0:8918a71cdbe9 13 * as published by the Free Software Foundation; either version 2
Sergunb 0:8918a71cdbe9 14 * of the License, or (at your option) any later version.
Sergunb 0:8918a71cdbe9 15 *
Sergunb 0:8918a71cdbe9 16 * This program is distributed in the hope that it will be useful,
Sergunb 0:8918a71cdbe9 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Sergunb 0:8918a71cdbe9 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Sergunb 0:8918a71cdbe9 19 * GNU General Public License for more details.
Sergunb 0:8918a71cdbe9 20 *
Sergunb 0:8918a71cdbe9 21 * You should have received a copy of the GNU General Public License
Sergunb 0:8918a71cdbe9 22 * along with this program; if not, write to the Free Software Foundation,
Sergunb 0:8918a71cdbe9 23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Sergunb 0:8918a71cdbe9 24 *
Sergunb 0:8918a71cdbe9 25 * @author Oryx Embedded SARL (www.oryx-embedded.com)
Sergunb 0:8918a71cdbe9 26 * @version 1.7.6
Sergunb 0:8918a71cdbe9 27 **/
Sergunb 0:8918a71cdbe9 28
Sergunb 0:8918a71cdbe9 29 //Switch to the appropriate trace level
Sergunb 0:8918a71cdbe9 30 #define TRACE_LEVEL WEB_SOCKET_TRACE_LEVEL
Sergunb 0:8918a71cdbe9 31
Sergunb 0:8918a71cdbe9 32 //Dependencies
Sergunb 0:8918a71cdbe9 33 #include <stdlib.h>
Sergunb 0:8918a71cdbe9 34 #include "core/net.h"
Sergunb 0:8918a71cdbe9 35 #include "web_socket/web_socket.h"
Sergunb 0:8918a71cdbe9 36 #include "web_socket/web_socket_auth.h"
Sergunb 0:8918a71cdbe9 37 #include "web_socket/web_socket_frame.h"
Sergunb 0:8918a71cdbe9 38 #include "web_socket/web_socket_transport.h"
Sergunb 0:8918a71cdbe9 39 #include "web_socket/web_socket_misc.h"
Sergunb 0:8918a71cdbe9 40 #include "base64.h"
Sergunb 0:8918a71cdbe9 41 #include "sha1.h"
Sergunb 0:8918a71cdbe9 42 #include "str.h"
Sergunb 0:8918a71cdbe9 43 #include "debug.h"
Sergunb 0:8918a71cdbe9 44
Sergunb 0:8918a71cdbe9 45 //Check TCP/IP stack configuration
Sergunb 0:8918a71cdbe9 46 #if (WEB_SOCKET_SUPPORT == ENABLED)
Sergunb 0:8918a71cdbe9 47
Sergunb 0:8918a71cdbe9 48 //WebSocket GUID
Sergunb 0:8918a71cdbe9 49 const char_t webSocketGuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
Sergunb 0:8918a71cdbe9 50
Sergunb 0:8918a71cdbe9 51
Sergunb 0:8918a71cdbe9 52 /**
Sergunb 0:8918a71cdbe9 53 * @brief HTTP status codes
Sergunb 0:8918a71cdbe9 54 **/
Sergunb 0:8918a71cdbe9 55
Sergunb 0:8918a71cdbe9 56 static const WebSocketStatusCodeDesc statusCodeList[] =
Sergunb 0:8918a71cdbe9 57 {
Sergunb 0:8918a71cdbe9 58 //Success
Sergunb 0:8918a71cdbe9 59 {200, "OK"},
Sergunb 0:8918a71cdbe9 60 {201, "Created"},
Sergunb 0:8918a71cdbe9 61 {202, "Accepted"},
Sergunb 0:8918a71cdbe9 62 {204, "No Content"},
Sergunb 0:8918a71cdbe9 63 //Redirection
Sergunb 0:8918a71cdbe9 64 {301, "Moved Permanently"},
Sergunb 0:8918a71cdbe9 65 {302, "Found"},
Sergunb 0:8918a71cdbe9 66 {304, "Not Modified"},
Sergunb 0:8918a71cdbe9 67 //Client error
Sergunb 0:8918a71cdbe9 68 {400, "Bad Request"},
Sergunb 0:8918a71cdbe9 69 {401, "Unauthorized"},
Sergunb 0:8918a71cdbe9 70 {403, "Forbidden"},
Sergunb 0:8918a71cdbe9 71 {404, "Not Found"},
Sergunb 0:8918a71cdbe9 72 //Server error
Sergunb 0:8918a71cdbe9 73 {500, "Internal Server Error"},
Sergunb 0:8918a71cdbe9 74 {501, "Not Implemented"},
Sergunb 0:8918a71cdbe9 75 {502, "Bad Gateway"},
Sergunb 0:8918a71cdbe9 76 {503, "Service Unavailable"}
Sergunb 0:8918a71cdbe9 77 };
Sergunb 0:8918a71cdbe9 78
Sergunb 0:8918a71cdbe9 79
Sergunb 0:8918a71cdbe9 80 /**
Sergunb 0:8918a71cdbe9 81 * @brief Update WebSocket state
Sergunb 0:8918a71cdbe9 82 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 83 * @param[in] newState New state to switch to
Sergunb 0:8918a71cdbe9 84 **/
Sergunb 0:8918a71cdbe9 85
Sergunb 0:8918a71cdbe9 86 void webSocketChangeState(WebSocket *webSocket, WebSocketState newState)
Sergunb 0:8918a71cdbe9 87 {
Sergunb 0:8918a71cdbe9 88 //Switch to the new state
Sergunb 0:8918a71cdbe9 89 webSocket->state = newState;
Sergunb 0:8918a71cdbe9 90 //Save current time;
Sergunb 0:8918a71cdbe9 91 webSocket->timestamp = osGetSystemTime();
Sergunb 0:8918a71cdbe9 92
Sergunb 0:8918a71cdbe9 93 //Reset sub-state
Sergunb 0:8918a71cdbe9 94 webSocket->txContext.state = WS_SUB_STATE_INIT;
Sergunb 0:8918a71cdbe9 95 webSocket->rxContext.state = WS_SUB_STATE_INIT;
Sergunb 0:8918a71cdbe9 96 }
Sergunb 0:8918a71cdbe9 97
Sergunb 0:8918a71cdbe9 98
Sergunb 0:8918a71cdbe9 99 /**
Sergunb 0:8918a71cdbe9 100 * @brief Parse client or server handshake
Sergunb 0:8918a71cdbe9 101 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 102 * @return Error code
Sergunb 0:8918a71cdbe9 103 **/
Sergunb 0:8918a71cdbe9 104
Sergunb 0:8918a71cdbe9 105 error_t webSocketParseHandshake(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 106 {
Sergunb 0:8918a71cdbe9 107 error_t error;
Sergunb 0:8918a71cdbe9 108 size_t n;
Sergunb 0:8918a71cdbe9 109 WebSocketFrameContext *rxContext;
Sergunb 0:8918a71cdbe9 110
Sergunb 0:8918a71cdbe9 111 //Point to the RX context
Sergunb 0:8918a71cdbe9 112 rxContext = &webSocket->rxContext;
Sergunb 0:8918a71cdbe9 113
Sergunb 0:8918a71cdbe9 114 //Initialize status code
Sergunb 0:8918a71cdbe9 115 error = NO_ERROR;
Sergunb 0:8918a71cdbe9 116
Sergunb 0:8918a71cdbe9 117 //Wait for the handshake to complete
Sergunb 0:8918a71cdbe9 118 while(1)
Sergunb 0:8918a71cdbe9 119 {
Sergunb 0:8918a71cdbe9 120 //Client or server operation?
Sergunb 0:8918a71cdbe9 121 if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
Sergunb 0:8918a71cdbe9 122 {
Sergunb 0:8918a71cdbe9 123 if(webSocket->state == WS_STATE_OPEN)
Sergunb 0:8918a71cdbe9 124 break;
Sergunb 0:8918a71cdbe9 125 }
Sergunb 0:8918a71cdbe9 126 else
Sergunb 0:8918a71cdbe9 127 {
Sergunb 0:8918a71cdbe9 128 if(webSocket->state == WS_STATE_SERVER_HANDSHAKE)
Sergunb 0:8918a71cdbe9 129 break;
Sergunb 0:8918a71cdbe9 130 }
Sergunb 0:8918a71cdbe9 131
Sergunb 0:8918a71cdbe9 132 //Check current sub-state
Sergunb 0:8918a71cdbe9 133 if(rxContext->state == WS_SUB_STATE_INIT)
Sergunb 0:8918a71cdbe9 134 {
Sergunb 0:8918a71cdbe9 135 //Initialize status code
Sergunb 0:8918a71cdbe9 136 webSocket->statusCode = WS_STATUS_CODE_NO_STATUS_RCVD;
Sergunb 0:8918a71cdbe9 137
Sergunb 0:8918a71cdbe9 138 //Initialize FIN flag
Sergunb 0:8918a71cdbe9 139 rxContext->fin = TRUE;
Sergunb 0:8918a71cdbe9 140
Sergunb 0:8918a71cdbe9 141 //Flush the receive buffer
Sergunb 0:8918a71cdbe9 142 rxContext->bufferPos = 0;
Sergunb 0:8918a71cdbe9 143 rxContext->bufferLen = 0;
Sergunb 0:8918a71cdbe9 144
Sergunb 0:8918a71cdbe9 145 //Initialize variables
Sergunb 0:8918a71cdbe9 146 webSocket->handshakeContext.statusCode = 0;
Sergunb 0:8918a71cdbe9 147 webSocket->handshakeContext.upgradeWebSocket = FALSE;
Sergunb 0:8918a71cdbe9 148 webSocket->handshakeContext.connectionUpgrade = FALSE;
Sergunb 0:8918a71cdbe9 149 webSocket->handshakeContext.connectionClose = FALSE;
Sergunb 0:8918a71cdbe9 150 webSocket->handshakeContext.contentLength = 0;
Sergunb 0:8918a71cdbe9 151 webSocket->handshakeContext.closingFrameSent = FALSE;
Sergunb 0:8918a71cdbe9 152 webSocket->handshakeContext.closingFrameReceived = FALSE;
Sergunb 0:8918a71cdbe9 153
Sergunb 0:8918a71cdbe9 154 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
Sergunb 0:8918a71cdbe9 155 webSocket->authContext.requiredAuthMode = WS_AUTH_MODE_NONE;
Sergunb 0:8918a71cdbe9 156 #endif
Sergunb 0:8918a71cdbe9 157
Sergunb 0:8918a71cdbe9 158 #if (WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
Sergunb 0:8918a71cdbe9 159 strcpy(webSocket->authContext.nonce, "");
Sergunb 0:8918a71cdbe9 160 strcpy(webSocket->authContext.opaque, "");
Sergunb 0:8918a71cdbe9 161 webSocket->authContext.stale = FALSE;
Sergunb 0:8918a71cdbe9 162 #endif
Sergunb 0:8918a71cdbe9 163
Sergunb 0:8918a71cdbe9 164 //Client or server operation?
Sergunb 0:8918a71cdbe9 165 if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
Sergunb 0:8918a71cdbe9 166 {
Sergunb 0:8918a71cdbe9 167 //Clear server key
Sergunb 0:8918a71cdbe9 168 strcpy(webSocket->handshakeContext.serverKey, "");
Sergunb 0:8918a71cdbe9 169
Sergunb 0:8918a71cdbe9 170 //Debug message
Sergunb 0:8918a71cdbe9 171 TRACE_DEBUG("WebSocket: server handshake\r\n");
Sergunb 0:8918a71cdbe9 172 }
Sergunb 0:8918a71cdbe9 173 else
Sergunb 0:8918a71cdbe9 174 {
Sergunb 0:8918a71cdbe9 175 //Clear client key
Sergunb 0:8918a71cdbe9 176 strcpy(webSocket->handshakeContext.clientKey, "");
Sergunb 0:8918a71cdbe9 177
Sergunb 0:8918a71cdbe9 178 //Debug message
Sergunb 0:8918a71cdbe9 179 TRACE_DEBUG("WebSocket: client handshake\r\n");
Sergunb 0:8918a71cdbe9 180 }
Sergunb 0:8918a71cdbe9 181
Sergunb 0:8918a71cdbe9 182 //Decode the leading line
Sergunb 0:8918a71cdbe9 183 rxContext->state = WS_SUB_STATE_HANDSHAKE_LEADING_LINE;
Sergunb 0:8918a71cdbe9 184 }
Sergunb 0:8918a71cdbe9 185 else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LEADING_LINE)
Sergunb 0:8918a71cdbe9 186 {
Sergunb 0:8918a71cdbe9 187 //Check whether more data is required
Sergunb 0:8918a71cdbe9 188 if(rxContext->bufferLen < 2)
Sergunb 0:8918a71cdbe9 189 {
Sergunb 0:8918a71cdbe9 190 //Limit the number of characters to read at a time
Sergunb 0:8918a71cdbe9 191 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
Sergunb 0:8918a71cdbe9 192
Sergunb 0:8918a71cdbe9 193 //Read data until a CLRF character is encountered
Sergunb 0:8918a71cdbe9 194 error = webSocketReceiveData(webSocket, rxContext->buffer +
Sergunb 0:8918a71cdbe9 195 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
Sergunb 0:8918a71cdbe9 196
Sergunb 0:8918a71cdbe9 197 //Update the length of the buffer
Sergunb 0:8918a71cdbe9 198 rxContext->bufferLen += n;
Sergunb 0:8918a71cdbe9 199 }
Sergunb 0:8918a71cdbe9 200 else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1))
Sergunb 0:8918a71cdbe9 201 {
Sergunb 0:8918a71cdbe9 202 //Report an error
Sergunb 0:8918a71cdbe9 203 error = ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 204 }
Sergunb 0:8918a71cdbe9 205 else if(strncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2, "\r\n", 2))
Sergunb 0:8918a71cdbe9 206 {
Sergunb 0:8918a71cdbe9 207 //Limit the number of characters to read at a time
Sergunb 0:8918a71cdbe9 208 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
Sergunb 0:8918a71cdbe9 209
Sergunb 0:8918a71cdbe9 210 //Read data until a CLRF character is encountered
Sergunb 0:8918a71cdbe9 211 error = webSocketReceiveData(webSocket, rxContext->buffer +
Sergunb 0:8918a71cdbe9 212 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
Sergunb 0:8918a71cdbe9 213
Sergunb 0:8918a71cdbe9 214 //Update the length of the buffer
Sergunb 0:8918a71cdbe9 215 rxContext->bufferLen += n;
Sergunb 0:8918a71cdbe9 216 }
Sergunb 0:8918a71cdbe9 217 else
Sergunb 0:8918a71cdbe9 218 {
Sergunb 0:8918a71cdbe9 219 //Properly terminate the string with a NULL character
Sergunb 0:8918a71cdbe9 220 rxContext->buffer[rxContext->bufferLen] = '\0';
Sergunb 0:8918a71cdbe9 221
Sergunb 0:8918a71cdbe9 222 //Client or server operation?
Sergunb 0:8918a71cdbe9 223 if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
Sergunb 0:8918a71cdbe9 224 {
Sergunb 0:8918a71cdbe9 225 //The leading line from the server follows the Status-Line format
Sergunb 0:8918a71cdbe9 226 error = webSocketParseStatusLine(webSocket, (char_t *) rxContext->buffer);
Sergunb 0:8918a71cdbe9 227 }
Sergunb 0:8918a71cdbe9 228 else
Sergunb 0:8918a71cdbe9 229 {
Sergunb 0:8918a71cdbe9 230 //The leading line from the client follows the Request-Line format
Sergunb 0:8918a71cdbe9 231 error = webSocketParseRequestLine(webSocket, (char_t *) rxContext->buffer);
Sergunb 0:8918a71cdbe9 232 }
Sergunb 0:8918a71cdbe9 233
Sergunb 0:8918a71cdbe9 234 //Flush the receive buffer
Sergunb 0:8918a71cdbe9 235 rxContext->bufferPos = 0;
Sergunb 0:8918a71cdbe9 236 rxContext->bufferLen = 0;
Sergunb 0:8918a71cdbe9 237
Sergunb 0:8918a71cdbe9 238 //Parse the header fields of the handshake
Sergunb 0:8918a71cdbe9 239 rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD;
Sergunb 0:8918a71cdbe9 240 }
Sergunb 0:8918a71cdbe9 241 }
Sergunb 0:8918a71cdbe9 242 else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_HEADER_FIELD)
Sergunb 0:8918a71cdbe9 243 {
Sergunb 0:8918a71cdbe9 244 //Check whether more data is required
Sergunb 0:8918a71cdbe9 245 if(rxContext->bufferLen < 2)
Sergunb 0:8918a71cdbe9 246 {
Sergunb 0:8918a71cdbe9 247 //Limit the number of characters to read at a time
Sergunb 0:8918a71cdbe9 248 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
Sergunb 0:8918a71cdbe9 249
Sergunb 0:8918a71cdbe9 250 //Read data until a CLRF character is encountered
Sergunb 0:8918a71cdbe9 251 error = webSocketReceiveData(webSocket, rxContext->buffer +
Sergunb 0:8918a71cdbe9 252 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
Sergunb 0:8918a71cdbe9 253
Sergunb 0:8918a71cdbe9 254 //Update the length of the buffer
Sergunb 0:8918a71cdbe9 255 rxContext->bufferLen += n;
Sergunb 0:8918a71cdbe9 256 }
Sergunb 0:8918a71cdbe9 257 else if(rxContext->bufferLen >= (WEB_SOCKET_BUFFER_SIZE - 1))
Sergunb 0:8918a71cdbe9 258 {
Sergunb 0:8918a71cdbe9 259 //Report an error
Sergunb 0:8918a71cdbe9 260 error = ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 261 }
Sergunb 0:8918a71cdbe9 262 else if(strncmp((char_t *) rxContext->buffer + rxContext->bufferLen - 2, "\r\n", 2))
Sergunb 0:8918a71cdbe9 263 {
Sergunb 0:8918a71cdbe9 264 //Limit the number of characters to read at a time
Sergunb 0:8918a71cdbe9 265 n = WEB_SOCKET_BUFFER_SIZE - 1 - rxContext->bufferLen;
Sergunb 0:8918a71cdbe9 266
Sergunb 0:8918a71cdbe9 267 //Read data until a CLRF character is encountered
Sergunb 0:8918a71cdbe9 268 error = webSocketReceiveData(webSocket, rxContext->buffer +
Sergunb 0:8918a71cdbe9 269 rxContext->bufferLen, n, &n, SOCKET_FLAG_BREAK_CRLF);
Sergunb 0:8918a71cdbe9 270
Sergunb 0:8918a71cdbe9 271 //Update the length of the buffer
Sergunb 0:8918a71cdbe9 272 rxContext->bufferLen += n;
Sergunb 0:8918a71cdbe9 273 }
Sergunb 0:8918a71cdbe9 274 else
Sergunb 0:8918a71cdbe9 275 {
Sergunb 0:8918a71cdbe9 276 //Properly terminate the string with a NULL character
Sergunb 0:8918a71cdbe9 277 rxContext->buffer[rxContext->bufferLen] = '\0';
Sergunb 0:8918a71cdbe9 278
Sergunb 0:8918a71cdbe9 279 //An empty line indicates the end of the header fields
Sergunb 0:8918a71cdbe9 280 if(!strcmp((char_t *) rxContext->buffer, "\r\n"))
Sergunb 0:8918a71cdbe9 281 {
Sergunb 0:8918a71cdbe9 282 //Client or server operation?
Sergunb 0:8918a71cdbe9 283 if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
Sergunb 0:8918a71cdbe9 284 {
Sergunb 0:8918a71cdbe9 285 //Verify server's handshake
Sergunb 0:8918a71cdbe9 286 error = webSocketVerifyServerHandshake(webSocket);
Sergunb 0:8918a71cdbe9 287 }
Sergunb 0:8918a71cdbe9 288 else
Sergunb 0:8918a71cdbe9 289 {
Sergunb 0:8918a71cdbe9 290 //Verify client's handshake
Sergunb 0:8918a71cdbe9 291 error = webSocketVerifyClientHandshake(webSocket);
Sergunb 0:8918a71cdbe9 292 }
Sergunb 0:8918a71cdbe9 293 }
Sergunb 0:8918a71cdbe9 294 else
Sergunb 0:8918a71cdbe9 295 {
Sergunb 0:8918a71cdbe9 296 //Read the next character to detect if the CRLF is immediately
Sergunb 0:8918a71cdbe9 297 //followed by a LWSP character
Sergunb 0:8918a71cdbe9 298 rxContext->state = WS_SUB_STATE_HANDSHAKE_LWSP;
Sergunb 0:8918a71cdbe9 299 }
Sergunb 0:8918a71cdbe9 300 }
Sergunb 0:8918a71cdbe9 301 }
Sergunb 0:8918a71cdbe9 302 else if(rxContext->state == WS_SUB_STATE_HANDSHAKE_LWSP)
Sergunb 0:8918a71cdbe9 303 {
Sergunb 0:8918a71cdbe9 304 char_t nextChar;
Sergunb 0:8918a71cdbe9 305
Sergunb 0:8918a71cdbe9 306 //Read the next character
Sergunb 0:8918a71cdbe9 307 error = webSocketReceiveData(webSocket, &nextChar, sizeof(char_t), &n, 0);
Sergunb 0:8918a71cdbe9 308
Sergunb 0:8918a71cdbe9 309 //Successful read operation?
Sergunb 0:8918a71cdbe9 310 if(!error && n == sizeof(char_t))
Sergunb 0:8918a71cdbe9 311 {
Sergunb 0:8918a71cdbe9 312 //LWSP character found?
Sergunb 0:8918a71cdbe9 313 if(nextChar == ' ' || nextChar == '\t')
Sergunb 0:8918a71cdbe9 314 {
Sergunb 0:8918a71cdbe9 315 //Unfolding is accomplished by regarding CRLF immediately
Sergunb 0:8918a71cdbe9 316 //followed by a LWSP as equivalent to the LWSP character
Sergunb 0:8918a71cdbe9 317 if(rxContext->bufferLen >= 2)
Sergunb 0:8918a71cdbe9 318 {
Sergunb 0:8918a71cdbe9 319 //Remove trailing CRLF sequence
Sergunb 0:8918a71cdbe9 320 rxContext->bufferLen -= 2;
Sergunb 0:8918a71cdbe9 321 }
Sergunb 0:8918a71cdbe9 322
Sergunb 0:8918a71cdbe9 323 //The header field spans multiple line
Sergunb 0:8918a71cdbe9 324 rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD;
Sergunb 0:8918a71cdbe9 325 }
Sergunb 0:8918a71cdbe9 326 else
Sergunb 0:8918a71cdbe9 327 {
Sergunb 0:8918a71cdbe9 328 //Parse header field
Sergunb 0:8918a71cdbe9 329 error = webSocketParseHeaderField(webSocket, (char_t *) rxContext->buffer);
Sergunb 0:8918a71cdbe9 330
Sergunb 0:8918a71cdbe9 331 //Restore the very first character of the header field
Sergunb 0:8918a71cdbe9 332 rxContext->buffer[0] = nextChar;
Sergunb 0:8918a71cdbe9 333 //Adjust the length of the receive buffer
Sergunb 0:8918a71cdbe9 334 rxContext->bufferLen = sizeof(char_t);
Sergunb 0:8918a71cdbe9 335
Sergunb 0:8918a71cdbe9 336 //Decode the next header field
Sergunb 0:8918a71cdbe9 337 rxContext->state = WS_SUB_STATE_HANDSHAKE_HEADER_FIELD;
Sergunb 0:8918a71cdbe9 338 }
Sergunb 0:8918a71cdbe9 339 }
Sergunb 0:8918a71cdbe9 340 }
Sergunb 0:8918a71cdbe9 341 else
Sergunb 0:8918a71cdbe9 342 {
Sergunb 0:8918a71cdbe9 343 //Invalid state
Sergunb 0:8918a71cdbe9 344 error = ERROR_WRONG_STATE;
Sergunb 0:8918a71cdbe9 345 }
Sergunb 0:8918a71cdbe9 346
Sergunb 0:8918a71cdbe9 347 //Any error to report?
Sergunb 0:8918a71cdbe9 348 if(error)
Sergunb 0:8918a71cdbe9 349 break;
Sergunb 0:8918a71cdbe9 350 }
Sergunb 0:8918a71cdbe9 351
Sergunb 0:8918a71cdbe9 352 //Return status code
Sergunb 0:8918a71cdbe9 353 return error;
Sergunb 0:8918a71cdbe9 354 }
Sergunb 0:8918a71cdbe9 355
Sergunb 0:8918a71cdbe9 356
Sergunb 0:8918a71cdbe9 357 /**
Sergunb 0:8918a71cdbe9 358 * @brief Parse the Request-Line of the client's handshake
Sergunb 0:8918a71cdbe9 359 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 360 * @param[in] line NULL-terminated string that contains the Request-Line
Sergunb 0:8918a71cdbe9 361 * @return Error code
Sergunb 0:8918a71cdbe9 362 **/
Sergunb 0:8918a71cdbe9 363
Sergunb 0:8918a71cdbe9 364 error_t webSocketParseRequestLine(WebSocket *webSocket, char_t *line)
Sergunb 0:8918a71cdbe9 365 {
Sergunb 0:8918a71cdbe9 366 error_t error;
Sergunb 0:8918a71cdbe9 367 char_t *token;
Sergunb 0:8918a71cdbe9 368 char_t *p;
Sergunb 0:8918a71cdbe9 369 char_t *s;
Sergunb 0:8918a71cdbe9 370
Sergunb 0:8918a71cdbe9 371 //Debug message
Sergunb 0:8918a71cdbe9 372 TRACE_DEBUG("%s", line);
Sergunb 0:8918a71cdbe9 373
Sergunb 0:8918a71cdbe9 374 //The Request-Line begins with a method token
Sergunb 0:8918a71cdbe9 375 token = strtok_r(line, " \r\n", &p);
Sergunb 0:8918a71cdbe9 376 //Unable to retrieve the method?
Sergunb 0:8918a71cdbe9 377 if(token == NULL)
Sergunb 0:8918a71cdbe9 378 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 379
Sergunb 0:8918a71cdbe9 380 //The method of the request must be GET
Sergunb 0:8918a71cdbe9 381 if(strcasecmp(token, "GET"))
Sergunb 0:8918a71cdbe9 382 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 383
Sergunb 0:8918a71cdbe9 384 //The Request-URI is following the method token
Sergunb 0:8918a71cdbe9 385 token = strtok_r(NULL, " \r\n", &p);
Sergunb 0:8918a71cdbe9 386 //Unable to retrieve the Request-URI?
Sergunb 0:8918a71cdbe9 387 if(token == NULL)
Sergunb 0:8918a71cdbe9 388 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 389
Sergunb 0:8918a71cdbe9 390 //Check whether a query string is present
Sergunb 0:8918a71cdbe9 391 s = strchr(token, '?');
Sergunb 0:8918a71cdbe9 392
Sergunb 0:8918a71cdbe9 393 //Query string found?
Sergunb 0:8918a71cdbe9 394 if(s != NULL)
Sergunb 0:8918a71cdbe9 395 {
Sergunb 0:8918a71cdbe9 396 //Split the string
Sergunb 0:8918a71cdbe9 397 *s = '\0';
Sergunb 0:8918a71cdbe9 398
Sergunb 0:8918a71cdbe9 399 //Save the Request-URI
Sergunb 0:8918a71cdbe9 400 error = webSocketDecodePercentEncodedString(token,
Sergunb 0:8918a71cdbe9 401 webSocket->uri, WEB_SOCKET_URI_MAX_LEN);
Sergunb 0:8918a71cdbe9 402 //Any error to report?
Sergunb 0:8918a71cdbe9 403 if(error)
Sergunb 0:8918a71cdbe9 404 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 405
Sergunb 0:8918a71cdbe9 406 //Check the length of the query string
Sergunb 0:8918a71cdbe9 407 if(strlen(s + 1) > WEB_SOCKET_QUERY_STRING_MAX_LEN)
Sergunb 0:8918a71cdbe9 408 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 409
Sergunb 0:8918a71cdbe9 410 //Save the query string
Sergunb 0:8918a71cdbe9 411 strcpy(webSocket->queryString, s + 1);
Sergunb 0:8918a71cdbe9 412 }
Sergunb 0:8918a71cdbe9 413 else
Sergunb 0:8918a71cdbe9 414 {
Sergunb 0:8918a71cdbe9 415 //Save the Request-URI
Sergunb 0:8918a71cdbe9 416 error = webSocketDecodePercentEncodedString(token,
Sergunb 0:8918a71cdbe9 417 webSocket->uri, WEB_SOCKET_URI_MAX_LEN);
Sergunb 0:8918a71cdbe9 418 //Any error to report?
Sergunb 0:8918a71cdbe9 419 if(error)
Sergunb 0:8918a71cdbe9 420 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 421
Sergunb 0:8918a71cdbe9 422 //No query string
Sergunb 0:8918a71cdbe9 423 webSocket->queryString[0] = '\0';
Sergunb 0:8918a71cdbe9 424 }
Sergunb 0:8918a71cdbe9 425
Sergunb 0:8918a71cdbe9 426 //The protocol version is following the Request-URI
Sergunb 0:8918a71cdbe9 427 token = strtok_r(NULL, " \r\n", &p);
Sergunb 0:8918a71cdbe9 428
Sergunb 0:8918a71cdbe9 429 //HTTP version 0.9?
Sergunb 0:8918a71cdbe9 430 if(token == NULL)
Sergunb 0:8918a71cdbe9 431 {
Sergunb 0:8918a71cdbe9 432 //Save version number
Sergunb 0:8918a71cdbe9 433 webSocket->handshakeContext.version = WS_HTTP_VERSION_0_9;
Sergunb 0:8918a71cdbe9 434 //Persistent connections are not supported
Sergunb 0:8918a71cdbe9 435 webSocket->handshakeContext.connectionClose = TRUE;
Sergunb 0:8918a71cdbe9 436 }
Sergunb 0:8918a71cdbe9 437 //HTTP version 1.0?
Sergunb 0:8918a71cdbe9 438 else if(!strcasecmp(token, "HTTP/1.0"))
Sergunb 0:8918a71cdbe9 439 {
Sergunb 0:8918a71cdbe9 440 //Save version number
Sergunb 0:8918a71cdbe9 441 webSocket->handshakeContext.version = WS_HTTP_VERSION_1_0;
Sergunb 0:8918a71cdbe9 442 //By default connections are not persistent
Sergunb 0:8918a71cdbe9 443 webSocket->handshakeContext.connectionClose = TRUE;
Sergunb 0:8918a71cdbe9 444 }
Sergunb 0:8918a71cdbe9 445 //HTTP version 1.1?
Sergunb 0:8918a71cdbe9 446 else if(!strcasecmp(token, "HTTP/1.1"))
Sergunb 0:8918a71cdbe9 447 {
Sergunb 0:8918a71cdbe9 448 //Save version number
Sergunb 0:8918a71cdbe9 449 webSocket->handshakeContext.version = WS_HTTP_VERSION_1_1;
Sergunb 0:8918a71cdbe9 450 //HTTP 1.1 makes persistent connections the default
Sergunb 0:8918a71cdbe9 451 webSocket->handshakeContext.connectionClose = FALSE;
Sergunb 0:8918a71cdbe9 452 }
Sergunb 0:8918a71cdbe9 453 //HTTP version not supported?
Sergunb 0:8918a71cdbe9 454 else
Sergunb 0:8918a71cdbe9 455 {
Sergunb 0:8918a71cdbe9 456 //Report an error
Sergunb 0:8918a71cdbe9 457 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 458 }
Sergunb 0:8918a71cdbe9 459
Sergunb 0:8918a71cdbe9 460 //Successful processing
Sergunb 0:8918a71cdbe9 461 return NO_ERROR;
Sergunb 0:8918a71cdbe9 462 }
Sergunb 0:8918a71cdbe9 463
Sergunb 0:8918a71cdbe9 464
Sergunb 0:8918a71cdbe9 465 /**
Sergunb 0:8918a71cdbe9 466 * @brief Parse the Status-Line of the server's handshake
Sergunb 0:8918a71cdbe9 467 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 468 * @param[in] line NULL-terminated string that contains the Status-Line
Sergunb 0:8918a71cdbe9 469 * @return Error code
Sergunb 0:8918a71cdbe9 470 **/
Sergunb 0:8918a71cdbe9 471
Sergunb 0:8918a71cdbe9 472 error_t webSocketParseStatusLine(WebSocket *webSocket, char_t *line)
Sergunb 0:8918a71cdbe9 473 {
Sergunb 0:8918a71cdbe9 474 char_t *p;
Sergunb 0:8918a71cdbe9 475 char_t *token;
Sergunb 0:8918a71cdbe9 476
Sergunb 0:8918a71cdbe9 477 //Debug message
Sergunb 0:8918a71cdbe9 478 TRACE_DEBUG("%s", line);
Sergunb 0:8918a71cdbe9 479
Sergunb 0:8918a71cdbe9 480 //Retrieve the HTTP-Version field
Sergunb 0:8918a71cdbe9 481 token = strtok_r(line, " ", &p);
Sergunb 0:8918a71cdbe9 482 //Any parsing error?
Sergunb 0:8918a71cdbe9 483 if(token == NULL)
Sergunb 0:8918a71cdbe9 484 return ERROR_INVALID_SYNTAX;
Sergunb 0:8918a71cdbe9 485
Sergunb 0:8918a71cdbe9 486 //Retrieve the Status-Code field
Sergunb 0:8918a71cdbe9 487 token = strtok_r(NULL, " ", &p);
Sergunb 0:8918a71cdbe9 488 //Any parsing error?
Sergunb 0:8918a71cdbe9 489 if(token == NULL)
Sergunb 0:8918a71cdbe9 490 return ERROR_INVALID_SYNTAX;
Sergunb 0:8918a71cdbe9 491
Sergunb 0:8918a71cdbe9 492 //Convert the status code
Sergunb 0:8918a71cdbe9 493 webSocket->handshakeContext.statusCode = strtoul(token, &p, 10);
Sergunb 0:8918a71cdbe9 494 //Any parsing error?
Sergunb 0:8918a71cdbe9 495 if(*p != '\0')
Sergunb 0:8918a71cdbe9 496 return ERROR_INVALID_SYNTAX;
Sergunb 0:8918a71cdbe9 497
Sergunb 0:8918a71cdbe9 498 //Successful processing
Sergunb 0:8918a71cdbe9 499 return NO_ERROR;
Sergunb 0:8918a71cdbe9 500 }
Sergunb 0:8918a71cdbe9 501
Sergunb 0:8918a71cdbe9 502
Sergunb 0:8918a71cdbe9 503 /**
Sergunb 0:8918a71cdbe9 504 * @brief Parse a header field
Sergunb 0:8918a71cdbe9 505 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 506 * @param[in] line NULL-terminated string that contains the header field
Sergunb 0:8918a71cdbe9 507 * @return Error code
Sergunb 0:8918a71cdbe9 508 **/
Sergunb 0:8918a71cdbe9 509
Sergunb 0:8918a71cdbe9 510 error_t webSocketParseHeaderField(WebSocket *webSocket, char_t *line)
Sergunb 0:8918a71cdbe9 511 {
Sergunb 0:8918a71cdbe9 512 char_t *separator;
Sergunb 0:8918a71cdbe9 513 char_t *name;
Sergunb 0:8918a71cdbe9 514 char_t *value;
Sergunb 0:8918a71cdbe9 515 WebSocketHandshakeContext *handshakeContext;
Sergunb 0:8918a71cdbe9 516
Sergunb 0:8918a71cdbe9 517 //Point to the handshake context
Sergunb 0:8918a71cdbe9 518 handshakeContext = &webSocket->handshakeContext;
Sergunb 0:8918a71cdbe9 519
Sergunb 0:8918a71cdbe9 520 //Debug message
Sergunb 0:8918a71cdbe9 521 TRACE_DEBUG("%s", line);
Sergunb 0:8918a71cdbe9 522
Sergunb 0:8918a71cdbe9 523 //Check whether a separator is present
Sergunb 0:8918a71cdbe9 524 separator = strchr(line, ':');
Sergunb 0:8918a71cdbe9 525
Sergunb 0:8918a71cdbe9 526 //Separator found?
Sergunb 0:8918a71cdbe9 527 if(separator != NULL)
Sergunb 0:8918a71cdbe9 528 {
Sergunb 0:8918a71cdbe9 529 //Split the line
Sergunb 0:8918a71cdbe9 530 *separator = '\0';
Sergunb 0:8918a71cdbe9 531
Sergunb 0:8918a71cdbe9 532 //Get field name and value
Sergunb 0:8918a71cdbe9 533 name = strTrimWhitespace(line);
Sergunb 0:8918a71cdbe9 534 value = strTrimWhitespace(separator + 1);
Sergunb 0:8918a71cdbe9 535
Sergunb 0:8918a71cdbe9 536 //Upgrade header field found?
Sergunb 0:8918a71cdbe9 537 if(!strcasecmp(name, "Upgrade"))
Sergunb 0:8918a71cdbe9 538 {
Sergunb 0:8918a71cdbe9 539 if(!strcasecmp(value, "websocket"))
Sergunb 0:8918a71cdbe9 540 handshakeContext->upgradeWebSocket = TRUE;
Sergunb 0:8918a71cdbe9 541
Sergunb 0:8918a71cdbe9 542 }
Sergunb 0:8918a71cdbe9 543 //Connection header field found?
Sergunb 0:8918a71cdbe9 544 else if(!strcasecmp(name, "Connection"))
Sergunb 0:8918a71cdbe9 545 {
Sergunb 0:8918a71cdbe9 546 //Parse Connection header field
Sergunb 0:8918a71cdbe9 547 webSocketParseConnectionField(webSocket, value);
Sergunb 0:8918a71cdbe9 548 }
Sergunb 0:8918a71cdbe9 549 //Sec-WebSocket-Key header field found?
Sergunb 0:8918a71cdbe9 550 else if(!strcasecmp(name, "Sec-WebSocket-Key"))
Sergunb 0:8918a71cdbe9 551 {
Sergunb 0:8918a71cdbe9 552 //Server operation?
Sergunb 0:8918a71cdbe9 553 if(webSocket->endpoint == WS_ENDPOINT_SERVER)
Sergunb 0:8918a71cdbe9 554 {
Sergunb 0:8918a71cdbe9 555 //Save the contents of the Sec-WebSocket-Key header field
Sergunb 0:8918a71cdbe9 556 strSafeCopy(handshakeContext->clientKey, value,
Sergunb 0:8918a71cdbe9 557 WEB_SOCKET_CLIENT_KEY_SIZE + 1);
Sergunb 0:8918a71cdbe9 558 }
Sergunb 0:8918a71cdbe9 559 }
Sergunb 0:8918a71cdbe9 560 //Sec-WebSocket-Accept header field found?
Sergunb 0:8918a71cdbe9 561 else if(!strcasecmp(name, "Sec-WebSocket-Accept"))
Sergunb 0:8918a71cdbe9 562 {
Sergunb 0:8918a71cdbe9 563 //Client operation?
Sergunb 0:8918a71cdbe9 564 if(webSocket->endpoint == WS_ENDPOINT_CLIENT)
Sergunb 0:8918a71cdbe9 565 {
Sergunb 0:8918a71cdbe9 566 //Save the contents of the Sec-WebSocket-Accept header field
Sergunb 0:8918a71cdbe9 567 strSafeCopy(handshakeContext->serverKey, value,
Sergunb 0:8918a71cdbe9 568 WEB_SOCKET_SERVER_KEY_SIZE + 1);
Sergunb 0:8918a71cdbe9 569 }
Sergunb 0:8918a71cdbe9 570 }
Sergunb 0:8918a71cdbe9 571 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
Sergunb 0:8918a71cdbe9 572 //WWW-Authenticate header field found?
Sergunb 0:8918a71cdbe9 573 else if(!strcasecmp(name, "WWW-Authenticate"))
Sergunb 0:8918a71cdbe9 574 {
Sergunb 0:8918a71cdbe9 575 //Parse WWW-Authenticate header field
Sergunb 0:8918a71cdbe9 576 webSocketParseAuthenticateField(webSocket, value);
Sergunb 0:8918a71cdbe9 577 }
Sergunb 0:8918a71cdbe9 578 #endif
Sergunb 0:8918a71cdbe9 579 //Content-Length header field found?
Sergunb 0:8918a71cdbe9 580 else if(!strcasecmp(name, "Content-Length"))
Sergunb 0:8918a71cdbe9 581 {
Sergunb 0:8918a71cdbe9 582 handshakeContext->contentLength = strtoul(value, NULL, 10);
Sergunb 0:8918a71cdbe9 583 }
Sergunb 0:8918a71cdbe9 584 }
Sergunb 0:8918a71cdbe9 585
Sergunb 0:8918a71cdbe9 586 //Successful processing
Sergunb 0:8918a71cdbe9 587 return NO_ERROR;
Sergunb 0:8918a71cdbe9 588 }
Sergunb 0:8918a71cdbe9 589
Sergunb 0:8918a71cdbe9 590
Sergunb 0:8918a71cdbe9 591 /**
Sergunb 0:8918a71cdbe9 592 * @brief Parse Connection header field
Sergunb 0:8918a71cdbe9 593 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 594 * @param[in] value NULL-terminated string that contains the value of header field
Sergunb 0:8918a71cdbe9 595 **/
Sergunb 0:8918a71cdbe9 596
Sergunb 0:8918a71cdbe9 597 void webSocketParseConnectionField(WebSocket *webSocket, char_t *value)
Sergunb 0:8918a71cdbe9 598 {
Sergunb 0:8918a71cdbe9 599 char_t *p;
Sergunb 0:8918a71cdbe9 600 char_t *token;
Sergunb 0:8918a71cdbe9 601
Sergunb 0:8918a71cdbe9 602 //Get the first value of the list
Sergunb 0:8918a71cdbe9 603 token = strtok_r(value, ",", &p);
Sergunb 0:8918a71cdbe9 604
Sergunb 0:8918a71cdbe9 605 //Parse the comma-separated list
Sergunb 0:8918a71cdbe9 606 while(token != NULL)
Sergunb 0:8918a71cdbe9 607 {
Sergunb 0:8918a71cdbe9 608 //Trim whitespace characters
Sergunb 0:8918a71cdbe9 609 value = strTrimWhitespace(token);
Sergunb 0:8918a71cdbe9 610
Sergunb 0:8918a71cdbe9 611 //Check current value
Sergunb 0:8918a71cdbe9 612 if(!strcasecmp(value, "keep-alive"))
Sergunb 0:8918a71cdbe9 613 {
Sergunb 0:8918a71cdbe9 614 //The connection is persistent
Sergunb 0:8918a71cdbe9 615 webSocket->handshakeContext.connectionClose = FALSE;
Sergunb 0:8918a71cdbe9 616 }
Sergunb 0:8918a71cdbe9 617 else if(!strcasecmp(value, "close"))
Sergunb 0:8918a71cdbe9 618 {
Sergunb 0:8918a71cdbe9 619 //The connection will be closed after completion of the response
Sergunb 0:8918a71cdbe9 620 webSocket->handshakeContext.connectionClose = TRUE;
Sergunb 0:8918a71cdbe9 621 }
Sergunb 0:8918a71cdbe9 622 else if(!strcasecmp(value, "upgrade"))
Sergunb 0:8918a71cdbe9 623 {
Sergunb 0:8918a71cdbe9 624 //Upgrade the connection
Sergunb 0:8918a71cdbe9 625 webSocket->handshakeContext.connectionUpgrade = TRUE;
Sergunb 0:8918a71cdbe9 626 }
Sergunb 0:8918a71cdbe9 627
Sergunb 0:8918a71cdbe9 628 //Get next value
Sergunb 0:8918a71cdbe9 629 token = strtok_r(NULL, ",", &p);
Sergunb 0:8918a71cdbe9 630 }
Sergunb 0:8918a71cdbe9 631 }
Sergunb 0:8918a71cdbe9 632
Sergunb 0:8918a71cdbe9 633
Sergunb 0:8918a71cdbe9 634 /**
Sergunb 0:8918a71cdbe9 635 * @brief Format client's handshake
Sergunb 0:8918a71cdbe9 636 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 637 * @param[in] serverPort TCP port number used to establish the connection
Sergunb 0:8918a71cdbe9 638 * @return Error code
Sergunb 0:8918a71cdbe9 639 **/
Sergunb 0:8918a71cdbe9 640
Sergunb 0:8918a71cdbe9 641 error_t webSocketFormatClientHandshake(WebSocket *webSocket, uint16_t serverPort)
Sergunb 0:8918a71cdbe9 642 {
Sergunb 0:8918a71cdbe9 643 char_t *p;
Sergunb 0:8918a71cdbe9 644 WebSocketFrameContext *txContext;
Sergunb 0:8918a71cdbe9 645
Sergunb 0:8918a71cdbe9 646 //Point to the TX context
Sergunb 0:8918a71cdbe9 647 txContext = &webSocket->txContext;
Sergunb 0:8918a71cdbe9 648 //Point to the buffer where to format the client's handshake
Sergunb 0:8918a71cdbe9 649 p = (char_t *) txContext->buffer;
Sergunb 0:8918a71cdbe9 650
Sergunb 0:8918a71cdbe9 651 //The Request-Line begins with a method token, followed by the
Sergunb 0:8918a71cdbe9 652 //Request-URI and the protocol version, and ending with CRLF
Sergunb 0:8918a71cdbe9 653 p += sprintf(p, "GET %s HTTP/1.1\r\n", webSocket->uri);
Sergunb 0:8918a71cdbe9 654
Sergunb 0:8918a71cdbe9 655 //Add Host header field
Sergunb 0:8918a71cdbe9 656 if(webSocket->host[0] != '\0')
Sergunb 0:8918a71cdbe9 657 {
Sergunb 0:8918a71cdbe9 658 //The Host header field specifies the Internet host and port number of
Sergunb 0:8918a71cdbe9 659 //the resource being requested
Sergunb 0:8918a71cdbe9 660 p += sprintf(p, "Host: %s:%d\r\n", webSocket->host, serverPort);
Sergunb 0:8918a71cdbe9 661 }
Sergunb 0:8918a71cdbe9 662 else
Sergunb 0:8918a71cdbe9 663 {
Sergunb 0:8918a71cdbe9 664 //If the requested URI does not include a host name for the service being
Sergunb 0:8918a71cdbe9 665 //requested, then the Host header field must be given with an empty value
Sergunb 0:8918a71cdbe9 666 p += sprintf(p, "Host:\r\n");
Sergunb 0:8918a71cdbe9 667 }
Sergunb 0:8918a71cdbe9 668
Sergunb 0:8918a71cdbe9 669 #if (WEB_SOCKET_BASIC_AUTH_SUPPORT == ENABLED || WEB_SOCKET_DIGEST_AUTH_SUPPORT == ENABLED)
Sergunb 0:8918a71cdbe9 670 //Check whether authentication is required
Sergunb 0:8918a71cdbe9 671 if(webSocket->authContext.selectedAuthMode != WS_AUTH_MODE_NONE)
Sergunb 0:8918a71cdbe9 672 {
Sergunb 0:8918a71cdbe9 673 //Add Authorization header field
Sergunb 0:8918a71cdbe9 674 p += webSocketAddAuthorizationField(webSocket, p);
Sergunb 0:8918a71cdbe9 675 }
Sergunb 0:8918a71cdbe9 676 #endif
Sergunb 0:8918a71cdbe9 677
Sergunb 0:8918a71cdbe9 678 //Add Origin header field
Sergunb 0:8918a71cdbe9 679 if(webSocket->origin[0] != '\0')
Sergunb 0:8918a71cdbe9 680 p += sprintf(p, "Origin: %s\r\n", webSocket->origin);
Sergunb 0:8918a71cdbe9 681 else
Sergunb 0:8918a71cdbe9 682 p += sprintf(p, "Origin: null\r\n");
Sergunb 0:8918a71cdbe9 683
Sergunb 0:8918a71cdbe9 684 //Add Upgrade header field
Sergunb 0:8918a71cdbe9 685 p += sprintf(p, "Upgrade: websocket\r\n");
Sergunb 0:8918a71cdbe9 686 //Add Connection header field
Sergunb 0:8918a71cdbe9 687 p += sprintf(p, "Connection: Upgrade\r\n");
Sergunb 0:8918a71cdbe9 688
Sergunb 0:8918a71cdbe9 689 //Add Sec-WebSocket-Protocol header field
Sergunb 0:8918a71cdbe9 690 if(webSocket->subProtocol[0] != '\0')
Sergunb 0:8918a71cdbe9 691 p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
Sergunb 0:8918a71cdbe9 692
Sergunb 0:8918a71cdbe9 693 //Add Sec-WebSocket-Key header field
Sergunb 0:8918a71cdbe9 694 p += sprintf(p, "Sec-WebSocket-Key: %s\r\n",
Sergunb 0:8918a71cdbe9 695 webSocket->handshakeContext.clientKey);
Sergunb 0:8918a71cdbe9 696
Sergunb 0:8918a71cdbe9 697 //Add Sec-WebSocket-Version header field
Sergunb 0:8918a71cdbe9 698 p += sprintf(p, "Sec-WebSocket-Version: 13\r\n");
Sergunb 0:8918a71cdbe9 699 //An empty line indicates the end of the header fields
Sergunb 0:8918a71cdbe9 700 p += sprintf(p, "\r\n");
Sergunb 0:8918a71cdbe9 701
Sergunb 0:8918a71cdbe9 702 //Debug message
Sergunb 0:8918a71cdbe9 703 TRACE_DEBUG("\r\n");
Sergunb 0:8918a71cdbe9 704 TRACE_DEBUG("WebSocket: client handshake\r\n");
Sergunb 0:8918a71cdbe9 705 TRACE_DEBUG("%s", txContext->buffer);
Sergunb 0:8918a71cdbe9 706
Sergunb 0:8918a71cdbe9 707 //Rewind to the beginning of the buffer
Sergunb 0:8918a71cdbe9 708 txContext->bufferPos = 0;
Sergunb 0:8918a71cdbe9 709 //Update the number of data buffered but not yet sent
Sergunb 0:8918a71cdbe9 710 txContext->bufferLen = strlen((char_t *) txContext->buffer);
Sergunb 0:8918a71cdbe9 711
Sergunb 0:8918a71cdbe9 712 //Successful processing
Sergunb 0:8918a71cdbe9 713 return NO_ERROR;
Sergunb 0:8918a71cdbe9 714 }
Sergunb 0:8918a71cdbe9 715
Sergunb 0:8918a71cdbe9 716
Sergunb 0:8918a71cdbe9 717 /**
Sergunb 0:8918a71cdbe9 718 * @brief Format server's handshake
Sergunb 0:8918a71cdbe9 719 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 720 * @return Error code
Sergunb 0:8918a71cdbe9 721 **/
Sergunb 0:8918a71cdbe9 722
Sergunb 0:8918a71cdbe9 723 error_t webSocketFormatServerHandshake(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 724 {
Sergunb 0:8918a71cdbe9 725 char_t *p;
Sergunb 0:8918a71cdbe9 726 WebSocketFrameContext *txContext;
Sergunb 0:8918a71cdbe9 727
Sergunb 0:8918a71cdbe9 728 //Point to the TX context
Sergunb 0:8918a71cdbe9 729 txContext = &webSocket->txContext;
Sergunb 0:8918a71cdbe9 730 //Point to the buffer where to format the client's handshake
Sergunb 0:8918a71cdbe9 731 p = (char_t *) txContext->buffer;
Sergunb 0:8918a71cdbe9 732
Sergunb 0:8918a71cdbe9 733 //The first line is an HTTP Status-Line, with the status code 101
Sergunb 0:8918a71cdbe9 734 p += sprintf(p, "HTTP/1.1 101 Switching Protocols\r\n");
Sergunb 0:8918a71cdbe9 735
Sergunb 0:8918a71cdbe9 736 //Add Upgrade header field
Sergunb 0:8918a71cdbe9 737 p += sprintf(p, "Upgrade: websocket\r\n");
Sergunb 0:8918a71cdbe9 738 //Add Connection header field
Sergunb 0:8918a71cdbe9 739 p += sprintf(p, "Connection: Upgrade\r\n");
Sergunb 0:8918a71cdbe9 740
Sergunb 0:8918a71cdbe9 741 //Add Sec-WebSocket-Protocol header field
Sergunb 0:8918a71cdbe9 742 if(webSocket->subProtocol[0] != '\0')
Sergunb 0:8918a71cdbe9 743 p += sprintf(p, "Sec-WebSocket-Protocol: %s\r\n", webSocket->subProtocol);
Sergunb 0:8918a71cdbe9 744
Sergunb 0:8918a71cdbe9 745 //Add Sec-WebSocket-Accept header field
Sergunb 0:8918a71cdbe9 746 p += sprintf(p, "Sec-WebSocket-Accept: %s\r\n",
Sergunb 0:8918a71cdbe9 747 webSocket->handshakeContext.serverKey);
Sergunb 0:8918a71cdbe9 748
Sergunb 0:8918a71cdbe9 749 //An empty line indicates the end of the header fields
Sergunb 0:8918a71cdbe9 750 p += sprintf(p, "\r\n");
Sergunb 0:8918a71cdbe9 751
Sergunb 0:8918a71cdbe9 752 //Debug message
Sergunb 0:8918a71cdbe9 753 TRACE_DEBUG("\r\n");
Sergunb 0:8918a71cdbe9 754 TRACE_DEBUG("WebSocket: server handshake\r\n");
Sergunb 0:8918a71cdbe9 755 TRACE_DEBUG("%s", txContext->buffer);
Sergunb 0:8918a71cdbe9 756
Sergunb 0:8918a71cdbe9 757 //Rewind to the beginning of the buffer
Sergunb 0:8918a71cdbe9 758 txContext->bufferPos = 0;
Sergunb 0:8918a71cdbe9 759 //Update the number of data buffered but not yet sent
Sergunb 0:8918a71cdbe9 760 txContext->bufferLen = strlen((char_t *) txContext->buffer);
Sergunb 0:8918a71cdbe9 761
Sergunb 0:8918a71cdbe9 762 //Successful processing
Sergunb 0:8918a71cdbe9 763 return NO_ERROR;
Sergunb 0:8918a71cdbe9 764 }
Sergunb 0:8918a71cdbe9 765
Sergunb 0:8918a71cdbe9 766
Sergunb 0:8918a71cdbe9 767 /**
Sergunb 0:8918a71cdbe9 768 * @brief Format HTTP error response
Sergunb 0:8918a71cdbe9 769 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 770 * @param[in] statusCode HTTP status code
Sergunb 0:8918a71cdbe9 771 * @param[in] message User message
Sergunb 0:8918a71cdbe9 772 * @return Error code
Sergunb 0:8918a71cdbe9 773 **/
Sergunb 0:8918a71cdbe9 774
Sergunb 0:8918a71cdbe9 775 error_t webSocketFormatErrorResponse(WebSocket *webSocket,
Sergunb 0:8918a71cdbe9 776 uint_t statusCode, const char_t *message)
Sergunb 0:8918a71cdbe9 777 {
Sergunb 0:8918a71cdbe9 778 uint_t i;
Sergunb 0:8918a71cdbe9 779 size_t length;
Sergunb 0:8918a71cdbe9 780 char_t *p;
Sergunb 0:8918a71cdbe9 781 WebSocketFrameContext *txContext;
Sergunb 0:8918a71cdbe9 782
Sergunb 0:8918a71cdbe9 783 //HTML response template
Sergunb 0:8918a71cdbe9 784 static const char_t template[] =
Sergunb 0:8918a71cdbe9 785 "<!doctype html>\r\n"
Sergunb 0:8918a71cdbe9 786 "<html>\r\n"
Sergunb 0:8918a71cdbe9 787 "<head><title>Error %03d</title></head>\r\n"
Sergunb 0:8918a71cdbe9 788 "<body>\r\n"
Sergunb 0:8918a71cdbe9 789 "<h2>Error %03d</h2>\r\n"
Sergunb 0:8918a71cdbe9 790 "<p>%s</p>\r\n"
Sergunb 0:8918a71cdbe9 791 "</body>\r\n"
Sergunb 0:8918a71cdbe9 792 "</html>\r\n";
Sergunb 0:8918a71cdbe9 793
Sergunb 0:8918a71cdbe9 794 //Point to the TX context
Sergunb 0:8918a71cdbe9 795 txContext = &webSocket->txContext;
Sergunb 0:8918a71cdbe9 796 //Point to the buffer where to format the client's handshake
Sergunb 0:8918a71cdbe9 797 p = (char_t *) txContext->buffer;
Sergunb 0:8918a71cdbe9 798
Sergunb 0:8918a71cdbe9 799 //The first line of a response message is the Status-Line, consisting
Sergunb 0:8918a71cdbe9 800 //of the protocol version followed by a numeric status code and its
Sergunb 0:8918a71cdbe9 801 //associated textual phrase
Sergunb 0:8918a71cdbe9 802 p += sprintf(p, "HTTP/%u.%u %u ", MSB(webSocket->handshakeContext.version),
Sergunb 0:8918a71cdbe9 803 LSB(webSocket->handshakeContext.version), statusCode);
Sergunb 0:8918a71cdbe9 804
Sergunb 0:8918a71cdbe9 805 //Retrieve the Reason-Phrase that corresponds to the Status-Code
Sergunb 0:8918a71cdbe9 806 for(i = 0; i < arraysize(statusCodeList); i++)
Sergunb 0:8918a71cdbe9 807 {
Sergunb 0:8918a71cdbe9 808 //Check the status code
Sergunb 0:8918a71cdbe9 809 if(statusCodeList[i].value == statusCode)
Sergunb 0:8918a71cdbe9 810 {
Sergunb 0:8918a71cdbe9 811 //Append the textual phrase to the Status-Line
Sergunb 0:8918a71cdbe9 812 p += sprintf(p, statusCodeList[i].message);
Sergunb 0:8918a71cdbe9 813 //Break the loop and continue processing
Sergunb 0:8918a71cdbe9 814 break;
Sergunb 0:8918a71cdbe9 815 }
Sergunb 0:8918a71cdbe9 816 }
Sergunb 0:8918a71cdbe9 817
Sergunb 0:8918a71cdbe9 818 //Properly terminate the Status-Line
Sergunb 0:8918a71cdbe9 819 p += sprintf(p, "\r\n");
Sergunb 0:8918a71cdbe9 820
Sergunb 0:8918a71cdbe9 821 //Content type
Sergunb 0:8918a71cdbe9 822 p += sprintf(p, "Content-Type: %s\r\n", "text/html");
Sergunb 0:8918a71cdbe9 823
Sergunb 0:8918a71cdbe9 824 //Compute the length of the response
Sergunb 0:8918a71cdbe9 825 length = strlen(template) + strlen(message) - 4;
Sergunb 0:8918a71cdbe9 826 //Set Content-Length field
Sergunb 0:8918a71cdbe9 827 p += sprintf(p, "Content-Length: %" PRIuSIZE "\r\n", length);
Sergunb 0:8918a71cdbe9 828
Sergunb 0:8918a71cdbe9 829 //Terminate the header with an empty line
Sergunb 0:8918a71cdbe9 830 p += sprintf(p, "\r\n");
Sergunb 0:8918a71cdbe9 831
Sergunb 0:8918a71cdbe9 832 //Format HTML response
Sergunb 0:8918a71cdbe9 833 p += sprintf(p, template, statusCode, statusCode, message);
Sergunb 0:8918a71cdbe9 834
Sergunb 0:8918a71cdbe9 835 //Rewind to the beginning of the buffer
Sergunb 0:8918a71cdbe9 836 txContext->bufferPos = 0;
Sergunb 0:8918a71cdbe9 837 //Update the number of data buffered but not yet sent
Sergunb 0:8918a71cdbe9 838 txContext->bufferLen = strlen((char_t *) txContext->buffer);
Sergunb 0:8918a71cdbe9 839
Sergunb 0:8918a71cdbe9 840 //Successful processing
Sergunb 0:8918a71cdbe9 841 return NO_ERROR;
Sergunb 0:8918a71cdbe9 842 }
Sergunb 0:8918a71cdbe9 843
Sergunb 0:8918a71cdbe9 844
Sergunb 0:8918a71cdbe9 845 /**
Sergunb 0:8918a71cdbe9 846 * @brief Verify client's handshake
Sergunb 0:8918a71cdbe9 847 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 848 * @return Error code
Sergunb 0:8918a71cdbe9 849 **/
Sergunb 0:8918a71cdbe9 850
Sergunb 0:8918a71cdbe9 851 error_t webSocketVerifyClientHandshake(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 852 {
Sergunb 0:8918a71cdbe9 853 error_t error;
Sergunb 0:8918a71cdbe9 854 WebSocketHandshakeContext *handshakeContext;
Sergunb 0:8918a71cdbe9 855
Sergunb 0:8918a71cdbe9 856 //Debug message
Sergunb 0:8918a71cdbe9 857 TRACE_DEBUG("WebSocket: verifying client handshake\r\n");
Sergunb 0:8918a71cdbe9 858
Sergunb 0:8918a71cdbe9 859 //Point to the handshake context
Sergunb 0:8918a71cdbe9 860 handshakeContext = &webSocket->handshakeContext;
Sergunb 0:8918a71cdbe9 861
Sergunb 0:8918a71cdbe9 862 //The HTTP version must be at least 1.1
Sergunb 0:8918a71cdbe9 863 if(handshakeContext->version < WS_HTTP_VERSION_1_1)
Sergunb 0:8918a71cdbe9 864 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 865
Sergunb 0:8918a71cdbe9 866 //The request must contain an Upgrade header field whose value
Sergunb 0:8918a71cdbe9 867 //must include the "websocket" keyword
Sergunb 0:8918a71cdbe9 868 if(!handshakeContext->upgradeWebSocket)
Sergunb 0:8918a71cdbe9 869 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 870
Sergunb 0:8918a71cdbe9 871 //The request must contain a Connection header field whose value
Sergunb 0:8918a71cdbe9 872 //must include the "Upgrade" token
Sergunb 0:8918a71cdbe9 873 if(!handshakeContext->connectionUpgrade)
Sergunb 0:8918a71cdbe9 874 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 875
Sergunb 0:8918a71cdbe9 876 //The request must include a header field with the name Sec-WebSocket-Key
Sergunb 0:8918a71cdbe9 877 if(handshakeContext->clientKey[0] == 0)
Sergunb 0:8918a71cdbe9 878 return ERROR_INVALID_REQUEST;
Sergunb 0:8918a71cdbe9 879
Sergunb 0:8918a71cdbe9 880 //Check the Sec-WebSocket-Key header field
Sergunb 0:8918a71cdbe9 881 error = webSocketVerifyClientKey(webSocket);
Sergunb 0:8918a71cdbe9 882 //Verification failed?
Sergunb 0:8918a71cdbe9 883 if(error)
Sergunb 0:8918a71cdbe9 884 return error;
Sergunb 0:8918a71cdbe9 885
Sergunb 0:8918a71cdbe9 886 //Generate the server part of the handshake
Sergunb 0:8918a71cdbe9 887 webSocketChangeState(webSocket, WS_STATE_SERVER_HANDSHAKE);
Sergunb 0:8918a71cdbe9 888
Sergunb 0:8918a71cdbe9 889 //Successful processing
Sergunb 0:8918a71cdbe9 890 return NO_ERROR;
Sergunb 0:8918a71cdbe9 891 }
Sergunb 0:8918a71cdbe9 892
Sergunb 0:8918a71cdbe9 893
Sergunb 0:8918a71cdbe9 894 /**
Sergunb 0:8918a71cdbe9 895 * @brief Verify server's handshake
Sergunb 0:8918a71cdbe9 896 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 897 * @return Error code
Sergunb 0:8918a71cdbe9 898 **/
Sergunb 0:8918a71cdbe9 899
Sergunb 0:8918a71cdbe9 900 error_t webSocketVerifyServerHandshake(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 901 {
Sergunb 0:8918a71cdbe9 902 error_t error;
Sergunb 0:8918a71cdbe9 903 WebSocketHandshakeContext *handshakeContext;
Sergunb 0:8918a71cdbe9 904
Sergunb 0:8918a71cdbe9 905 //Debug message
Sergunb 0:8918a71cdbe9 906 TRACE_DEBUG("WebSocket: verifying server handshake\r\n");
Sergunb 0:8918a71cdbe9 907
Sergunb 0:8918a71cdbe9 908 //Point to the handshake context
Sergunb 0:8918a71cdbe9 909 handshakeContext = &webSocket->handshakeContext;
Sergunb 0:8918a71cdbe9 910
Sergunb 0:8918a71cdbe9 911 //If the status code received from the server is not 101, the client
Sergunb 0:8918a71cdbe9 912 //handles the response per HTTP procedures
Sergunb 0:8918a71cdbe9 913 if(handshakeContext->statusCode == 401)
Sergunb 0:8918a71cdbe9 914 {
Sergunb 0:8918a71cdbe9 915 //Authorization required
Sergunb 0:8918a71cdbe9 916 return ERROR_AUTH_REQUIRED;
Sergunb 0:8918a71cdbe9 917 }
Sergunb 0:8918a71cdbe9 918 else if(handshakeContext->statusCode != 101)
Sergunb 0:8918a71cdbe9 919 {
Sergunb 0:8918a71cdbe9 920 //Unknown status code
Sergunb 0:8918a71cdbe9 921 return ERROR_INVALID_STATUS;
Sergunb 0:8918a71cdbe9 922 }
Sergunb 0:8918a71cdbe9 923
Sergunb 0:8918a71cdbe9 924 //If the response lacks an Upgrade header field or the Upgrade header field
Sergunb 0:8918a71cdbe9 925 //contains a value that is not an ASCII case-insensitive match for the
Sergunb 0:8918a71cdbe9 926 //value "websocket", the client must fail the WebSocket connection
Sergunb 0:8918a71cdbe9 927 if(!handshakeContext->upgradeWebSocket)
Sergunb 0:8918a71cdbe9 928 return ERROR_INVALID_SYNTAX;
Sergunb 0:8918a71cdbe9 929
Sergunb 0:8918a71cdbe9 930 //If the response lacks a Connection header field or the Connection header
Sergunb 0:8918a71cdbe9 931 //field doesn't contain a token that is an ASCII case-insensitive match for
Sergunb 0:8918a71cdbe9 932 //the value "Upgrade", the client must fail the WebSocket connection
Sergunb 0:8918a71cdbe9 933 if(!handshakeContext->connectionUpgrade)
Sergunb 0:8918a71cdbe9 934 return ERROR_INVALID_SYNTAX;
Sergunb 0:8918a71cdbe9 935
Sergunb 0:8918a71cdbe9 936 //If the response lacks a Sec-WebSocket-Accept header field, the client
Sergunb 0:8918a71cdbe9 937 //must fail the WebSocket connection
Sergunb 0:8918a71cdbe9 938 if(strlen(handshakeContext->serverKey) == 0)
Sergunb 0:8918a71cdbe9 939 return ERROR_INVALID_SYNTAX;
Sergunb 0:8918a71cdbe9 940
Sergunb 0:8918a71cdbe9 941 //Check the Sec-WebSocket-Accept header field
Sergunb 0:8918a71cdbe9 942 error = webSocketVerifyServerKey(webSocket);
Sergunb 0:8918a71cdbe9 943 //Verification failed?
Sergunb 0:8918a71cdbe9 944 if(error)
Sergunb 0:8918a71cdbe9 945 return error;
Sergunb 0:8918a71cdbe9 946
Sergunb 0:8918a71cdbe9 947 //If the server's response is validated as provided for above, it is
Sergunb 0:8918a71cdbe9 948 //said that the WebSocket connection is established and that the
Sergunb 0:8918a71cdbe9 949 //WebSocket connection is in the OPEN state
Sergunb 0:8918a71cdbe9 950 webSocketChangeState(webSocket, WS_STATE_OPEN);
Sergunb 0:8918a71cdbe9 951
Sergunb 0:8918a71cdbe9 952 //Successful processing
Sergunb 0:8918a71cdbe9 953 return NO_ERROR;
Sergunb 0:8918a71cdbe9 954 }
Sergunb 0:8918a71cdbe9 955
Sergunb 0:8918a71cdbe9 956
Sergunb 0:8918a71cdbe9 957 /**
Sergunb 0:8918a71cdbe9 958 * @brief Generate client's key
Sergunb 0:8918a71cdbe9 959 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 960 * @return Error code
Sergunb 0:8918a71cdbe9 961 **/
Sergunb 0:8918a71cdbe9 962
Sergunb 0:8918a71cdbe9 963 error_t webSocketGenerateClientKey(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 964 {
Sergunb 0:8918a71cdbe9 965 error_t error;
Sergunb 0:8918a71cdbe9 966 size_t n;
Sergunb 0:8918a71cdbe9 967 uint8_t nonce[16];
Sergunb 0:8918a71cdbe9 968 WebSocketHandshakeContext *handshakeContext;
Sergunb 0:8918a71cdbe9 969
Sergunb 0:8918a71cdbe9 970 //Debug message
Sergunb 0:8918a71cdbe9 971 TRACE_DEBUG("WebSocket: Generating client's key...\r\n");
Sergunb 0:8918a71cdbe9 972
Sergunb 0:8918a71cdbe9 973 //Point to the handshake context
Sergunb 0:8918a71cdbe9 974 handshakeContext = &webSocket->handshakeContext;
Sergunb 0:8918a71cdbe9 975
Sergunb 0:8918a71cdbe9 976 //Make sure that the RNG callback function has been registered
Sergunb 0:8918a71cdbe9 977 if(webSockRandCallback == NULL)
Sergunb 0:8918a71cdbe9 978 {
Sergunb 0:8918a71cdbe9 979 //A cryptographically strong random number generator
Sergunb 0:8918a71cdbe9 980 //must be used to generate the nonce
Sergunb 0:8918a71cdbe9 981 return ERROR_PRNG_NOT_READY;
Sergunb 0:8918a71cdbe9 982 }
Sergunb 0:8918a71cdbe9 983
Sergunb 0:8918a71cdbe9 984 //A nonce must be selected randomly for each connection
Sergunb 0:8918a71cdbe9 985 error = webSockRandCallback(nonce, sizeof(nonce));
Sergunb 0:8918a71cdbe9 986 //Any error to report?
Sergunb 0:8918a71cdbe9 987 if(error)
Sergunb 0:8918a71cdbe9 988 return error;
Sergunb 0:8918a71cdbe9 989
Sergunb 0:8918a71cdbe9 990 //Encode the client's key
Sergunb 0:8918a71cdbe9 991 base64Encode(nonce, sizeof(nonce), handshakeContext->clientKey, &n);
Sergunb 0:8918a71cdbe9 992
Sergunb 0:8918a71cdbe9 993 //Debug message
Sergunb 0:8918a71cdbe9 994 TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey);
Sergunb 0:8918a71cdbe9 995
Sergunb 0:8918a71cdbe9 996 //Successful processing
Sergunb 0:8918a71cdbe9 997 return NO_ERROR;
Sergunb 0:8918a71cdbe9 998 }
Sergunb 0:8918a71cdbe9 999
Sergunb 0:8918a71cdbe9 1000
Sergunb 0:8918a71cdbe9 1001 /**
Sergunb 0:8918a71cdbe9 1002 * @brief Generate server's key
Sergunb 0:8918a71cdbe9 1003 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 1004 * @return Error code
Sergunb 0:8918a71cdbe9 1005 **/
Sergunb 0:8918a71cdbe9 1006
Sergunb 0:8918a71cdbe9 1007 error_t webSocketGenerateServerKey(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 1008 {
Sergunb 0:8918a71cdbe9 1009 size_t n;
Sergunb 0:8918a71cdbe9 1010 WebSocketHandshakeContext *handshakeContext;
Sergunb 0:8918a71cdbe9 1011 Sha1Context sha1Context;
Sergunb 0:8918a71cdbe9 1012
Sergunb 0:8918a71cdbe9 1013 //Debug message
Sergunb 0:8918a71cdbe9 1014 TRACE_DEBUG("WebSocket: Generating server's key...\r\n");
Sergunb 0:8918a71cdbe9 1015
Sergunb 0:8918a71cdbe9 1016 //Point to the handshake context
Sergunb 0:8918a71cdbe9 1017 handshakeContext = &webSocket->handshakeContext;
Sergunb 0:8918a71cdbe9 1018
Sergunb 0:8918a71cdbe9 1019 //Retrieve the length of the client key
Sergunb 0:8918a71cdbe9 1020 n = strlen(handshakeContext->clientKey);
Sergunb 0:8918a71cdbe9 1021
Sergunb 0:8918a71cdbe9 1022 //Concatenate the Sec-WebSocket-Key with the GUID string and digest
Sergunb 0:8918a71cdbe9 1023 //the resulting string using SHA-1
Sergunb 0:8918a71cdbe9 1024 sha1Init(&sha1Context);
Sergunb 0:8918a71cdbe9 1025 sha1Update(&sha1Context, handshakeContext->clientKey, n);
Sergunb 0:8918a71cdbe9 1026 sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid));
Sergunb 0:8918a71cdbe9 1027 sha1Final(&sha1Context, NULL);
Sergunb 0:8918a71cdbe9 1028
Sergunb 0:8918a71cdbe9 1029 //Encode the result using Base64
Sergunb 0:8918a71cdbe9 1030 base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE,
Sergunb 0:8918a71cdbe9 1031 handshakeContext->serverKey, &n);
Sergunb 0:8918a71cdbe9 1032
Sergunb 0:8918a71cdbe9 1033 //Debug message
Sergunb 0:8918a71cdbe9 1034 TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey);
Sergunb 0:8918a71cdbe9 1035
Sergunb 0:8918a71cdbe9 1036 //Successful processing
Sergunb 0:8918a71cdbe9 1037 return NO_ERROR;
Sergunb 0:8918a71cdbe9 1038 }
Sergunb 0:8918a71cdbe9 1039
Sergunb 0:8918a71cdbe9 1040
Sergunb 0:8918a71cdbe9 1041 /**
Sergunb 0:8918a71cdbe9 1042 * @brief Verify client's key
Sergunb 0:8918a71cdbe9 1043 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 1044 * @return Error code
Sergunb 0:8918a71cdbe9 1045 **/
Sergunb 0:8918a71cdbe9 1046
Sergunb 0:8918a71cdbe9 1047 error_t webSocketVerifyClientKey(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 1048 {
Sergunb 0:8918a71cdbe9 1049 error_t error;
Sergunb 0:8918a71cdbe9 1050 size_t n;
Sergunb 0:8918a71cdbe9 1051 char_t *buffer;
Sergunb 0:8918a71cdbe9 1052 WebSocketHandshakeContext *handshakeContext;
Sergunb 0:8918a71cdbe9 1053
Sergunb 0:8918a71cdbe9 1054 //Debug message
Sergunb 0:8918a71cdbe9 1055 TRACE_DEBUG("WebSocket: Verifying client's key...\r\n");
Sergunb 0:8918a71cdbe9 1056
Sergunb 0:8918a71cdbe9 1057 //Temporary buffer
Sergunb 0:8918a71cdbe9 1058 buffer = (char_t *) webSocket->txContext.buffer;
Sergunb 0:8918a71cdbe9 1059
Sergunb 0:8918a71cdbe9 1060 //Point to the handshake context
Sergunb 0:8918a71cdbe9 1061 handshakeContext = &webSocket->handshakeContext;
Sergunb 0:8918a71cdbe9 1062
Sergunb 0:8918a71cdbe9 1063 //Retrieve the length of the client's key
Sergunb 0:8918a71cdbe9 1064 n = strlen(handshakeContext->clientKey);
Sergunb 0:8918a71cdbe9 1065
Sergunb 0:8918a71cdbe9 1066 //The value of the Sec-WebSocket-Key header field must be a 16-byte
Sergunb 0:8918a71cdbe9 1067 //value that has been Base64-encoded
Sergunb 0:8918a71cdbe9 1068 error = base64Decode(handshakeContext->clientKey, n, buffer, &n);
Sergunb 0:8918a71cdbe9 1069 //Decoding failed?
Sergunb 0:8918a71cdbe9 1070 if(error)
Sergunb 0:8918a71cdbe9 1071 return ERROR_INVALID_KEY;
Sergunb 0:8918a71cdbe9 1072
Sergunb 0:8918a71cdbe9 1073 //Check the length of the resulting value
Sergunb 0:8918a71cdbe9 1074 if(n != 16)
Sergunb 0:8918a71cdbe9 1075 return ERROR_INVALID_KEY;
Sergunb 0:8918a71cdbe9 1076
Sergunb 0:8918a71cdbe9 1077 //Successful verification
Sergunb 0:8918a71cdbe9 1078 return NO_ERROR;
Sergunb 0:8918a71cdbe9 1079 }
Sergunb 0:8918a71cdbe9 1080
Sergunb 0:8918a71cdbe9 1081
Sergunb 0:8918a71cdbe9 1082 /**
Sergunb 0:8918a71cdbe9 1083 * @brief Verify server's key
Sergunb 0:8918a71cdbe9 1084 * @param[in] webSocket Handle to a WebSocket
Sergunb 0:8918a71cdbe9 1085 * @return Error code
Sergunb 0:8918a71cdbe9 1086 **/
Sergunb 0:8918a71cdbe9 1087
Sergunb 0:8918a71cdbe9 1088 error_t webSocketVerifyServerKey(WebSocket *webSocket)
Sergunb 0:8918a71cdbe9 1089 {
Sergunb 0:8918a71cdbe9 1090 size_t n;
Sergunb 0:8918a71cdbe9 1091 char_t *buffer;
Sergunb 0:8918a71cdbe9 1092 WebSocketHandshakeContext *handshakeContext;
Sergunb 0:8918a71cdbe9 1093 Sha1Context sha1Context;
Sergunb 0:8918a71cdbe9 1094
Sergunb 0:8918a71cdbe9 1095 //Debug message
Sergunb 0:8918a71cdbe9 1096 TRACE_DEBUG("WebSocket: Verifying server's key...\r\n");
Sergunb 0:8918a71cdbe9 1097
Sergunb 0:8918a71cdbe9 1098 //Temporary buffer
Sergunb 0:8918a71cdbe9 1099 buffer = (char_t *) webSocket->txContext.buffer;
Sergunb 0:8918a71cdbe9 1100
Sergunb 0:8918a71cdbe9 1101 //Point to the handshake context
Sergunb 0:8918a71cdbe9 1102 handshakeContext = &webSocket->handshakeContext;
Sergunb 0:8918a71cdbe9 1103
Sergunb 0:8918a71cdbe9 1104 //Retrieve the length of the client's key
Sergunb 0:8918a71cdbe9 1105 n = strlen(handshakeContext->clientKey);
Sergunb 0:8918a71cdbe9 1106
Sergunb 0:8918a71cdbe9 1107 //Concatenate the Sec-WebSocket-Key with the GUID string and digest
Sergunb 0:8918a71cdbe9 1108 //the resulting string using SHA-1
Sergunb 0:8918a71cdbe9 1109 sha1Init(&sha1Context);
Sergunb 0:8918a71cdbe9 1110 sha1Update(&sha1Context, handshakeContext->clientKey, n);
Sergunb 0:8918a71cdbe9 1111 sha1Update(&sha1Context, webSocketGuid, strlen(webSocketGuid));
Sergunb 0:8918a71cdbe9 1112 sha1Final(&sha1Context, NULL);
Sergunb 0:8918a71cdbe9 1113
Sergunb 0:8918a71cdbe9 1114 //Encode the result using Base64
Sergunb 0:8918a71cdbe9 1115 base64Encode(sha1Context.digest, SHA1_DIGEST_SIZE, buffer, &n);
Sergunb 0:8918a71cdbe9 1116
Sergunb 0:8918a71cdbe9 1117 //Debug message
Sergunb 0:8918a71cdbe9 1118 TRACE_DEBUG(" Client key: %s\r\n", handshakeContext->clientKey);
Sergunb 0:8918a71cdbe9 1119 TRACE_DEBUG(" Server key: %s\r\n", handshakeContext->serverKey);
Sergunb 0:8918a71cdbe9 1120 TRACE_DEBUG(" Calculated key: %s\r\n", webSocket->txContext.buffer);
Sergunb 0:8918a71cdbe9 1121
Sergunb 0:8918a71cdbe9 1122 //Check whether the server's key is valid
Sergunb 0:8918a71cdbe9 1123 if(strcmp(handshakeContext->serverKey, buffer))
Sergunb 0:8918a71cdbe9 1124 return ERROR_INVALID_KEY;
Sergunb 0:8918a71cdbe9 1125
Sergunb 0:8918a71cdbe9 1126 //Successful verification
Sergunb 0:8918a71cdbe9 1127 return NO_ERROR;
Sergunb 0:8918a71cdbe9 1128 }
Sergunb 0:8918a71cdbe9 1129
Sergunb 0:8918a71cdbe9 1130
Sergunb 0:8918a71cdbe9 1131 /**
Sergunb 0:8918a71cdbe9 1132 * @brief Check whether a status code is valid
Sergunb 0:8918a71cdbe9 1133 * @param[in] statusCode Status code
Sergunb 0:8918a71cdbe9 1134 * @return The function returns TRUE is the specified status code is
Sergunb 0:8918a71cdbe9 1135 * valid. Otherwise, FALSE is returned
Sergunb 0:8918a71cdbe9 1136 **/
Sergunb 0:8918a71cdbe9 1137
Sergunb 0:8918a71cdbe9 1138 bool_t webSocketCheckStatusCode(uint16_t statusCode)
Sergunb 0:8918a71cdbe9 1139 {
Sergunb 0:8918a71cdbe9 1140 bool_t valid;
Sergunb 0:8918a71cdbe9 1141
Sergunb 0:8918a71cdbe9 1142 //Check status code
Sergunb 0:8918a71cdbe9 1143 if(statusCode == WS_STATUS_CODE_NORMAL_CLOSURE ||
Sergunb 0:8918a71cdbe9 1144 statusCode == WS_STATUS_CODE_GOING_AWAY ||
Sergunb 0:8918a71cdbe9 1145 statusCode == WS_STATUS_CODE_PROTOCOL_ERROR ||
Sergunb 0:8918a71cdbe9 1146 statusCode == WS_STATUS_CODE_UNSUPPORTED_DATA ||
Sergunb 0:8918a71cdbe9 1147 statusCode == WS_STATUS_CODE_INVALID_PAYLOAD_DATA ||
Sergunb 0:8918a71cdbe9 1148 statusCode == WS_STATUS_CODE_POLICY_VIOLATION ||
Sergunb 0:8918a71cdbe9 1149 statusCode == WS_STATUS_CODE_MESSAGE_TOO_BIG ||
Sergunb 0:8918a71cdbe9 1150 statusCode == WS_STATUS_CODE_MANDATORY_EXT ||
Sergunb 0:8918a71cdbe9 1151 statusCode == WS_STATUS_CODE_INTERNAL_ERROR)
Sergunb 0:8918a71cdbe9 1152 {
Sergunb 0:8918a71cdbe9 1153 valid = TRUE;
Sergunb 0:8918a71cdbe9 1154 }
Sergunb 0:8918a71cdbe9 1155 else if(statusCode >= 3000)
Sergunb 0:8918a71cdbe9 1156 {
Sergunb 0:8918a71cdbe9 1157 valid = TRUE;
Sergunb 0:8918a71cdbe9 1158 }
Sergunb 0:8918a71cdbe9 1159 else
Sergunb 0:8918a71cdbe9 1160 {
Sergunb 0:8918a71cdbe9 1161 valid = FALSE;
Sergunb 0:8918a71cdbe9 1162 }
Sergunb 0:8918a71cdbe9 1163
Sergunb 0:8918a71cdbe9 1164 //The function returns TRUE is the specified status code is valid
Sergunb 0:8918a71cdbe9 1165 return valid;
Sergunb 0:8918a71cdbe9 1166 }
Sergunb 0:8918a71cdbe9 1167
Sergunb 0:8918a71cdbe9 1168
Sergunb 0:8918a71cdbe9 1169 /**
Sergunb 0:8918a71cdbe9 1170 * @brief Decode a percent-encoded string
Sergunb 0:8918a71cdbe9 1171 * @param[in] input NULL-terminated string to be decoded
Sergunb 0:8918a71cdbe9 1172 * @param[out] output NULL-terminated string resulting from the decoding process
Sergunb 0:8918a71cdbe9 1173 * @param[in] outputSize Size of the output buffer in bytes
Sergunb 0:8918a71cdbe9 1174 * @return Error code
Sergunb 0:8918a71cdbe9 1175 **/
Sergunb 0:8918a71cdbe9 1176
Sergunb 0:8918a71cdbe9 1177 error_t webSocketDecodePercentEncodedString(const char_t *input,
Sergunb 0:8918a71cdbe9 1178 char_t *output, size_t outputSize)
Sergunb 0:8918a71cdbe9 1179 {
Sergunb 0:8918a71cdbe9 1180 size_t i;
Sergunb 0:8918a71cdbe9 1181 char_t buffer[3];
Sergunb 0:8918a71cdbe9 1182
Sergunb 0:8918a71cdbe9 1183 //Check parameters
Sergunb 0:8918a71cdbe9 1184 if(input == NULL || output == NULL)
Sergunb 0:8918a71cdbe9 1185 return ERROR_INVALID_PARAMETER;
Sergunb 0:8918a71cdbe9 1186
Sergunb 0:8918a71cdbe9 1187 //Decode the percent-encoded string
Sergunb 0:8918a71cdbe9 1188 for(i = 0; *input != '\0' && i < outputSize; i++)
Sergunb 0:8918a71cdbe9 1189 {
Sergunb 0:8918a71cdbe9 1190 //Check current character
Sergunb 0:8918a71cdbe9 1191 if(*input == '+')
Sergunb 0:8918a71cdbe9 1192 {
Sergunb 0:8918a71cdbe9 1193 //Replace '+' characters with spaces
Sergunb 0:8918a71cdbe9 1194 output[i] = ' ';
Sergunb 0:8918a71cdbe9 1195 //Advance data pointer
Sergunb 0:8918a71cdbe9 1196 input++;
Sergunb 0:8918a71cdbe9 1197 }
Sergunb 0:8918a71cdbe9 1198 else if(input[0] == '%' && input[1] != '\0' && input[2] != '\0')
Sergunb 0:8918a71cdbe9 1199 {
Sergunb 0:8918a71cdbe9 1200 //Process percent-encoded characters
Sergunb 0:8918a71cdbe9 1201 buffer[0] = input[1];
Sergunb 0:8918a71cdbe9 1202 buffer[1] = input[2];
Sergunb 0:8918a71cdbe9 1203 buffer[2] = '\0';
Sergunb 0:8918a71cdbe9 1204 //String to integer conversion
Sergunb 0:8918a71cdbe9 1205 output[i] = (uint8_t) strtoul(buffer, NULL, 16);
Sergunb 0:8918a71cdbe9 1206 //Advance data pointer
Sergunb 0:8918a71cdbe9 1207 input += 3;
Sergunb 0:8918a71cdbe9 1208 }
Sergunb 0:8918a71cdbe9 1209 else
Sergunb 0:8918a71cdbe9 1210 {
Sergunb 0:8918a71cdbe9 1211 //Copy any other characters
Sergunb 0:8918a71cdbe9 1212 output[i] = *input;
Sergunb 0:8918a71cdbe9 1213 //Advance data pointer
Sergunb 0:8918a71cdbe9 1214 input++;
Sergunb 0:8918a71cdbe9 1215 }
Sergunb 0:8918a71cdbe9 1216 }
Sergunb 0:8918a71cdbe9 1217
Sergunb 0:8918a71cdbe9 1218 //Check whether the output buffer runs out of space
Sergunb 0:8918a71cdbe9 1219 if(i >= outputSize)
Sergunb 0:8918a71cdbe9 1220 return ERROR_FAILURE;
Sergunb 0:8918a71cdbe9 1221
Sergunb 0:8918a71cdbe9 1222 //Properly terminate the resulting string
Sergunb 0:8918a71cdbe9 1223 output[i] = '\0';
Sergunb 0:8918a71cdbe9 1224 //Successful processing
Sergunb 0:8918a71cdbe9 1225 return NO_ERROR;
Sergunb 0:8918a71cdbe9 1226 }
Sergunb 0:8918a71cdbe9 1227
Sergunb 0:8918a71cdbe9 1228
Sergunb 0:8918a71cdbe9 1229 /**
Sergunb 0:8918a71cdbe9 1230 * @brief Check whether a an UTF-8 stream is valid
Sergunb 0:8918a71cdbe9 1231 * @param[in] context UTF-8 decoding context
Sergunb 0:8918a71cdbe9 1232 * @param[in] data Pointer to the chunk of data to be processed
Sergunb 0:8918a71cdbe9 1233 * @param[in] length Data chunk length
Sergunb 0:8918a71cdbe9 1234 * @param[in] remaining number of remaining bytes in the UTF-8 stream
Sergunb 0:8918a71cdbe9 1235 * @return The function returns TRUE is the specified UTF-8 stream is
Sergunb 0:8918a71cdbe9 1236 * valid. Otherwise, FALSE is returned
Sergunb 0:8918a71cdbe9 1237 **/
Sergunb 0:8918a71cdbe9 1238
Sergunb 0:8918a71cdbe9 1239 bool_t webSocketCheckUtf8Stream(WebSocketUtf8Context *context,
Sergunb 0:8918a71cdbe9 1240 const uint8_t *data, size_t length, size_t remaining)
Sergunb 0:8918a71cdbe9 1241 {
Sergunb 0:8918a71cdbe9 1242 size_t i;
Sergunb 0:8918a71cdbe9 1243 bool_t valid;
Sergunb 0:8918a71cdbe9 1244
Sergunb 0:8918a71cdbe9 1245 //Initialize flag
Sergunb 0:8918a71cdbe9 1246 valid = TRUE;
Sergunb 0:8918a71cdbe9 1247
Sergunb 0:8918a71cdbe9 1248 //Interpret the byte stream as UTF-8
Sergunb 0:8918a71cdbe9 1249 for(i = 0; i < length && valid; i++)
Sergunb 0:8918a71cdbe9 1250 {
Sergunb 0:8918a71cdbe9 1251 //Leading or continuation byte?
Sergunb 0:8918a71cdbe9 1252 if(context->utf8CharIndex == 0)
Sergunb 0:8918a71cdbe9 1253 {
Sergunb 0:8918a71cdbe9 1254 //7-bit code point?
Sergunb 0:8918a71cdbe9 1255 if((data[i] & 0x80) == 0x00)
Sergunb 0:8918a71cdbe9 1256 {
Sergunb 0:8918a71cdbe9 1257 //The code point consist of a single byte
Sergunb 0:8918a71cdbe9 1258 context->utf8CharSize = 1;
Sergunb 0:8918a71cdbe9 1259 //Decode the first byte of the sequence
Sergunb 0:8918a71cdbe9 1260 context->utf8CodePoint = data[i] & 0x7F;
Sergunb 0:8918a71cdbe9 1261 }
Sergunb 0:8918a71cdbe9 1262 //11-bit code point?
Sergunb 0:8918a71cdbe9 1263 else if((data[i] & 0xE0) == 0xC0)
Sergunb 0:8918a71cdbe9 1264 {
Sergunb 0:8918a71cdbe9 1265 //The code point consist of a 2 bytes
Sergunb 0:8918a71cdbe9 1266 context->utf8CharSize = 2;
Sergunb 0:8918a71cdbe9 1267 //Decode the first byte of the sequence
Sergunb 0:8918a71cdbe9 1268 context->utf8CodePoint = (data[i] & 0x1F) << 6;
Sergunb 0:8918a71cdbe9 1269 }
Sergunb 0:8918a71cdbe9 1270 //16-bit code point?
Sergunb 0:8918a71cdbe9 1271 else if((data[i] & 0xF0) == 0xE0)
Sergunb 0:8918a71cdbe9 1272 {
Sergunb 0:8918a71cdbe9 1273 //The code point consist of a 3 bytes
Sergunb 0:8918a71cdbe9 1274 context->utf8CharSize = 3;
Sergunb 0:8918a71cdbe9 1275 //Decode the first byte of the sequence
Sergunb 0:8918a71cdbe9 1276 context->utf8CodePoint = (data[i] & 0x0F) << 12;
Sergunb 0:8918a71cdbe9 1277 }
Sergunb 0:8918a71cdbe9 1278 //21-bit code point?
Sergunb 0:8918a71cdbe9 1279 else if((data[i] & 0xF8) == 0xF0)
Sergunb 0:8918a71cdbe9 1280 {
Sergunb 0:8918a71cdbe9 1281 //The code point consist of a 3 bytes
Sergunb 0:8918a71cdbe9 1282 context->utf8CharSize = 4;
Sergunb 0:8918a71cdbe9 1283 //Decode the first byte of the sequence
Sergunb 0:8918a71cdbe9 1284 context->utf8CodePoint = (data[i] & 0x07) << 18;
Sergunb 0:8918a71cdbe9 1285 }
Sergunb 0:8918a71cdbe9 1286 else
Sergunb 0:8918a71cdbe9 1287 {
Sergunb 0:8918a71cdbe9 1288 //The UTF-8 stream is not valid
Sergunb 0:8918a71cdbe9 1289 valid = FALSE;
Sergunb 0:8918a71cdbe9 1290 }
Sergunb 0:8918a71cdbe9 1291
Sergunb 0:8918a71cdbe9 1292 //This test only applies to frames that are not fragmented
Sergunb 0:8918a71cdbe9 1293 if(length <= remaining)
Sergunb 0:8918a71cdbe9 1294 {
Sergunb 0:8918a71cdbe9 1295 //Make sure the UTF-8 stream is properly terminated
Sergunb 0:8918a71cdbe9 1296 if((i + context->utf8CharSize) > remaining)
Sergunb 0:8918a71cdbe9 1297 {
Sergunb 0:8918a71cdbe9 1298 //The UTF-8 stream is not valid
Sergunb 0:8918a71cdbe9 1299 valid = FALSE;
Sergunb 0:8918a71cdbe9 1300 }
Sergunb 0:8918a71cdbe9 1301 }
Sergunb 0:8918a71cdbe9 1302
Sergunb 0:8918a71cdbe9 1303 //Decode the next byte of the sequence
Sergunb 0:8918a71cdbe9 1304 context->utf8CharIndex = context->utf8CharSize - 1;
Sergunb 0:8918a71cdbe9 1305 }
Sergunb 0:8918a71cdbe9 1306 else
Sergunb 0:8918a71cdbe9 1307 {
Sergunb 0:8918a71cdbe9 1308 //Continuation bytes all have 10 in the high-order position
Sergunb 0:8918a71cdbe9 1309 if((data[i] & 0xC0) == 0x80)
Sergunb 0:8918a71cdbe9 1310 {
Sergunb 0:8918a71cdbe9 1311 //Decode the multi-byte sequence
Sergunb 0:8918a71cdbe9 1312 context->utf8CharIndex--;
Sergunb 0:8918a71cdbe9 1313 //All continuation bytes contain exactly 6 bits from the code point
Sergunb 0:8918a71cdbe9 1314 context->utf8CodePoint |= (data[i] & 0x3F) << (context->utf8CharIndex * 6);
Sergunb 0:8918a71cdbe9 1315
Sergunb 0:8918a71cdbe9 1316 //The correct encoding of a code point use only the minimum number
Sergunb 0:8918a71cdbe9 1317 //of bytes required to hold the significant bits of the code point
Sergunb 0:8918a71cdbe9 1318 if(context->utf8CharSize == 2)
Sergunb 0:8918a71cdbe9 1319 {
Sergunb 0:8918a71cdbe9 1320 //Overlong encoding is not supported
Sergunb 0:8918a71cdbe9 1321 if((context->utf8CodePoint & ~0x7F) == 0)
Sergunb 0:8918a71cdbe9 1322 valid = FALSE;
Sergunb 0:8918a71cdbe9 1323 }
Sergunb 0:8918a71cdbe9 1324 if(context->utf8CharSize == 3 && context->utf8CharIndex < 2)
Sergunb 0:8918a71cdbe9 1325 {
Sergunb 0:8918a71cdbe9 1326 //Overlong encoding is not supported
Sergunb 0:8918a71cdbe9 1327 if((context->utf8CodePoint & ~0x7FF) == 0)
Sergunb 0:8918a71cdbe9 1328 valid = FALSE;
Sergunb 0:8918a71cdbe9 1329 }
Sergunb 0:8918a71cdbe9 1330 if(context->utf8CharSize == 4 && context->utf8CharIndex < 3)
Sergunb 0:8918a71cdbe9 1331 {
Sergunb 0:8918a71cdbe9 1332 //Overlong encoding is not supported
Sergunb 0:8918a71cdbe9 1333 if((context->utf8CodePoint & ~0xFFFF) == 0)
Sergunb 0:8918a71cdbe9 1334 valid = FALSE;
Sergunb 0:8918a71cdbe9 1335 }
Sergunb 0:8918a71cdbe9 1336
Sergunb 0:8918a71cdbe9 1337 //According to the UTF-8 definition (RFC 3629) the high and low
Sergunb 0:8918a71cdbe9 1338 //surrogate halves used by UTF-16 (U+D800 through U+DFFF) are not
Sergunb 0:8918a71cdbe9 1339 //legal Unicode values, and their UTF-8 encoding should be treated
Sergunb 0:8918a71cdbe9 1340 //as an invalid byte sequence
Sergunb 0:8918a71cdbe9 1341 if(context->utf8CodePoint >= 0xD800 && context->utf8CodePoint < 0xE000)
Sergunb 0:8918a71cdbe9 1342 valid = FALSE;
Sergunb 0:8918a71cdbe9 1343
Sergunb 0:8918a71cdbe9 1344 //Code points greater than U+10FFFF are not valid
Sergunb 0:8918a71cdbe9 1345 if(context->utf8CodePoint >= 0x110000)
Sergunb 0:8918a71cdbe9 1346 valid = FALSE;
Sergunb 0:8918a71cdbe9 1347 }
Sergunb 0:8918a71cdbe9 1348 else
Sergunb 0:8918a71cdbe9 1349 {
Sergunb 0:8918a71cdbe9 1350 //The start byte is not followed by enough continuation bytes
Sergunb 0:8918a71cdbe9 1351 valid = FALSE;
Sergunb 0:8918a71cdbe9 1352 }
Sergunb 0:8918a71cdbe9 1353 }
Sergunb 0:8918a71cdbe9 1354 }
Sergunb 0:8918a71cdbe9 1355
Sergunb 0:8918a71cdbe9 1356 //The function returns TRUE is the specified UTF-8 stream is valid
Sergunb 0:8918a71cdbe9 1357 return valid;
Sergunb 0:8918a71cdbe9 1358 }
Sergunb 0:8918a71cdbe9 1359
Sergunb 0:8918a71cdbe9 1360 #endif
Sergunb 0:8918a71cdbe9 1361