From 40c6cbb3abf1d8531a3897ee88f640cf14d141bf Mon Sep 17 00:00:00 2001 From: David Cermak Date: Tue, 25 Sep 2018 10:34:04 +0200 Subject: [PATCH] tcp_transport: renamed transport related header files to esp_ prefixed to avoid collisions tcp_transport component used public header files such as 'transport.h', etc. which are too generic and might collide with user or user libraries headers This change closes #2417 --- components/esp_http_client/esp_http_client.c | 7 +- .../esp_http_client/lib/include/http_utils.h | 2 +- components/mqtt/CMakeLists.txt | 11 +- components/mqtt/esp-mqtt | 2 +- components/tcp_transport/CMakeLists.txt | 11 +- .../include/{transport.h => esp_transport.h} | 0 .../{transport_ssl.h => esp_transport_ssl.h} | 2 +- .../{transport_tcp.h => esp_transport_tcp.h} | 2 +- ...ransport_utils.h => esp_transport_utils.h} | 0 .../tcp_transport/include/esp_transport_ws.h | 46 +++ components/tcp_transport/transport.c | 4 +- components/tcp_transport/transport_ssl.c | 6 +- components/tcp_transport/transport_tcp.c | 4 +- components/tcp_transport/transport_utils.c | 2 +- components/tcp_transport/transport_ws.c | 264 ++++++++++++++++++ 15 files changed, 336 insertions(+), 27 deletions(-) rename components/tcp_transport/include/{transport.h => esp_transport.h} (100%) rename components/tcp_transport/include/{transport_ssl.h => esp_transport_ssl.h} (98%) rename components/tcp_transport/include/{transport_tcp.h => esp_transport_tcp.h} (97%) rename components/tcp_transport/include/{transport_utils.h => esp_transport_utils.h} (100%) create mode 100644 components/tcp_transport/include/esp_transport_ws.h create mode 100644 components/tcp_transport/transport_ws.c diff --git a/components/esp_http_client/esp_http_client.c b/components/esp_http_client/esp_http_client.c index ae174f137b..163cc41a67 100644 --- a/components/esp_http_client/esp_http_client.c +++ b/components/esp_http_client/esp_http_client.c @@ -19,17 +19,16 @@ #include "esp_log.h" #include "http_header.h" -#include "transport.h" -#include "transport_tcp.h" +#include "esp_transport.h" +#include "esp_transport_tcp.h" #include "http_utils.h" #include "http_auth.h" #include "sdkconfig.h" -#include "transport.h" #include "esp_http_client.h" #include "errno.h" #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS -#include "transport_ssl.h" +#include "esp_transport_ssl.h" #endif static const char *TAG = "HTTP_CLIENT"; diff --git a/components/esp_http_client/lib/include/http_utils.h b/components/esp_http_client/lib/include/http_utils.h index 3d1728505d..1a224984e4 100644 --- a/components/esp_http_client/lib/include/http_utils.h +++ b/components/esp_http_client/lib/include/http_utils.h @@ -16,7 +16,7 @@ #ifndef _HTTP_UTILS_H_ #define _HTTP_UTILS_H_ #include -#include "transport_utils.h" +#include "esp_transport_utils.h" /** * @brief Assign new_str to *str pointer, and realloc *str if it not NULL * diff --git a/components/mqtt/CMakeLists.txt b/components/mqtt/CMakeLists.txt index d100f80a0e..85f63cc9a8 100644 --- a/components/mqtt/CMakeLists.txt +++ b/components/mqtt/CMakeLists.txt @@ -3,17 +3,8 @@ set(COMPONENT_PRIV_INCLUDEDIRS "esp-mqtt/lib/include") set(COMPONENT_SRCS "esp-mqtt/mqtt_client.c" "esp-mqtt/lib/mqtt_msg.c" "esp-mqtt/lib/mqtt_outbox.c" - "esp-mqtt/lib/platform_esp32_idf.c" - "esp-mqtt/lib/transport_ws.c") + "esp-mqtt/lib/platform_esp32_idf.c") set(COMPONENT_REQUIRES lwip nghttp mbedtls tcp_transport) register_component() - -if(GCC_NOT_5_2_0) - # Temporary suppress "format-overflow" warning until we are fixed in esp-mqtt repo - set_source_files_properties( - esp-mqtt/lib/transport_ws.c - PROPERTIES COMPILE_FLAGS - -Wno-format-overflow) -endif() diff --git a/components/mqtt/esp-mqtt b/components/mqtt/esp-mqtt index bcb38e45f5..ca893e2c23 160000 --- a/components/mqtt/esp-mqtt +++ b/components/mqtt/esp-mqtt @@ -1 +1 @@ -Subproject commit bcb38e45f521085f997439a8b6c4ead34dff9043 +Subproject commit ca893e2c23a104cead0bb756a11af7b80464c2d4 diff --git a/components/tcp_transport/CMakeLists.txt b/components/tcp_transport/CMakeLists.txt index 8843ac0416..3fe93384cd 100644 --- a/components/tcp_transport/CMakeLists.txt +++ b/components/tcp_transport/CMakeLists.txt @@ -1,10 +1,19 @@ set(COMPONENT_SRCS "transport.c" "transport_ssl.c" "transport_tcp.c" + "transport_ws.c" "transport_utils.c") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_REQUIRES lwip esp-tls) -register_component() \ No newline at end of file +register_component() + +if(GCC_NOT_5_2_0) + # Temporary suppress "format-overflow" warning until it is fixed + set_source_files_properties( + transport_ws.c + PROPERTIES COMPILE_FLAGS + -Wno-format-overflow) +endif() diff --git a/components/tcp_transport/include/transport.h b/components/tcp_transport/include/esp_transport.h similarity index 100% rename from components/tcp_transport/include/transport.h rename to components/tcp_transport/include/esp_transport.h diff --git a/components/tcp_transport/include/transport_ssl.h b/components/tcp_transport/include/esp_transport_ssl.h similarity index 98% rename from components/tcp_transport/include/transport_ssl.h rename to components/tcp_transport/include/esp_transport_ssl.h index a00b36babc..b8353ea8ff 100644 --- a/components/tcp_transport/include/transport_ssl.h +++ b/components/tcp_transport/include/esp_transport_ssl.h @@ -15,7 +15,7 @@ #ifndef _TRANSPORT_SSL_H_ #define _TRANSPORT_SSL_H_ -#include "transport.h" +#include "esp_transport.h" #ifdef __cplusplus extern "C" { diff --git a/components/tcp_transport/include/transport_tcp.h b/components/tcp_transport/include/esp_transport_tcp.h similarity index 97% rename from components/tcp_transport/include/transport_tcp.h rename to components/tcp_transport/include/esp_transport_tcp.h index e1cc1d3432..182ea95634 100644 --- a/components/tcp_transport/include/transport_tcp.h +++ b/components/tcp_transport/include/esp_transport_tcp.h @@ -15,7 +15,7 @@ #ifndef _TRANSPORT_TCP_H_ #define _TRANSPORT_TCP_H_ -#include "transport.h" +#include "esp_transport.h" #ifdef __cplusplus extern "C" { diff --git a/components/tcp_transport/include/transport_utils.h b/components/tcp_transport/include/esp_transport_utils.h similarity index 100% rename from components/tcp_transport/include/transport_utils.h rename to components/tcp_transport/include/esp_transport_utils.h diff --git a/components/tcp_transport/include/esp_transport_ws.h b/components/tcp_transport/include/esp_transport_ws.h new file mode 100644 index 0000000000..76653b0d2e --- /dev/null +++ b/components/tcp_transport/include/esp_transport_ws.h @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms and conditions defined in + * file 'LICENSE', which is part of this source code package. + * Tuan PM + */ + +#ifndef _TRANSPORT_WS_H_ +#define _TRANSPORT_WS_H_ + +#include "esp_transport.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WS_FIN 0x80 +#define WS_OPCODE_TEXT 0x01 +#define WS_OPCODE_BINARY 0x02 +#define WS_OPCODE_CLOSE 0x08 +#define WS_OPCODE_PING 0x09 +#define WS_OPCODE_PONG 0x0a +// Second byte +#define WS_MASK 0x80 +#define WS_SIZE16 126 +#define WS_SIZE64 127 +#define MAX_WEBSOCKET_HEADER_SIZE 10 +#define WS_RESPONSE_OK 101 + +/** + * @brief Create TCP transport + * + * @return + * - transport + * - NULL + */ +transport_handle_t transport_ws_init(transport_handle_t parent_handle); + +void transport_ws_set_path(transport_handle_t t, const char *path); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/tcp_transport/transport.c b/components/tcp_transport/transport.c index c23ed8d342..6597b53d4e 100644 --- a/components/tcp_transport/transport.c +++ b/components/tcp_transport/transport.c @@ -19,8 +19,8 @@ #include "rom/queue.h" #include "esp_log.h" -#include "transport.h" -#include "transport_utils.h" +#include "esp_transport.h" +#include "esp_transport_utils.h" static const char *TAG = "TRANSPORT"; diff --git a/components/tcp_transport/transport_ssl.c b/components/tcp_transport/transport_ssl.c index 8e995764e6..cd242ca5bf 100644 --- a/components/tcp_transport/transport_ssl.c +++ b/components/tcp_transport/transport_ssl.c @@ -21,9 +21,9 @@ #include "esp_log.h" #include "esp_system.h" -#include "transport.h" -#include "transport_ssl.h" -#include "transport_utils.h" +#include "esp_transport.h" +#include "esp_transport_ssl.h" +#include "esp_transport_utils.h" static const char *TAG = "TRANS_SSL"; diff --git a/components/tcp_transport/transport_tcp.c b/components/tcp_transport/transport_tcp.c index 2de0dab874..0c75627eba 100644 --- a/components/tcp_transport/transport_tcp.c +++ b/components/tcp_transport/transport_tcp.c @@ -23,8 +23,8 @@ #include "esp_system.h" #include "esp_err.h" -#include "transport_utils.h" -#include "transport.h" +#include "esp_transport_utils.h" +#include "esp_transport.h" static const char *TAG = "TRANS_TCP"; diff --git a/components/tcp_transport/transport_utils.c b/components/tcp_transport/transport_utils.c index 95174cf59d..36699a9242 100644 --- a/components/tcp_transport/transport_utils.c +++ b/components/tcp_transport/transport_utils.c @@ -4,7 +4,7 @@ #include #include -#include "transport_utils.h" +#include "esp_transport_utils.h" void transport_utils_ms_to_timeval(int timeout_ms, struct timeval *tv) { diff --git a/components/tcp_transport/transport_ws.c b/components/tcp_transport/transport_ws.c new file mode 100644 index 0000000000..9e54a93200 --- /dev/null +++ b/components/tcp_transport/transport_ws.c @@ -0,0 +1,264 @@ +#include +#include +#include + +#include "esp_log.h" +#include "esp_transport.h" +#include "esp_transport_tcp.h" +#include "esp_transport_ws.h" +#include "esp_transport_utils.h" +#include "mbedtls/base64.h" +#include "mbedtls/sha1.h" + +static const char *TAG = "TRANSPORT_WS"; + +#define DEFAULT_WS_BUFFER (1024) + +typedef struct { + char *path; + char *buffer; + transport_handle_t parent; +} transport_ws_t; + +transport_handle_t ws_transport_get_payload_transport_handle(transport_handle_t t) +{ + transport_ws_t *ws = transport_get_context_data(t); + return ws->parent; +} + +static char *trimwhitespace(const char *str) +{ + char *end; + + // Trim leading space + while (isspace((unsigned char)*str)) str++; + + if (*str == 0) { + return (char *)str; + } + + // Trim trailing space + end = (char *)(str + strlen(str) - 1); + while (end > str && isspace((unsigned char)*end)) end--; + + // Write new null terminator + *(end + 1) = 0; + + return (char *)str; +} + + +static char *get_http_header(const char *buffer, const char *key) +{ + char *found = strstr(buffer, key); + if (found) { + found += strlen(key); + char *found_end = strstr(found, "\r\n"); + if (found_end) { + found_end[0] = 0;//terminal string + + return trimwhitespace(found); + } + } + return NULL; +} + +static int ws_connect(transport_handle_t t, const char *host, int port, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + if (transport_connect(ws->parent, host, port, timeout_ms) < 0) { + ESP_LOGE(TAG, "Error connect to ther server"); + } + unsigned char random_key[16] = { 0 }, client_key[32] = {0}; + int i; + for (i = 0; i < sizeof(random_key); i++) { + random_key[i] = rand() & 0xFF; + } + size_t outlen = 0; + mbedtls_base64_encode(client_key, 32, &outlen, random_key, 16); + int len = snprintf(ws->buffer, DEFAULT_WS_BUFFER, + "GET %s HTTP/1.1\r\n" + "Connection: Upgrade\r\n" + "Host: %s:%d\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Protocol: mqtt\r\n" + "Sec-WebSocket-Key: %s\r\n" + "User-Agent: ESP32 MQTT Client\r\n\r\n", + ws->path, + host, port, + client_key); + ESP_LOGD(TAG, "Write upgrate request\r\n%s", ws->buffer); + if (transport_write(ws->parent, ws->buffer, len, timeout_ms) <= 0) { + ESP_LOGE(TAG, "Error write Upgrade header %s", ws->buffer); + return -1; + } + if ((len = transport_read(ws->parent, ws->buffer, DEFAULT_WS_BUFFER, timeout_ms)) <= 0) { + ESP_LOGE(TAG, "Error read response for Upgrade header %s", ws->buffer); + return -1; + } + char *server_key = get_http_header(ws->buffer, "Sec-WebSocket-Accept:"); + if (server_key == NULL) { + ESP_LOGE(TAG, "Sec-WebSocket-Accept not found"); + return -1; + } + + unsigned char client_key_b64[64], valid_client_key[20], accept_key[32] = {0}; + int key_len = sprintf((char*)client_key_b64, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", (char*)client_key); + mbedtls_sha1_ret(client_key_b64, (size_t)key_len, valid_client_key); + mbedtls_base64_encode(accept_key, 32, &outlen, valid_client_key, 20); + accept_key[outlen] = 0; + ESP_LOGD(TAG, "server key=%s, send_key=%s, accept_key=%s", (char *)server_key, (char*)client_key, accept_key); + if (strcmp((char*)accept_key, (char*)server_key) != 0) { + ESP_LOGE(TAG, "Invalid websocket key"); + return -1; + } + return 0; +} + +static int ws_write(transport_handle_t t, const char *buff, int len, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + char ws_header[MAX_WEBSOCKET_HEADER_SIZE]; + char *mask; + int header_len = 0, i; + char *buffer = (char *)buff; + int poll_write; + if ((poll_write = transport_poll_write(ws->parent, timeout_ms)) <= 0) { + return poll_write; + } + + ws_header[header_len++] = WS_OPCODE_BINARY | WS_FIN; + + // NOTE: no support for > 16-bit sized messages + if (len > 125) { + ws_header[header_len++] = WS_SIZE16 | WS_MASK; + ws_header[header_len++] = (uint8_t)(len >> 8); + ws_header[header_len++] = (uint8_t)(len & 0xFF); + } else { + ws_header[header_len++] = (uint8_t)(len | WS_MASK); + } + mask = &ws_header[header_len]; + ws_header[header_len++] = rand() & 0xFF; + ws_header[header_len++] = rand() & 0xFF; + ws_header[header_len++] = rand() & 0xFF; + ws_header[header_len++] = rand() & 0xFF; + + for (i = 0; i < len; ++i) { + buffer[i] = (buffer[i] ^ mask[i % 4]); + } + if (transport_write(ws->parent, ws_header, header_len, timeout_ms) != header_len) { + ESP_LOGE(TAG, "Error write header"); + return -1; + } + return transport_write(ws->parent, buffer, len, timeout_ms); +} + +static int ws_read(transport_handle_t t, char *buffer, int len, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + int payload_len; + int payload_len_buff = len; + char *data_ptr = buffer, opcode, mask, *mask_key = NULL; + int rlen; + int poll_read; + if ((poll_read = transport_poll_read(ws->parent, timeout_ms)) <= 0) { + return poll_read; + } + if ((rlen = transport_read(ws->parent, buffer, len, timeout_ms)) <= 0) { + ESP_LOGE(TAG, "Error read data"); + return rlen; + } + opcode = (*data_ptr & 0x0F); + data_ptr ++; + mask = ((*data_ptr >> 7) & 0x01); + payload_len = (*data_ptr & 0x7F); + data_ptr++; + ESP_LOGD(TAG, "Opcode: %d, mask: %d, len: %d\r\n", opcode, mask, payload_len); + if (payload_len == 126) { + // headerLen += 2; + payload_len = data_ptr[0] << 8 | data_ptr[1]; + payload_len_buff = len - 4; + data_ptr += 2; + } else if (payload_len == 127) { + // headerLen += 8; + + if (data_ptr[0] != 0 || data_ptr[1] != 0 || data_ptr[2] != 0 || data_ptr[3] != 0) { + // really too big! + payload_len = 0xFFFFFFFF; + } else { + payload_len = data_ptr[4] << 24 | data_ptr[5] << 16 | data_ptr[6] << 8 | data_ptr[7]; + } + data_ptr += 8; + payload_len_buff = len - 10; + } + if (payload_len > payload_len_buff) { + ESP_LOGD(TAG, "Actual data received (%d) are longer than mqtt buffer (%d)", payload_len, payload_len_buff); + payload_len = payload_len_buff; + } + + if (mask) { + mask_key = data_ptr; + data_ptr += 4; + for (int i = 0; i < payload_len; i++) { + buffer[i] = (data_ptr[i] ^ mask_key[i % 4]); + } + } else { + memmove(buffer, data_ptr, payload_len); + } + return payload_len; +} + +static int ws_poll_read(transport_handle_t t, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + return transport_poll_read(ws->parent, timeout_ms); +} + +static int ws_poll_write(transport_handle_t t, int timeout_ms) +{ + transport_ws_t *ws = transport_get_context_data(t); + return transport_poll_write(ws->parent, timeout_ms);; +} + +static int ws_close(transport_handle_t t) +{ + transport_ws_t *ws = transport_get_context_data(t); + return transport_close(ws->parent); +} + +static esp_err_t ws_destroy(transport_handle_t t) +{ + transport_ws_t *ws = transport_get_context_data(t); + free(ws->buffer); + free(ws->path); + free(ws); + return 0; +} +void transport_ws_set_path(transport_handle_t t, const char *path) +{ + transport_ws_t *ws = transport_get_context_data(t); + ws->path = realloc(ws->path, strlen(path) + 1); + strcpy(ws->path, path); +} +transport_handle_t transport_ws_init(transport_handle_t parent_handle) +{ + transport_handle_t t = transport_init(); + transport_ws_t *ws = calloc(1, sizeof(transport_ws_t)); + TRANSPORT_MEM_CHECK(TAG, ws, return NULL); + ws->parent = parent_handle; + + ws->path = strdup("/"); + TRANSPORT_MEM_CHECK(TAG, ws->path, return NULL); + ws->buffer = malloc(DEFAULT_WS_BUFFER); + TRANSPORT_MEM_CHECK(TAG, ws->buffer, { + free(ws->path); + free(ws); + return NULL; + }); + + transport_set_func(t, ws_connect, ws_read, ws_write, ws_close, ws_poll_read, ws_poll_write, ws_destroy, ws_transport_get_payload_transport_handle); + transport_set_context_data(t, ws); + return t; +} +