| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506 |
- /*******************************************************************************
- * Copyright (c) 2018, 2024 Wind River Systems, Inc., Ian Craggs and others
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v2.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- *
- * The Eclipse Public License is available at
- * https://www.eclipse.org/legal/epl-2.0/
- * and the Eclipse Distribution License is available at
- * http://www.eclipse.org/org/documents/edl-v10.php.
- *
- * Contributors:
- * Keith Holman - initial implementation and documentation
- * Ian Craggs - use memory tracking
- * Ian Craggs - fix for one MQTT packet spread over >1 ws frame
- * Sven Gambel - move WebSocket proxy support to generic proxy support
- *******************************************************************************/
- #include <stdint.h>
- #include <stdio.h>
- #include <string.h>
- #include "WebSocket.h"
- #include "Base64.h"
- #include "Log.h"
- #if defined(OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x030000000
- #include "openssl/evp.h"
- #else
- #include "SHA1.h"
- #endif
- #include "LinkedList.h"
- #include "MQTTProtocolOut.h"
- #include "SocketBuffer.h"
- #include "StackTrace.h"
- #if defined(__linux__)
- # include <endian.h>
- #elif defined(__APPLE__)
- # include <libkern/OSByteOrder.h>
- # define htobe16(x) OSSwapHostToBigInt16(x)
- # define htobe32(x) OSSwapHostToBigInt32(x)
- # define htobe64(x) OSSwapHostToBigInt64(x)
- # define be16toh(x) OSSwapBigToHostInt16(x)
- # define be32toh(x) OSSwapBigToHostInt32(x)
- # define be64toh(x) OSSwapBigToHostInt64(x)
- #elif defined(__FreeBSD__) || defined(__NetBSD__)
- # include <sys/endian.h>
- #elif defined(_WIN32) || defined(_WIN64)
- # pragma comment(lib, "rpcrt4.lib")
- # include <rpc.h>
- # if !(defined(__MINGW32__))
- # define strncasecmp(s1,s2,c) _strnicmp(s1,s2,c)
- # endif
- # if defined(__MINGW32__)
- # define htonll __builtin_bswap64
- # define ntohll __builtin_bswap64
- # else
- # define htonll(x) _byteswap_uint64(x)
- # define ntohll(x) _byteswap_uint64(x)
- # endif
- # if BYTE_ORDER == LITTLE_ENDIAN
- # define htobe16(x) htons(x)
- # define htobe32(x) htonl(x)
- # define htobe64(x) htonll(x)
- # define be16toh(x) ntohs(x)
- # define be32toh(x) ntohl(x)
- # define be64toh(x) ntohll(x)
- # elif BYTE_ORDER == BIG_ENDIAN
- # define htobe16(x) (x)
- # define htobe32(x) (x)
- # define htobe64(x) (x)
- # define be16toh(x) (x)
- # define be32toh(x) (x)
- # define be64toh(x) (x)
- # else
- # error "unknown endian"
- # endif
- /* For Microsoft Visual Studio < 2015 */
- # if defined(_MSC_VER) && _MSC_VER < 1900
- # define snprintf _snprintf
- # endif
- #endif
- #if defined(OPENSSL)
- #include "SSLSocket.h"
- #include <openssl/rand.h>
- #endif /* defined(OPENSSL) */
- #include "Socket.h"
- #define HTTP_PROTOCOL(x) x ? "https" : "http"
- #if !(defined(_WIN32) || defined(_WIN64))
- #if defined(USE_LIBUUID)
- #include <uuid/uuid.h>
- #else /* if defined(USE_LIBUUID) */
- #include <limits.h>
- #include <stdlib.h>
- #include <time.h>
- /** @brief raw uuid type */
- typedef unsigned char uuid_t[16];
- /**
- * @brief generates a uuid, compatible with RFC 4122, version 4 (random)
- * @note Uses a very insecure algorithm but no external dependencies
- */
- static void uuid_generate( uuid_t out )
- {
- #if defined(OPENSSL)
- int rc = RAND_bytes( out, sizeof(uuid_t));
- if ( !rc )
- #endif /* defined (OPENSSL) */
- {
- /* very insecure, but generates a random uuid */
- int i;
- srand(time(NULL));
- for ( i = 0; i < 16; ++i )
- out[i] = (unsigned char)(rand() % UCHAR_MAX);
- out[6] = (out[6] & 0x0f) | 0x40;
- out[8] = (out[8] & 0x3F) | 0x80;
- }
- }
- /** @brief converts a uuid to a string */
- #if 0
- static void uuid_unparse( uuid_t uu, char *out )
- {
- int i;
- for ( i = 0; i < 16; ++i )
- {
- if ( i == 4 || i == 6 || i == 8 || i == 10 )
- {
- *out = '-';
- ++out;
- }
- out += sprintf( out, "%02x", uu[i] );
- }
- *out = '\0';
- }
- #endif
- #endif /* else if defined(USE_LIBUUID) */
- #endif /* if !(defined(_WIN32) || defined(_WIN64)) */
- #include "Heap.h"
- /** raw websocket frame data */
- struct ws_frame
- {
- size_t len; /**< length of frame */
- size_t pos; /**< current position within the buffer */
- };
- /** Current frame being processed */
- struct ws_frame *last_frame = NULL;
- /** Holds any received websocket frames, to be process */
- static List* in_frames = NULL;
- static char * frame_buffer = NULL;
- static size_t frame_buffer_len = 0;
- static size_t frame_buffer_index = 0;
- static size_t frame_buffer_data_len = 0;
- /* static function declarations */
- static const char *WebSocket_strcasefind(
- const char *buf, const char *str, size_t len);
- static char *WebSocket_getRawSocketData(
- networkHandles *net, size_t bytes, size_t* actual_len, int* rc);
- static void WebSocket_rewindData( void );
- static void WebSocket_pong(
- networkHandles *net, char *app_data, size_t app_data_len);
- static int WebSocket_receiveFrame(networkHandles *net, size_t *actual_len);
- /**
- * calculates the amount of data required for the websocket header
- *
- * this function is used to calculate how much offset is required before calling
- * @p WebSocket_putdatas, as that function will write data before the passed in
- * buffer
- *
- * @param[in,out] net network connection
- * @param[in] mask_data whether to mask the data
- * @param[in] data_len amount of data in the payload
- *
- * @return the size in bytes of the websocket header required
- *
- * @see WebSocket_putdatas
- */
- size_t WebSocket_calculateFrameHeaderSize(networkHandles *net, int mask_data, size_t data_len)
- {
- int ret = 0;
- if ( net && net->websocket )
- {
- if ( data_len < 126u)
- ret = 2; /* header 2 bytes */
- else if ( data_len < 65536u )
- ret = 4; /* for extra 2-bytes for payload length */
- else if ( data_len < 0xFFFFFFFFFFFFFFFF )
- ret = 10; /* for extra 8-bytes for payload length */
- if ( mask_data & 0x1 )
- ret += sizeof(uint32_t); /* for mask */
- }
- return ret;
- }
- /**
- * @brief builds a websocket frame for data transmission
- *
- * write a websocket header and will mask the payload in all the passed in
- * buffers
- *
- * @param[in,out] net network connection
- * @param[in] opcode websocket opcode for the packet
- * @param[in] mask_data whether to mask the data
- * @param[in,out] buf0 first buffer, will write before this
- * @param[in] buf0len size of first buffer
- * @param[in] count number of payload buffers
- * @param[in,out] buffers array of payload buffers
- * @param[in] buflens array of payload buffer sizes
- * @param[in] freeData array indicating to free payload buffers
- *
- * @return amount of data to write to socket
- */
- struct frameData {
- char* wsbuf0;
- size_t wsbuf0len;
- };
- static struct frameData WebSocket_buildFrame(networkHandles* net, int opcode, int mask_data,
- char** pbuf0, size_t* pbuf0len, PacketBuffers* bufs)
- {
- int buf_len = 0u;
- struct frameData rc;
- int new_mask = 0;
- FUNC_ENTRY;
- memset(&rc, '\0', sizeof(rc));
- if ( net->websocket )
- {
- size_t ws_header_size = 0u;
- size_t data_len = 0L;
- int i;
- /* Calculate total length of MQTT buffers */
- data_len = *pbuf0len;
- for (i = 0; i < bufs->count; ++i)
- data_len += bufs->buflens[i];
- /* add space for websocket frame header */
- ws_header_size = WebSocket_calculateFrameHeaderSize(net, mask_data, data_len);
- if (*pbuf0)
- {
- rc.wsbuf0len = *pbuf0len + ws_header_size;
- rc.wsbuf0 = malloc(rc.wsbuf0len);
- if (rc.wsbuf0 == NULL)
- goto exit;
- memcpy(&rc.wsbuf0[ws_header_size], *pbuf0, *pbuf0len);
- }
- else
- {
- rc.wsbuf0 = malloc(ws_header_size);
- if (rc.wsbuf0 == NULL)
- goto exit;
- rc.wsbuf0len = ws_header_size;
- }
- if (mask_data && (bufs->mask[0] == 0))
- {
- /* generate mask, since we are a client */
- #if defined(OPENSSL)
- RAND_bytes(&bufs->mask[0], sizeof(bufs->mask));
- #else /* if defined(OPENSSL) */
- bufs->mask[0] = (rand() % UINT8_MAX);
- bufs->mask[1] = (rand() % UINT8_MAX);
- bufs->mask[2] = (rand() % UINT8_MAX);
- bufs->mask[3] = (rand() % UINT8_MAX);
- #endif /* else if defined(OPENSSL) */
- new_mask = 1;
- }
- /* 1st byte */
- rc.wsbuf0[buf_len] = (char)(1 << 7); /* final flag */
- /* 3 bits reserved for negotiation of protocol */
- rc.wsbuf0[buf_len] |= (char)(opcode & 0x0F); /* op code */
- ++buf_len;
- /* 2nd byte */
- rc.wsbuf0[buf_len] = (char)((mask_data & 0x1) << 7); /* masking bit */
- /* payload length */
- if ( data_len < 126u )
- rc.wsbuf0[buf_len++] |= data_len & 0x7F;
- /* 3rd byte & 4th bytes - extended payload length */
- else if ( data_len < 65536u )
- {
- uint16_t len = htobe16((uint16_t)data_len);
- rc.wsbuf0[buf_len++] |= (126u & 0x7F);
- memcpy( &rc.wsbuf0[buf_len], &len, 2u );
- buf_len += 2;
- }
- else if ( data_len < 0xFFFFFFFFFFFFFFFF )
- {
- uint64_t len = htobe64((uint64_t)data_len);
- rc.wsbuf0[buf_len++] |= (127u & 0x7F);
- memcpy( &rc.wsbuf0[buf_len], &len, 8 );
- buf_len += 8;
- }
- else
- {
- Log(TRACE_PROTOCOL, 1, "Data too large for websocket frame" );
- buf_len = -1;
- }
- if (mask_data)
- {
- size_t idx = 0u;
- /* copy masking key into ws header */
- memcpy( &rc.wsbuf0[buf_len], &bufs->mask, sizeof(uint32_t));
- buf_len += sizeof(uint32_t);
- /* mask packet fixed header */
- for (i = (int)ws_header_size; i < (int)rc.wsbuf0len; ++i, ++idx)
- rc.wsbuf0[i] ^= bufs->mask[idx % 4];
- /* variable data buffers */
- for (i = 0; i < bufs->count; ++i)
- {
- size_t j;
- if (new_mask == 0 && (i == 2 || i == bufs->count-1))
- /* topic (2) and payload (last) buffers are already masked */
- break;
- for ( j = 0u; j < bufs->buflens[i]; ++j, ++idx )
- {
- bufs->buffers[i][j] ^= bufs->mask[idx % 4];
- }
- }
- }
- }
- exit:
- FUNC_EXIT_RC(buf_len);
- return rc;
- }
- static void WebSocket_unmaskData(size_t idx, PacketBuffers* bufs)
- {
- int i;
- FUNC_ENTRY;
- for (i = 0; i < bufs->count; ++i)
- {
- size_t j;
- for (j = 0u; j < bufs->buflens[i]; ++j, ++idx)
- bufs->buffers[i][j] ^= bufs->mask[idx % 4];
- }
- /* show that the mask has been removed */
- bufs->mask[0] = bufs->mask[1] = bufs->mask[2] = bufs->mask[3] = 0;
- FUNC_EXIT;
- }
- /**
- * sends out a websocket request on the given uri
- *
- * @param[in] net network connection
- * @param[in] ssl ssl flag
- * @param[in] uri uri to connect to
- *
- * @retval SOCKET_ERROR on failure
- * @retval 1 on success
- *
- * @see WebSocket_upgrade
- */
- int WebSocket_connect( networkHandles *net, int ssl, const char *uri)
- {
- int rc;
- char *buf = NULL;
- char *headers_buf = NULL;
- const MQTTClient_nameValue *headers = net->httpHeaders;
- int i, buf_len = 0;
- int headers_buf_len = 0;
- size_t hostname_len;
- int port = 80;
- const char *topic = NULL;
- #if defined(_WIN32) || defined(_WIN64)
- UUID uuid;
- #else /* if defined(_WIN32) || defined(_WIN64) */
- uuid_t uuid;
- #endif /* else if defined(_WIN32) || defined(_WIN64) */
- FUNC_ENTRY;
- /* Generate UUID */
- if (net->websocket_key == NULL)
- net->websocket_key = malloc(25u);
- else
- {
- void* newPtr = realloc(net->websocket_key, 25u);
- if (newPtr == NULL)
- {
- free(net->websocket_key);
- net->websocket_key = NULL;
- }
- else
- {
- net->websocket_key = newPtr;
- }
- }
- if (net->websocket_key == NULL)
- {
- rc = PAHO_MEMORY_ERROR;
- goto exit;
- }
- #if defined(_WIN32) || defined(_WIN64)
- ZeroMemory( &uuid, sizeof(UUID) );
- UuidCreate( &uuid );
- Base64_encode( net->websocket_key, 25u, (const b64_data_t*)&uuid, sizeof(UUID) );
- #else /* if defined(_WIN32) || defined(_WIN64) */
- uuid_generate( uuid );
- Base64_encode( net->websocket_key, 25u, uuid, sizeof(uuid_t) );
- #endif /* else if defined(_WIN32) || defined(_WIN64) */
- hostname_len = MQTTProtocol_addressPort(uri, &port, &topic, ssl ? WSS_DEFAULT_PORT : WS_DEFAULT_PORT);
- /* if no topic, use default */
- if ( !topic )
- topic = "/mqtt";
- if ( headers )
- {
- char *headers_buf_cur = NULL;
- while ( headers->name != NULL && headers->value != NULL )
- {
- headers_buf_len += (int)(strlen(headers->name) + strlen(headers->value) + 4);
- headers++;
- }
- headers_buf_len++;
- if ((headers_buf = malloc(headers_buf_len)) == NULL)
- {
- rc = PAHO_MEMORY_ERROR;
- goto exit;
- }
- headers = net->httpHeaders;
- headers_buf_cur = headers_buf;
- while ( headers->name != NULL && headers->value != NULL )
- {
- headers_buf_cur += snprintf(headers_buf_cur, headers_buf_len - (headers_buf_cur - headers_buf),
- "%s: %s\r\n", headers->name, headers->value);
- headers++;
- }
- *headers_buf_cur = '\0';
- }
- for ( i = 0; i < 2; ++i )
- {
- buf_len = snprintf( buf, (size_t)buf_len,
- "GET %s HTTP/1.1\r\n"
- "Host: %.*s:%d\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Origin: %s://%.*s:%d\r\n"
- "Sec-WebSocket-Key: %s\r\n"
- "Sec-WebSocket-Version: 13\r\n"
- "Sec-WebSocket-Protocol: mqtt\r\n"
- "%s"
- "\r\n", topic,
- (int)hostname_len, uri, port,
- #if defined(OPENSSL)
- HTTP_PROTOCOL(net->ssl),
- #else
- HTTP_PROTOCOL(0),
- #endif
-
- (int)hostname_len, uri, port,
- net->websocket_key,
- headers_buf ? headers_buf : "");
- if ( i == 0 && buf_len > 0 )
- {
- ++buf_len; /* need 1 extra byte for ending '\0' */
- if ((buf = malloc( buf_len )) == NULL)
- {
- rc = PAHO_MEMORY_ERROR;
- goto exit;
- }
- }
- }
- if (headers_buf)
- free( headers_buf );
- if ( buf )
- {
- PacketBuffers nulbufs = {0, NULL, NULL, NULL, {0, 0, 0, 0}};
- #if defined(OPENSSL)
- if (net->ssl)
- SSLSocket_putdatas(net->ssl, net->socket, buf, buf_len, nulbufs);
- else
- #endif
- Socket_putdatas(net->socket, buf, buf_len, nulbufs);
- free( buf );
- rc = 1;
- }
- else
- {
- free(net->websocket_key);
- net->websocket_key = NULL;
- rc = SOCKET_ERROR;
- }
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * closes a websocket connection
- *
- * @param[in,out] net structure containing network connection
- * @param[in] status_code websocket close status code
- * @param[in] reason reason for closing connection (optional)
- */
- void WebSocket_close(networkHandles *net, int status_code, const char *reason)
- {
- struct frameData fd;
- PacketBuffers nulbufs = {0, NULL, NULL, NULL, {0, 0, 0, 0}};
- FUNC_ENTRY;
- if ( net->websocket )
- {
- char *buf0;
- size_t buf0len = sizeof(uint16_t);
- uint16_t status_code_be;
- const int mask_data = 1; /* all frames from client must be masked */
- if ( status_code < WebSocket_CLOSE_NORMAL ||
- status_code > WebSocket_CLOSE_TLS_FAIL )
- status_code = WebSocket_CLOSE_GOING_AWAY;
- if ( reason )
- buf0len += strlen(reason);
- buf0 = malloc(buf0len);
- if ( !buf0 )
- goto exit;
- /* encode status code */
- status_code_be = htobe16((uint16_t)status_code);
- memcpy(buf0, &status_code_be, sizeof(uint16_t));
- /* encode reason, if provided */
- if ( reason )
- strcpy( &buf0[sizeof(uint16_t)], reason );
- fd = WebSocket_buildFrame( net, WebSocket_OP_CLOSE, mask_data, &buf0, &buf0len, &nulbufs);
- #if defined(OPENSSL)
- if (net->ssl)
- SSLSocket_putdatas(net->ssl, net->socket, fd.wsbuf0, fd.wsbuf0len, nulbufs);
- else
- #endif
- Socket_putdatas(net->socket, fd.wsbuf0, fd.wsbuf0len, nulbufs);
- free(fd.wsbuf0); /* free temporary ws header */
- /* websocket connection is now closed */
- net->websocket = 0;
- free( buf0 );
- }
- if ( net->websocket_key )
- {
- free( net->websocket_key );
- net->websocket_key = NULL;
- }
- exit:
- FUNC_EXIT;
- }
- /**
- * @brief receives 1 byte from a socket
- *
- * @param[in,out] net network connection
- * @param[out] c byte that was read
- *
- * @retval SOCKET_ERROR on error
- * @retval TCPSOCKET_INTERRUPTED no data available
- * @retval TCPSOCKET_COMPLETE on success
- *
- * @see WebSocket_getdata
- */
- int WebSocket_getch(networkHandles *net, char* c)
- {
- int rc = SOCKET_ERROR;
- FUNC_ENTRY;
- if ( net->websocket )
- {
- struct ws_frame *frame = NULL;
- if ( in_frames && in_frames->first )
- frame = in_frames->first->content;
- if ( !frame || frame->len == frame->pos )
- {
- size_t actual_len = 0u;
- rc = WebSocket_receiveFrame( net, &actual_len);
- if ( rc != TCPSOCKET_COMPLETE )
- goto exit;
- /* we got a frame, let take off the top of queue */
- if ( in_frames->first )
- frame = in_frames->first->content;
- }
- /* set current working frame */
- if (frame && frame->len > frame->pos)
- {
- unsigned char *buf =
- (unsigned char *)frame + sizeof(struct ws_frame);
- *c = buf[frame->pos++];
- rc = TCPSOCKET_COMPLETE;
- }
- }
- #if defined(OPENSSL)
- else if ( net->ssl )
- rc = SSLSocket_getch(net->ssl, net->socket, c);
- #endif
- else
- rc = Socket_getch(net->socket, c);
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
- size_t WebSocket_framePos()
- {
- if ( in_frames && in_frames->first )
- {
- struct ws_frame *frame = in_frames->first->content;
- return frame->pos;
- }
- else
- {
- return 0;
- }
- }
- void WebSocket_framePosSeekTo(size_t pos)
- {
- if ( in_frames && in_frames->first )
- {
- struct ws_frame *frame = in_frames->first->content;
- frame->pos = pos;
- }
- }
- /**
- * @brief receives data from a socket.
- * It should receive all data from the socket that is immediately available.
- * Because it is encapsulated in websocket frames which cannot be
- *
- * @param[in,out] net network connection
- * @param[in] bytes amount of data to get (0 if last packet)
- * @param[out] actual_len amount of data read
- *
- * @return a pointer to the read data
- *
- * @see WebSocket_getch
- */
- char *WebSocket_getdata(networkHandles *net, size_t bytes, size_t* actual_len)
- {
- char *rv = NULL;
- int rc;
- FUNC_ENTRY;
- if ( net->websocket )
- {
- struct ws_frame *frame = NULL;
- if ( bytes == 0u )
- {
- /* done with current frame, move it to last frame */
- if ( in_frames && in_frames->first )
- frame = in_frames->first->content;
- /* return the data from the next frame, if we have one */
- if ( frame && frame->pos == frame->len )
- {
- rv = (char *)frame +
- sizeof(struct ws_frame) + frame->pos;
- *actual_len = frame->len - frame->pos;
- if ( last_frame )
- free( last_frame );
- last_frame = ListDetachHead(in_frames);
- }
- goto exit;
- }
- /* look at the first websocket frame */
- if ( in_frames && in_frames->first )
- frame = in_frames->first->content;
- /* no current frame, so let's go receive one for the network */
- if ( !frame )
- {
- rc = WebSocket_receiveFrame( net, actual_len );
- if ( rc == TCPSOCKET_COMPLETE && in_frames && in_frames->first)
- frame = in_frames->first->content;
- }
- if ( frame )
- {
- rv = (char *)frame + sizeof(struct ws_frame) + frame->pos;
- *actual_len = frame->len - frame->pos; /* use the rest of the frame */
- while (*actual_len < bytes) {
- rc = WebSocket_receiveFrame(net, actual_len);
- if (rc != TCPSOCKET_COMPLETE) {
- goto exit;
- }
- /* refresh pointers */
- frame = in_frames->first->content;
- rv = (char *)frame + sizeof(struct ws_frame) + frame->pos;
- *actual_len = frame->len - frame->pos; /* use the rest of the frame */
- } /* end while */
- if (*actual_len > bytes)
- {
- frame->pos += bytes;
- }
- else if (*actual_len == bytes && in_frames)
- {
- if ( last_frame )
- free( last_frame );
- last_frame = ListDetachHead(in_frames);
- }
- }
- }
- #if defined(OPENSSL)
- else if ( net->ssl )
- rv = SSLSocket_getdata(net->ssl, net->socket, bytes, actual_len, &rc);
- #endif
- else
- rv = Socket_getdata(net->socket, bytes, actual_len, &rc);
- exit:
- FUNC_EXIT_RC(rv);
- return rv;
- }
- void WebSocket_rewindData( void )
- {
- frame_buffer_index = 0;
- }
- /**
- * reads raw socket data for underlying layers
- *
- * @param[in] net network connection
- * @param[in] bytes number of bytes to read, 0 to complete packet
- * @param[in] actual_len amount of data read
- *
- * @return a buffer containing raw data
- */
- char *WebSocket_getRawSocketData(networkHandles *net, size_t bytes, size_t* actual_len, int* rc)
- {
- char *rv = NULL;
- size_t bytes_requested = bytes;
- FUNC_ENTRY;
- if (bytes > 0)
- {
- if (frame_buffer_data_len - frame_buffer_index >= bytes)
- {
- *actual_len = bytes;
- rv = frame_buffer + frame_buffer_index;
- frame_buffer_index += bytes;
- *rc = (int)bytes;
- goto exit;
- }
- else
- {
- bytes = bytes - (frame_buffer_data_len - frame_buffer_index);
- }
- }
- *actual_len = 0;
-
- // not enough data in the buffer, get data from socket
- #if defined(OPENSSL)
- if ( net->ssl )
- rv = SSLSocket_getdata(net->ssl, net->socket, bytes, actual_len, rc);
- else
- #endif
- rv = Socket_getdata(net->socket, bytes, actual_len, rc);
- if (*rc == 0)
- {
- *rc = SOCKET_ERROR;
- goto exit;
- }
- // clear buffer
- if (bytes == 0)
- {
- frame_buffer_index = 0;
- frame_buffer_data_len = 0;
- frame_buffer_len = 0;
-
- if (frame_buffer)
- {
- free (frame_buffer);
- frame_buffer = NULL;
- }
- }
- // append data to the buffer
- else if (rv != NULL && *actual_len != 0U)
- {
- // no buffer allocated
- if (!frame_buffer)
- {
- if ((frame_buffer = (char *)malloc(*actual_len)) == NULL)
- {
- rv = NULL;
- goto exit;
- }
- memcpy(frame_buffer, rv, *actual_len);
- frame_buffer_index = 0;
- frame_buffer_data_len = *actual_len;
- frame_buffer_len = *actual_len;
- }
- // buffer size is big enough
- else if (frame_buffer_data_len + *actual_len < frame_buffer_len)
- {
- memcpy(frame_buffer + frame_buffer_data_len, rv, *actual_len);
- frame_buffer_data_len += *actual_len;
- }
- // resize buffer
- else
- {
- void* newPtr = realloc(frame_buffer, frame_buffer_data_len + *actual_len);
- if (newPtr == NULL)
- {
- free(frame_buffer);
- frame_buffer = NULL;
- rv = NULL;
- goto exit;
- }
- else
- {
- frame_buffer = newPtr;
- }
- frame_buffer_len = frame_buffer_data_len + *actual_len;
- memcpy(frame_buffer + frame_buffer_data_len, rv, *actual_len);
- frame_buffer_data_len += *actual_len;
- }
- SocketBuffer_complete(net->socket);
- }
- else
- goto exit;
- bytes = bytes_requested;
- // if possible, return data from the buffer
- if (bytes > 0)
- {
- if (frame_buffer_data_len - frame_buffer_index >= bytes)
- {
- *actual_len = bytes;
- rv = frame_buffer + frame_buffer_index;
- frame_buffer_index += bytes;
- }
- else
- {
- *actual_len = frame_buffer_data_len - frame_buffer_index;
- rv = frame_buffer + frame_buffer_index;
- frame_buffer_index += *actual_len;
- }
- }
- exit:
- FUNC_EXIT;
- return rv;
- }
- /**
- * sends a "websocket pong" message
- *
- * @param[in] net network connection
- * @param[in] app_data application data to put in payload
- * @param[in] app_data_len application data length
- */
- void WebSocket_pong(networkHandles *net, char *app_data, size_t app_data_len)
- {
- FUNC_ENTRY;
- if ( net->websocket )
- {
- char *buf0 = NULL;
- size_t buf0len = 0;
- int freeData = 0;
- struct frameData fd;
- const int mask_data = 1; /* all frames from client must be masked */
- PacketBuffers appbuf = {1, &app_data, &app_data_len, &freeData, {0, 0, 0, 0}};
- fd = WebSocket_buildFrame( net, WebSocket_OP_PONG, mask_data, &buf0, &buf0len, &appbuf);
- Log(TRACE_PROTOCOL, 1, "Sending WebSocket PONG" );
- #if defined(OPENSSL)
- if (net->ssl)
- SSLSocket_putdatas(net->ssl, net->socket, fd.wsbuf0, fd.wsbuf0len /*header_len + app_data_len*/, appbuf);
- else
- #endif
- Socket_putdatas(net->socket, fd.wsbuf0, fd.wsbuf0len /*header_len + app_data_len*/, appbuf);
- free(fd.wsbuf0);
- free(buf0);
- }
- FUNC_EXIT;
- }
- /**
- * writes data to a socket (websocket header will be prepended if required)
- *
- * @warning buf0 will be expanded (backwords before @p buf0 buffer, to add a
- * websocket frame header to the data if required). So use
- * @p WebSocket_calculateFrameHeader, to determine if extra space is needed
- * before the @p buf0 pointer.
- *
- * @param[in,out] net network connection
- * @param[in,out] buf0 first buffer
- * @param[in] buf0len size of first buffer
- * @param[in] count number of payload buffers
- * @param[in,out] buffers array of paylaod buffers
- * @param[in] buflens array of payload buffer sizes
- * @param[in] freeData array indicating to free payload buffers
- *
- * @return amount of data wrote to socket
- *
- * @see WebSocket_calculateFrameHeaderSize
- */
- int WebSocket_putdatas(networkHandles* net, char** buf0, size_t* buf0len, PacketBuffers* bufs)
- {
- const int mask_data = 1; /* must mask websocket data from client */
- int rc;
- FUNC_ENTRY;
- if (net->websocket)
- {
- struct frameData wsdata;
- wsdata = WebSocket_buildFrame(net, WebSocket_OP_BINARY, mask_data, buf0, buf0len, bufs);
- #if defined(OPENSSL)
- if (net->ssl)
- rc = SSLSocket_putdatas(net->ssl, net->socket, wsdata.wsbuf0, wsdata.wsbuf0len, *bufs);
- else
- #endif
- rc = Socket_putdatas(net->socket, wsdata.wsbuf0, wsdata.wsbuf0len, *bufs);
- if (rc != TCPSOCKET_INTERRUPTED)
- {
- if (mask_data)
- WebSocket_unmaskData(*buf0len, bufs);
- free(wsdata.wsbuf0); /* free temporary ws header */
- }
- }
- else
- {
- #if defined(OPENSSL)
- if (net->ssl)
- rc = SSLSocket_putdatas(net->ssl, net->socket, *buf0, *buf0len, *bufs);
- else
- #endif
- rc = Socket_putdatas(net->socket, *buf0, *buf0len, *bufs);
- }
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * receives incoming socket data and parses websocket frames
- * Copes with socket reads returning partial websocket frames by using the
- * SocketBuffer mechanism.
- *
- * @param[in] net network connection
- * @param[out] actual_len amount of data actually read
- *
- * @retval TCPSOCKET_COMPLETE packet received
- * @retval TCPSOCKET_INTERRUPTED incomplete packet received
- * @retval SOCKET_ERROR an error was encountered
- */
- int WebSocket_receiveFrame(networkHandles *net, size_t *actual_len)
- {
- struct ws_frame *res = NULL;
- int rc = TCPSOCKET_COMPLETE;
- int opcode = 0;
- FUNC_ENTRY;
- if ( !in_frames )
- in_frames = ListInitialize();
- /* see if there is frame currently on queue */
- if ( in_frames->first )
- res = in_frames->first->content;
- //while( !res )
- //{
- opcode = WebSocket_OP_BINARY;
- do
- {
- /* obtain all frames in the sequence */
- int is_final = 0;
- while ( is_final == 0 )
- {
- char *b;
- size_t len = 0u;
- int tmp_opcode;
- int has_mask;
- size_t cur_len = 0u;
- uint8_t mask[4] = { 0u, 0u, 0u, 0u };
- size_t payload_len;
- int rcs; /* socket return code */
- b = WebSocket_getRawSocketData(net, 2u, &len, &rcs);
- if (rcs == SOCKET_ERROR)
- {
- rc = rcs;
- goto exit;
- }
- if ( !b )
- {
- rc = TCPSOCKET_INTERRUPTED;
- goto exit;
- }
- else if (len < 2u )
- {
- rc = TCPSOCKET_INTERRUPTED;
- goto exit;
- }
- /* 1st byte */
- is_final = (b[0] & 0xFF) >> 7;
- tmp_opcode = (b[0] & 0x0F);
- if ( tmp_opcode ) /* not a continuation frame */
- opcode = tmp_opcode;
- /* invalid websocket packet must return error */
- if ( opcode < WebSocket_OP_CONTINUE ||
- opcode > WebSocket_OP_PONG ||
- ( opcode > WebSocket_OP_BINARY &&
- opcode < WebSocket_OP_CLOSE ) )
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- /* 2nd byte */
- has_mask = (b[1] & 0xFF) >> 7;
- payload_len = (b[1] & 0x7F);
- /* determine payload length */
- if ( payload_len == 126 )
- {
- /* If 126, the following 2 bytes interpreted as a
- 16-bit unsigned integer are the payload length. */
- b = WebSocket_getRawSocketData(net, 2u, &len, &rcs);
- if (rcs == SOCKET_ERROR)
- {
- rc = rcs;
- goto exit;
- }
- if ( !b )
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- else if (len < 2u )
- {
- rc = TCPSOCKET_INTERRUPTED;
- goto exit;
- }
- /* convert from big endian 16 to host */
- payload_len = be16toh(*(uint16_t*)b);
- }
- else if ( payload_len == 127 )
- {
- /* If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
- most significant bit MUST be 0) are the payload length */
- b = WebSocket_getRawSocketData(net, 8u, &len, &rcs);
- if (rcs == SOCKET_ERROR)
- {
- rc = rcs;
- goto exit;
- }
- if ( !b )
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- else if (len < 8u )
- {
- rc = TCPSOCKET_INTERRUPTED;
- goto exit;
- }
- /* convert from big-endian 64 to host */
- payload_len = (size_t)be64toh(*(uint64_t*)b);
- }
- if ( has_mask )
- {
- uint8_t mask[4];
- b = WebSocket_getRawSocketData(net, 4u, &len, &rcs);
- if (rcs == SOCKET_ERROR)
- {
- rc = rcs;
- goto exit;
- }
- if ( !b )
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- if (len < 4u )
- {
- rc = TCPSOCKET_INTERRUPTED;
- goto exit;
- }
- memcpy( &mask[0], b, sizeof(uint32_t));
- }
- /* use the socket buffer to read in the whole websocket frame */
- b = WebSocket_getRawSocketData(net, payload_len, &len, &rcs);
- if (rcs == SOCKET_ERROR)
- {
- rc = rcs;
- goto exit;
- }
- if (!b)
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- if (len < payload_len )
- {
- rc = TCPSOCKET_INTERRUPTED;
- goto exit;
- }
- /* unmask data */
- if ( has_mask )
- {
- size_t i;
- for ( i = 0u; i < payload_len; ++i )
- b[i] ^= mask[i % 4];
- }
- if ( res )
- cur_len = res->len;
- if (res == NULL)
- {
- if ((res = malloc( sizeof(struct ws_frame) + cur_len + len)) == NULL)
- {
- rc = PAHO_MEMORY_ERROR;
- goto exit;
- }
- res->pos = 0u;
- } else
- {
- void* newPtr = realloc( res, sizeof(struct ws_frame) + cur_len + len );
- if (newPtr == NULL)
- {
- free(res);
- res = NULL;
- rc = PAHO_MEMORY_ERROR;
- goto exit;
- }
- else
- {
- res = newPtr;
- }
- }
- if (in_frames && in_frames->first)
- in_frames->first->content = res; /* realloc moves the data */
- memcpy( (unsigned char *)res + sizeof(struct ws_frame) + cur_len, b, len );
- res->len = cur_len + len;
- WebSocket_getRawSocketData(net, 0u, &len, &rcs);
- if (rcs == SOCKET_ERROR)
- {
- rc = rcs;
- goto exit;
- }
- }
- if ( opcode == WebSocket_OP_PING || opcode == WebSocket_OP_PONG )
- {
- /* respond to a "ping" with a "pong" */
- if ( opcode == WebSocket_OP_PING )
- WebSocket_pong( net,
- (char *)res + sizeof(struct ws_frame),
- res->len );
- /* discard message */
- free( res );
- res = NULL;
- }
- else if ( opcode == WebSocket_OP_CLOSE )
- {
- /* server end closed websocket connection */
- free( res );
- WebSocket_close( net, WebSocket_CLOSE_GOING_AWAY, NULL );
- rc = SOCKET_ERROR; /* closes socket */
- goto exit;
- }
- } while ( opcode == WebSocket_OP_PING || opcode == WebSocket_OP_PONG );
- //}
- if (in_frames->count == 0)
- ListAppend( in_frames, res, sizeof(struct ws_frame) + res->len);
- *actual_len = res->len - res->pos;
- exit:
- if (rc == TCPSOCKET_INTERRUPTED)
- {
- WebSocket_rewindData();
- }
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * case-insensitive string search
- *
- * similar to @p strcase, but takes a maximum length
- *
- * @param[in] buf buffer to search
- * @param[in] str string to find
- * @param[in] len length of the buffer
- *
- * @retval !NULL location of string found
- * @retval NULL string not found
- */
- const char *WebSocket_strcasefind(const char *buf, const char *str, size_t len)
- {
- const char *res = NULL;
- if ( buf && len > 0u && str )
- {
- const size_t str_len = strlen( str );
- while ( len >= str_len && !res )
- {
- if ( strncasecmp( buf, str, str_len ) == 0 )
- res = buf;
- ++buf;
- --len;
- }
- }
- return res;
- }
- /**
- * releases resources used by the websocket sub-system
- */
- void WebSocket_terminate( void )
- {
- FUNC_ENTRY;
- /* clean up and un-processed websocket frames */
- if ( in_frames )
- {
- struct ws_frame *f = ListDetachHead( in_frames );
- while ( f )
- {
- free( f );
- f = ListDetachHead( in_frames );
- }
- ListFree( in_frames );
- in_frames = NULL;
- }
- if ( last_frame )
- {
- free( last_frame );
- last_frame = NULL;
- }
-
- if ( frame_buffer )
- {
- free( frame_buffer );
- frame_buffer = NULL;
- }
-
- frame_buffer_len = 0;
- frame_buffer_index = 0;
- frame_buffer_data_len = 0;
- Socket_outTerminate();
- #if defined(OPENSSL)
- SSLSocket_terminate();
- #endif
- FUNC_EXIT;
- }
- /**
- * handles the websocket upgrade response
- *
- * @param[in,out] net network connection to upgrade
- *
- * @retval SOCKET_ERROR failed to upgrade network connection
- * @retval TCPSOCKET_INTERRUPTED upgrade not complete, but not failed. Try again
- * @retval 1 socket upgraded to use websockets
- *
- * @see WebSocket_connect
- */
- int WebSocket_upgrade( networkHandles *net )
- {
- static const char *const ws_guid =
- "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- int rc = SOCKET_ERROR;
- FUNC_ENTRY;
- if ( net->websocket_key )
- {
- char ws_key[62u] = {0};
- size_t rcv = 0u;
- char *read_buf;
- #if defined(OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x030000000
- EVP_MD_CTX *sha1_ctx = NULL;
- unsigned char sha_hash[EVP_MAX_MD_SIZE];
- unsigned int sha_len = 0;
- #else
- SHA_CTX ctx;
- unsigned char sha_hash[SHA1_DIGEST_LENGTH];
- #endif
- /* calculate the expected websocket key, expected from server */
- snprintf(ws_key, sizeof(ws_key), "%s%s", net->websocket_key, ws_guid);
- #if defined(OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x030000000
- sha1_ctx = EVP_MD_CTX_new();
- if (sha1_ctx) {
- rc = EVP_DigestInit(sha1_ctx, EVP_sha1());
- if (rc == 0)
- Log(LOG_ERROR, 1, "EVP_DigestInit failed");
- else
- rc = EVP_DigestUpdate(sha1_ctx, ws_key, strlen(ws_key));
- if (rc == 0)
- Log(LOG_ERROR, 1, "EVP_DigestUpdate failed");
- else
- rc = EVP_DigestFinal(sha1_ctx, sha_hash, &sha_len);
- if (rc == 0)
- Log(LOG_ERROR, 1, "EVP_DigestFinal failed");
- EVP_MD_CTX_free(sha1_ctx);
- if (rc == 0)
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- } else
- {
- Log(LOG_ERROR, 1, "EVP_MD_CTX_new failed");
- rc = SOCKET_ERROR;
- goto exit;
- }
- Base64_encode( ws_key, sizeof(ws_key), sha_hash, sha_len);
- #else
- SHA1_Init( &ctx );
- SHA1_Update( &ctx, ws_key, strlen(ws_key));
- SHA1_Final( sha_hash, &ctx );
- Base64_encode( ws_key, sizeof(ws_key), sha_hash, SHA1_DIGEST_LENGTH );
- #endif
- read_buf = WebSocket_getRawSocketData( net, 12u, &rcv, &rc);
- if (rc == SOCKET_ERROR)
- goto exit;
- if ((read_buf == NULL) || rcv < 12u)
- {
- Log(TRACE_PROTOCOL, 1, "WebSocket upgrade read not complete %lu", rcv );
- rc = TCPSOCKET_INTERRUPTED;
- goto exit;
- }
- if (strncmp( read_buf, "HTTP/1.1", 8u ) == 0)
- {
- if (strncmp( &read_buf[9], "101", 3u ) != 0)
- {
- Log(TRACE_PROTOCOL, 1, "WebSocket HTTP rc %.3s", &read_buf[9]);
- rc = SOCKET_ERROR;
- goto exit;
- }
- }
- if (strncmp( read_buf, "HTTP/1.1 101", 12u ) == 0)
- {
- const char *p;
- read_buf = WebSocket_getRawSocketData(net, 1024u, &rcv, &rc);
- if (rc == SOCKET_ERROR)
- goto exit;
- /* Did we read the whole response? */
- if (read_buf && rcv > 4 && memcmp(&read_buf[rcv-4], "\r\n\r\n", 4) != 0)
- {
- Log(TRACE_PROTOCOL, -1, "WebSocket HTTP upgrade response read not complete %lu", rcv);
- rc = SOCKET_ERROR;
- goto exit;
- }
- /* check for upgrade */
- p = WebSocket_strcasefind(
- read_buf, "Connection", rcv );
- if ( p )
- {
- const char *eol;
- eol = memchr( p, '\n', rcv-(read_buf-p) );
- if ( eol )
- p = WebSocket_strcasefind(
- p, "Upgrade", eol - p);
- else
- p = NULL;
- }
- /* check key hash */
- if ( p )
- p = WebSocket_strcasefind( read_buf,
- "sec-websocket-accept", rcv );
- if ( p )
- {
- const char *eol;
- eol = memchr( p, '\n', rcv-(read_buf-p) );
- if ( eol )
- {
- p = memchr( p, ':', eol-p );
- if ( p )
- {
- size_t hash_len = eol-p-1;
- while ( *p == ':' || *p == ' ' )
- {
- ++p;
- --hash_len;
- }
- if ( strncmp( p, ws_key, hash_len ) != 0 )
- p = NULL;
- }
- }
- else
- p = NULL;
- }
- if ( p )
- {
- net->websocket = 1;
- Log(TRACE_PROTOCOL, 1, "WebSocket connection upgraded" );
- rc = 1;
- }
- else
- {
- Log(TRACE_PROTOCOL, 1, "WebSocket failed to upgrade connection" );
- rc = SOCKET_ERROR;
- }
- if ( net->websocket_key )
- {
- free(net->websocket_key);
- net->websocket_key = NULL;
- }
- /* indicate that we done with the packet */
- WebSocket_getRawSocketData( net, 0u, &rcv, &rc);
- }
- }
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
|