| /******************************************************************************* |
| * Copyright (c) 2018, 2020 Wind River Systems, Inc. and others. All Rights Reserved. |
| * |
| * 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 |
| *******************************************************************************/ |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| // for timeout process in WebSocket_proxy_connect() |
| #include <time.h> |
| #if defined(_WIN32) || defined(_WIN64) |
| #include <windows.h> |
| #else |
| #include <unistd.h> |
| #endif |
| |
| #include "WebSocket.h" |
| |
| #include "Base64.h" |
| #include "Log.h" |
| #include "SHA1.h" |
| #include "LinkedList.h" |
| #include "MQTTProtocolOut.h" |
| #include "SocketBuffer.h" |
| #include "StackTrace.h" |
| |
| #if defined(__MINGW32__) |
| #define htonll __builtin_bswap64 |
| #define ntohll __builtin_bswap64 |
| #endif |
| |
| #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> |
| # define strncasecmp(s1,s2,c) _strnicmp(s1,s2,c) |
| # define htonll(x) _byteswap_uint64(x) |
| # define ntohll(x) _byteswap_uint64(x) |
| |
| # 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(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 |
| */ |
| 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 */ |
| 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 /* else if defined(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] uri uri to connect to |
| * |
| * @retval SOCKET_ERROR on failure |
| * @retval 1 on success |
| * |
| * @see WebSocket_upgrade |
| */ |
| int WebSocket_connect( networkHandles *net, 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 |
| net->websocket_key = realloc(net->websocket_key, 25u); |
| 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, 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 += sprintf(headers_buf_cur, "%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 ) |
| { |
| const int 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) { |
| const int 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; |
| |
| 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; |
| |
| 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 |
| { |
| frame_buffer = realloc(frame_buffer, frame_buffer_data_len + *actual_len); |
| 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 |
| { |
| if ((res = realloc( res, sizeof(struct ws_frame) + cur_len + len )) == NULL) |
| { |
| rc = PAHO_MEMORY_ERROR; |
| goto exit; |
| } |
| } |
| 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 ) |
| { |
| SHA_CTX ctx; |
| char ws_key[62u] = { 0 }; |
| unsigned char sha_hash[SHA1_DIGEST_LENGTH]; |
| size_t rcv = 0u; |
| char *read_buf; |
| |
| /* calculate the expected websocket key, expected from server */ |
| snprintf( ws_key, sizeof(ws_key), "%s%s", net->websocket_key, ws_guid ); |
| 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 ); |
| |
| rc = TCPSOCKET_INTERRUPTED; |
| 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 ); |
| 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; |
| } |
| |
| /** |
| * Notify the IP address and port of the endpoint to proxy, and wait connection to endpoint. |
| * |
| * @param[in] net network connection to proxy. |
| * @param[in] ssl enable ssl. |
| * @param[in] hostname hostname of endpoint. |
| * |
| * @retval SOCKET_ERROR failed to network connection |
| * @retval 0 connection to endpoint |
| * |
| */ |
| int WebSocket_proxy_connect( networkHandles *net, int ssl, const char *hostname) |
| { |
| int port, i, rc = 0, buf_len=0; |
| char *buf = NULL; |
| size_t hostname_len, actual_len = 0; |
| time_t current, timeout; |
| PacketBuffers nulbufs = {0, NULL, NULL, NULL, {0, 0, 0, 0}}; |
| |
| FUNC_ENTRY; |
| hostname_len = MQTTProtocol_addressPort(hostname, &port, NULL, WS_DEFAULT_PORT); |
| for ( i = 0; i < 2; ++i ) { |
| #if defined(OPENSSL) |
| if(ssl) { |
| if (net->https_proxy_auth) { |
| buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n" |
| "Host: %.*s\r\n" |
| "Proxy-authorization: Basic %s\r\n" |
| "\r\n", |
| (int)hostname_len, hostname, port, |
| (int)hostname_len, hostname, net->https_proxy_auth); |
| } |
| else { |
| buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n" |
| "Host: %.*s\r\n" |
| "\r\n", |
| (int)hostname_len, hostname, port, |
| (int)hostname_len, hostname); |
| } |
| } |
| else { |
| #endif |
| if (net->http_proxy_auth) { |
| buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n" |
| "Host: %.*s\r\n" |
| "Proxy-authorization: Basic %s\r\n" |
| "\r\n", |
| (int)hostname_len, hostname, port, |
| (int)hostname_len, hostname, net->http_proxy_auth); |
| } |
| else { |
| buf_len = snprintf( buf, (size_t)buf_len, "CONNECT %.*s:%d HTTP/1.1\r\n" |
| "Host: %.*s\r\n" |
| "\r\n", |
| (int)hostname_len, hostname, port, |
| (int)hostname_len, hostname); |
| } |
| #if defined(OPENSSL) |
| } |
| #endif |
| if ( i==0 && buf_len > 0 ) { |
| ++buf_len; |
| if ((buf = malloc( buf_len )) == NULL) |
| { |
| rc = PAHO_MEMORY_ERROR; |
| goto exit; |
| } |
| |
| } |
| } |
| Log(TRACE_PROTOCOL, -1, "WebSocket_proxy_connect: \"%s\"", buf); |
| |
| Socket_putdatas(net->socket, buf, buf_len, nulbufs); |
| free(buf); |
| buf = NULL; |
| |
| time(&timeout); |
| timeout += (time_t)10; |
| |
| while(1) { |
| buf = Socket_getdata(net->socket, (size_t)12, &actual_len, &rc); |
| if(actual_len) { |
| if ( (strncmp( buf, "HTTP/1.0 200", 12 ) != 0) && (strncmp( buf, "HTTP/1.1 200", 12 ) != 0) ) |
| rc = SOCKET_ERROR; |
| break; |
| } |
| else { |
| time(¤t); |
| if(current > timeout) { |
| rc = SOCKET_ERROR; |
| break; |
| } |
| #if defined(_WIN32) || defined(_WIN64) |
| Sleep(250); |
| #else |
| usleep(250000); |
| #endif |
| } |
| } |
| |
| /* flush the SocketBuffer */ |
| actual_len = 1; |
| while (actual_len) |
| { |
| int rc1; |
| |
| buf = Socket_getdata(net->socket, (size_t)1, &actual_len, &rc1); |
| } |
| |
| exit: |
| FUNC_EXIT_RC(rc); |
| return rc; |
| } |