Webserver+3d print
cyclone_tcp/web_socket/web_socket_misc.c@0:8918a71cdbe9, 2017-02-04 (annotated)
- Committer:
- Sergunb
- Date:
- Sat Feb 04 18:15:49 2017 +0000
- Revision:
- 0:8918a71cdbe9
nothing else
Who changed what in which revision?
User | Revision | Line number | New 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 |