mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-08-06 15:15:15 +08:00
Merge branch 'feature/update_tls_from_idf_v3.3' into 'release/v3.3'
feat(tls): update tls from idf v3.3 (backport v3.3) See merge request sdk/ESP8266_RTOS_SDK!1347
This commit is contained in:
@ -14,22 +14,22 @@ menu "ESP-TLS"
|
|||||||
|
|
||||||
config ESP_TLS_SERVER
|
config ESP_TLS_SERVER
|
||||||
bool "Enable ESP-TLS Server"
|
bool "Enable ESP-TLS Server"
|
||||||
depends on ESP_TLS_USING_MBEDTLS
|
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable support for creating server side SSL/TLS session, uses the mbedtls crypto library
|
Enable support for creating server side SSL/TLS session, available for mbedTLS
|
||||||
|
as well as wolfSSL TLS library.
|
||||||
|
|
||||||
config ESP_TLS_PSK_VERIFICATION
|
config ESP_TLS_PSK_VERIFICATION
|
||||||
bool "Enable PSK verification"
|
bool "Enable PSK verification"
|
||||||
depends on ESP_TLS_USING_MBEDTLS
|
select MBEDTLS_PSK_MODES if ESP_TLS_USING_MBEDTLS
|
||||||
select MBEDTLS_PSK_MODES
|
select MBEDTLS_KEY_EXCHANGE_PSK if ESP_TLS_USING_MBEDTLS
|
||||||
select MBEDTLS_KEY_EXCHANGE_PSK
|
select MBEDTLS_KEY_EXCHANGE_DHE_PSK if ESP_TLS_USING_MBEDTLS
|
||||||
select MBEDTLS_KEY_EXCHANGE_DHE_PSK
|
select MBEDTLS_KEY_EXCHANGE_ECDHE_PSK if ESP_TLS_USING_MBEDTLS
|
||||||
select MBEDTLS_KEY_EXCHANGE_ECDHE_PSK
|
select MBEDTLS_KEY_EXCHANGE_RSA_PSK if ESP_TLS_USING_MBEDTLS
|
||||||
select MBEDTLS_KEY_EXCHANGE_RSA_PSK
|
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable support for pre shared key ciphers, uses the mbedtls crypto library
|
Enable support for pre shared key ciphers, supported for both mbedTLS as well as
|
||||||
|
wolfSSL TLS library.
|
||||||
|
|
||||||
config ESP_WOLFSSL_SMALL_CERT_VERIFY
|
config ESP_WOLFSSL_SMALL_CERT_VERIFY
|
||||||
bool "Enable SMALL_CERT_VERIFY"
|
bool "Enable SMALL_CERT_VERIFY"
|
||||||
|
@ -60,6 +60,10 @@ static const char *TAG = "esp-tls";
|
|||||||
#define _esp_tls_read esp_wolfssl_read
|
#define _esp_tls_read esp_wolfssl_read
|
||||||
#define _esp_tls_write esp_wolfssl_write
|
#define _esp_tls_write esp_wolfssl_write
|
||||||
#define _esp_tls_conn_delete esp_wolfssl_conn_delete
|
#define _esp_tls_conn_delete esp_wolfssl_conn_delete
|
||||||
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
|
#define _esp_tls_server_session_create esp_wolfssl_server_session_create
|
||||||
|
#define _esp_tls_server_session_delete esp_wolfssl_server_session_delete
|
||||||
|
#endif /* CONFIG_ESP_TLS_SERVER */
|
||||||
#define _esp_tls_get_bytes_avail esp_wolfssl_get_bytes_avail
|
#define _esp_tls_get_bytes_avail esp_wolfssl_get_bytes_avail
|
||||||
#define _esp_tls_init_global_ca_store esp_wolfssl_init_global_ca_store
|
#define _esp_tls_init_global_ca_store esp_wolfssl_init_global_ca_store
|
||||||
#define _esp_tls_set_global_ca_store esp_wolfssl_set_global_ca_store /*!< Callback function for setting global CA store data for TLS/SSL */
|
#define _esp_tls_set_global_ca_store esp_wolfssl_set_global_ca_store /*!< Callback function for setting global CA store data for TLS/SSL */
|
||||||
@ -115,8 +119,9 @@ esp_tls_t *esp_tls_init(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
|
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
|
||||||
tls->server_fd.fd = tls->sockfd = -1;
|
tls->server_fd.fd = -1;
|
||||||
#endif
|
#endif
|
||||||
|
tls->sockfd = -1;
|
||||||
return tls;
|
return tls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +198,11 @@ static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *s
|
|||||||
}
|
}
|
||||||
if (cfg->non_block) {
|
if (cfg->non_block) {
|
||||||
int flags = fcntl(fd, F_GETFL, 0);
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
if (ret < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to configure the socket as non-blocking (errno %d)", errno);
|
||||||
|
goto err_freesocket;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,9 +268,9 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
|
|||||||
ms_to_timeval(cfg->timeout_ms, &tv);
|
ms_to_timeval(cfg->timeout_ms, &tv);
|
||||||
|
|
||||||
/* In case of non-blocking I/O, we use the select() API to check whether
|
/* In case of non-blocking I/O, we use the select() API to check whether
|
||||||
connection has been estbalished or not*/
|
connection has been established or not*/
|
||||||
if (select(tls->sockfd + 1, &tls->rset, &tls->wset, NULL,
|
if (select(tls->sockfd + 1, &tls->rset, &tls->wset, NULL,
|
||||||
cfg->timeout_ms ? &tv : NULL) == 0) {
|
cfg->timeout_ms>0 ? &tv : NULL) == 0) {
|
||||||
ESP_LOGD(TAG, "select() timed out");
|
ESP_LOGD(TAG, "select() timed out");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -309,12 +318,13 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
|
|||||||
*/
|
*/
|
||||||
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg)
|
esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg)
|
||||||
{
|
{
|
||||||
esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
|
esp_tls_t *tls = esp_tls_init();
|
||||||
if (!tls) {
|
if (!tls) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* esp_tls_conn_new() API establishes connection in a blocking manner thus this loop ensures that esp_tls_conn_new()
|
/* esp_tls_conn_new() API establishes connection in a blocking manner thus this loop ensures that esp_tls_conn_new()
|
||||||
API returns only after connection is established unless there is an error*/
|
API returns only after connection is established unless there is an error*/
|
||||||
|
size_t start = xTaskGetTickCount();
|
||||||
while (1) {
|
while (1) {
|
||||||
int ret = esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
|
int ret = esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
|
||||||
if (ret == 1) {
|
if (ret == 1) {
|
||||||
@ -323,6 +333,14 @@ esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const e
|
|||||||
esp_tls_conn_delete(tls);
|
esp_tls_conn_delete(tls);
|
||||||
ESP_LOGE(TAG, "Failed to open new connection");
|
ESP_LOGE(TAG, "Failed to open new connection");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
} else if (ret == 0 && cfg->timeout_ms >= 0) {
|
||||||
|
size_t timeout_ticks = pdMS_TO_TICKS(cfg->timeout_ms);
|
||||||
|
uint32_t expired = xTaskGetTickCount() - start;
|
||||||
|
if (expired >= timeout_ticks) {
|
||||||
|
esp_tls_conn_delete(tls);
|
||||||
|
ESP_LOGE(TAG, "Failed to open new connection in specified timeout");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -330,8 +348,9 @@ esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const e
|
|||||||
|
|
||||||
int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
|
int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
|
||||||
{
|
{
|
||||||
/* esp_tls_conn_new_sync() is a sync alternative to esp_tls_conn_new_async() with symetric function prototype
|
/* esp_tls_conn_new_sync() is a sync alternative to esp_tls_conn_new_async() with symmetric function prototype
|
||||||
it is an alternative to esp_tls_conn_new() which is left for compatibility reasons */
|
it is an alternative to esp_tls_conn_new() which is left for compatibility reasons */
|
||||||
|
size_t start = xTaskGetTickCount();
|
||||||
while (1) {
|
while (1) {
|
||||||
int ret = esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
|
int ret = esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
|
||||||
if (ret == 1) {
|
if (ret == 1) {
|
||||||
@ -339,6 +358,14 @@ int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp
|
|||||||
} else if (ret == -1) {
|
} else if (ret == -1) {
|
||||||
ESP_LOGE(TAG, "Failed to open new connection");
|
ESP_LOGE(TAG, "Failed to open new connection");
|
||||||
return -1;
|
return -1;
|
||||||
|
} else if (ret == 0 && cfg->timeout_ms >= 0) {
|
||||||
|
size_t timeout_ticks = pdMS_TO_TICKS(cfg->timeout_ms);
|
||||||
|
uint32_t expired = xTaskGetTickCount() - start;
|
||||||
|
if (expired >= timeout_ticks) {
|
||||||
|
ESP_LOGW(TAG, "Failed to open new connection in specified timeout");
|
||||||
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -384,6 +411,7 @@ esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg)
|
|||||||
get_port(url, &u), cfg, tls) == 1) {
|
get_port(url, &u), cfg, tls) == 1) {
|
||||||
return tls;
|
return tls;
|
||||||
}
|
}
|
||||||
|
esp_tls_conn_delete(tls);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,6 +437,7 @@ mbedtls_x509_crt *esp_tls_get_global_ca_store(void)
|
|||||||
return _esp_tls_get_global_ca_store();
|
return _esp_tls_get_global_ca_store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
|
||||||
#ifdef CONFIG_ESP_TLS_SERVER
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
/**
|
/**
|
||||||
* @brief Create a server side TLS/SSL connection
|
* @brief Create a server side TLS/SSL connection
|
||||||
@ -425,13 +454,22 @@ void esp_tls_server_session_delete(esp_tls_t *tls)
|
|||||||
return _esp_tls_server_session_delete(tls);
|
return _esp_tls_server_session_delete(tls);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_ESP_TLS_SERVER */
|
#endif /* CONFIG_ESP_TLS_SERVER */
|
||||||
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
|
|
||||||
|
|
||||||
ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls)
|
ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls)
|
||||||
{
|
{
|
||||||
return _esp_tls_get_bytes_avail(tls);
|
return _esp_tls_get_bytes_avail(tls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_tls_get_conn_sockfd(esp_tls_t *tls, int *sockfd)
|
||||||
|
{
|
||||||
|
if (!tls || !sockfd) {
|
||||||
|
ESP_LOGE(TAG, "Invalid arguments passed");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
*sockfd = tls->sockfd;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags)
|
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags)
|
||||||
{
|
{
|
||||||
if (!h) {
|
if (!h) {
|
||||||
|
@ -54,6 +54,15 @@ extern "C" {
|
|||||||
#define ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0F) /*!< mbedtls api returned failed */
|
#define ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED (ESP_ERR_ESP_TLS_BASE + 0x0F) /*!< mbedtls api returned failed */
|
||||||
#define ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x10) /*!< mbedtls api returned failed */
|
#define ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x10) /*!< mbedtls api returned failed */
|
||||||
#define ESP_ERR_MBEDTLS_SSL_CONF_PSK_FAILED (ESP_ERR_ESP_TLS_BASE + 0x11) /*!< mbedtls api returned failed */
|
#define ESP_ERR_MBEDTLS_SSL_CONF_PSK_FAILED (ESP_ERR_ESP_TLS_BASE + 0x11) /*!< mbedtls api returned failed */
|
||||||
|
#define ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT (ESP_ERR_ESP_TLS_BASE + 0x12) /*!< new connection in esp_tls_low_level_conn connection timeouted */
|
||||||
|
#define ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED (ESP_ERR_ESP_TLS_BASE + 0x13) /*!< wolfSSL api returned error */
|
||||||
|
#define ESP_ERR_WOLFSSL_SSL_CONF_ALPN_PROTOCOLS_FAILED (ESP_ERR_ESP_TLS_BASE + 0x14) /*!< wolfSSL api returned error */
|
||||||
|
#define ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x15) /*!< wolfSSL api returned error */
|
||||||
|
#define ESP_ERR_WOLFSSL_KEY_VERIFY_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x16) /*!< wolfSSL api returned error */
|
||||||
|
#define ESP_ERR_WOLFSSL_SSL_HANDSHAKE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x17) /*!< wolfSSL api returned failed */
|
||||||
|
#define ESP_ERR_WOLFSSL_CTX_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x18) /*!< wolfSSL api returned failed */
|
||||||
|
#define ESP_ERR_WOLFSSL_SSL_SETUP_FAILED (ESP_ERR_ESP_TLS_BASE + 0x19) /*!< wolfSSL api returned failed */
|
||||||
|
#define ESP_ERR_WOLFSSL_SSL_WRITE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x1A) /*!< wolfSSL api returned failed */
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
|
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
|
||||||
#define ESP_TLS_ERR_SSL_WANT_READ MBEDTLS_ERR_SSL_WANT_READ
|
#define ESP_TLS_ERR_SSL_WANT_READ MBEDTLS_ERR_SSL_WANT_READ
|
||||||
@ -188,6 +197,10 @@ typedef struct esp_tls_cfg {
|
|||||||
then PSK authentication is enabled with configured setup.
|
then PSK authentication is enabled with configured setup.
|
||||||
Important note: the pointer must be valid for connection */
|
Important note: the pointer must be valid for connection */
|
||||||
|
|
||||||
|
esp_err_t (*crt_bundle_attach)(void *conf);
|
||||||
|
/*!< Function pointer to esp_crt_bundle_attach. Enables the use of certification
|
||||||
|
bundle for server verification, must be enabled in menuconfig */
|
||||||
|
|
||||||
} esp_tls_cfg_t;
|
} esp_tls_cfg_t;
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_TLS_SERVER
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
@ -249,28 +262,6 @@ typedef struct esp_tls_cfg_server {
|
|||||||
* @brief ESP-TLS Connection Handle
|
* @brief ESP-TLS Connection Handle
|
||||||
*/
|
*/
|
||||||
typedef struct esp_tls {
|
typedef struct esp_tls {
|
||||||
int sockfd; /*!< Underlying socket file descriptor. */
|
|
||||||
|
|
||||||
ssize_t (*_read)(struct esp_tls *tls, char *data, size_t datalen); /*!< Callback function for reading data from TLS/SSL
|
|
||||||
connection. */
|
|
||||||
|
|
||||||
ssize_t (*_write)(struct esp_tls *tls, const char *data, size_t datalen); /*!< Callback function for writing data to TLS/SSL
|
|
||||||
connection. */
|
|
||||||
|
|
||||||
esp_tls_conn_state_t conn_state; /*!< ESP-TLS Connection state */
|
|
||||||
|
|
||||||
fd_set rset; /*!< read file descriptors */
|
|
||||||
|
|
||||||
fd_set wset; /*!< write file descriptors */
|
|
||||||
|
|
||||||
bool is_tls; /*!< indicates connection type (TLS or NON-TLS) */
|
|
||||||
|
|
||||||
esp_tls_role_t role; /*!< esp-tls role
|
|
||||||
- ESP_TLS_CLIENT
|
|
||||||
- ESP_TLS_SERVER */
|
|
||||||
|
|
||||||
esp_tls_error_handle_t error_handle; /*!< handle to error descriptor */
|
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
|
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
|
||||||
mbedtls_ssl_context ssl; /*!< TLS/SSL context */
|
mbedtls_ssl_context ssl; /*!< TLS/SSL context */
|
||||||
|
|
||||||
@ -304,6 +295,28 @@ typedef struct esp_tls {
|
|||||||
void *priv_ctx;
|
void *priv_ctx;
|
||||||
void *priv_ssl;
|
void *priv_ssl;
|
||||||
#endif
|
#endif
|
||||||
|
int sockfd; /*!< Underlying socket file descriptor. */
|
||||||
|
|
||||||
|
ssize_t (*_read)(struct esp_tls *tls, char *data, size_t datalen); /*!< Callback function for reading data from TLS/SSL
|
||||||
|
connection. */
|
||||||
|
|
||||||
|
ssize_t (*_write)(struct esp_tls *tls, const char *data, size_t datalen); /*!< Callback function for writing data to TLS/SSL
|
||||||
|
connection. */
|
||||||
|
|
||||||
|
esp_tls_conn_state_t conn_state; /*!< ESP-TLS Connection state */
|
||||||
|
|
||||||
|
fd_set rset; /*!< read file descriptors */
|
||||||
|
|
||||||
|
fd_set wset; /*!< write file descriptors */
|
||||||
|
|
||||||
|
bool is_tls; /*!< indicates connection type (TLS or NON-TLS) */
|
||||||
|
|
||||||
|
esp_tls_role_t role; /*!< esp-tls role
|
||||||
|
- ESP_TLS_CLIENT
|
||||||
|
- ESP_TLS_SERVER */
|
||||||
|
|
||||||
|
esp_tls_error_handle_t error_handle; /*!< handle to error descriptor */
|
||||||
|
|
||||||
} esp_tls_t;
|
} esp_tls_t;
|
||||||
|
|
||||||
|
|
||||||
@ -358,7 +371,7 @@ esp_tls_t *esp_tls_conn_new(const char *hostname, int hostlen, int port, const e
|
|||||||
* @return
|
* @return
|
||||||
* - -1 If connection establishment fails.
|
* - -1 If connection establishment fails.
|
||||||
* - 1 If connection establishment is successful.
|
* - 1 If connection establishment is successful.
|
||||||
* - 0 Reserved for connection state is in progress.
|
* - 0 If connection state is in progress.
|
||||||
*/
|
*/
|
||||||
int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls);
|
int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls);
|
||||||
|
|
||||||
@ -477,6 +490,18 @@ void esp_tls_conn_delete(esp_tls_t *tls);
|
|||||||
*/
|
*/
|
||||||
ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls);
|
ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the connection socket file descriptor from esp_tls session
|
||||||
|
*
|
||||||
|
* @param[in] tls handle to esp_tls context
|
||||||
|
*
|
||||||
|
* @param[out] sockfd int pointer to sockfd value.
|
||||||
|
*
|
||||||
|
* @return - ESP_OK on success and value of sockfd will be updated with socket file descriptor for connection
|
||||||
|
* - ESP_ERR_INVALID_ARG if (tls == NULL || sockfd == NULL)
|
||||||
|
*/
|
||||||
|
esp_err_t esp_tls_get_conn_sockfd(esp_tls_t *tls, int *sockfd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a global CA store, initially empty.
|
* @brief Create a global CA store, initially empty.
|
||||||
*
|
*
|
||||||
@ -549,6 +574,7 @@ esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tl
|
|||||||
*/
|
*/
|
||||||
mbedtls_x509_crt *esp_tls_get_global_ca_store(void);
|
mbedtls_x509_crt *esp_tls_get_global_ca_store(void);
|
||||||
|
|
||||||
|
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
|
||||||
#ifdef CONFIG_ESP_TLS_SERVER
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
/**
|
/**
|
||||||
* @brief Create TLS/SSL server session
|
* @brief Create TLS/SSL server session
|
||||||
@ -576,7 +602,6 @@ int esp_tls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls
|
|||||||
*/
|
*/
|
||||||
void esp_tls_server_session_delete(esp_tls_t *tls);
|
void esp_tls_server_session_delete(esp_tls_t *tls);
|
||||||
#endif /* ! CONFIG_ESP_TLS_SERVER */
|
#endif /* ! CONFIG_ESP_TLS_SERVER */
|
||||||
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||||
|
#include "esp_crt_bundle.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static const char *TAG = "esp-tls-mbedtls";
|
static const char *TAG = "esp-tls-mbedtls";
|
||||||
static mbedtls_x509_crt *global_cacert = NULL;
|
static mbedtls_x509_crt *global_cacert = NULL;
|
||||||
|
|
||||||
@ -266,7 +271,7 @@ static esp_err_t set_pki_context(esp_tls_t *tls, const esp_tls_pki_t *pki)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = mbedtls_pk_parse_key(pki->pk_key, pki->privkey_pem_buf, pki->privkey_pem_bytes,
|
ret = mbedtls_pk_parse_key(pki->pk_key, pki->privkey_pem_buf, pki->privkey_pem_bytes,
|
||||||
NULL, 0);
|
pki->privkey_password, pki->privkey_password_len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ESP_LOGE(TAG, "mbedtls_pk_parse_keyfile returned -0x%x", -ret);
|
ESP_LOGE(TAG, "mbedtls_pk_parse_keyfile returned -0x%x", -ret);
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
|
||||||
@ -389,16 +394,30 @@ esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t
|
|||||||
return ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED;
|
return ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MBEDTLS_SSL_ALPN
|
|
||||||
if (cfg->alpn_protos) {
|
if (cfg->alpn_protos) {
|
||||||
|
#ifdef CONFIG_MBEDTLS_SSL_ALPN
|
||||||
if ((ret = mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos) != 0)) {
|
if ((ret = mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos) != 0)) {
|
||||||
ESP_LOGE(TAG, "mbedtls_ssl_conf_alpn_protocols returned -0x%x", -ret);
|
ESP_LOGE(TAG, "mbedtls_ssl_conf_alpn_protocols returned -0x%x", -ret);
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
|
||||||
return ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED;
|
return ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED;
|
||||||
}
|
}
|
||||||
}
|
#else
|
||||||
|
ESP_LOGE(TAG, "alpn_protos configured but not enabled in menuconfig: Please enable MBEDTLS_SSL_ALPN option");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
#endif
|
#endif
|
||||||
if (cfg->use_global_ca_store == true) {
|
}
|
||||||
|
|
||||||
|
if (cfg->crt_bundle_attach != NULL) {
|
||||||
|
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||||
|
ESP_LOGD(TAG, "Use certificate bundle");
|
||||||
|
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
||||||
|
cfg->crt_bundle_attach(&tls->conf);
|
||||||
|
#else //CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||||
|
ESP_LOGE(TAG, "use_crt_bundle configured but not enabled in menuconfig: Please enable MBEDTLS_CERTIFICATE_BUNDLE option");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
#endif
|
||||||
|
} else if (cfg->use_global_ca_store == true) {
|
||||||
esp_err_t esp_ret = set_global_ca_store(tls);
|
esp_err_t esp_ret = set_global_ca_store(tls);
|
||||||
if (esp_ret != ESP_OK) {
|
if (esp_ret != ESP_OK) {
|
||||||
return esp_ret;
|
return esp_ret;
|
||||||
|
@ -31,16 +31,86 @@ static unsigned char *global_cacert = NULL;
|
|||||||
static unsigned int global_cacert_pem_bytes = 0;
|
static unsigned int global_cacert_pem_bytes = 0;
|
||||||
static const char *TAG = "esp-tls-wolfssl";
|
static const char *TAG = "esp-tls-wolfssl";
|
||||||
|
|
||||||
int esp_create_wolfssl_handle(const char *hostname, size_t hostlen, const void *cfg1, esp_tls_t *tls)
|
/* Prototypes for the static functions */
|
||||||
|
static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t *cfg, esp_tls_t *tls);
|
||||||
|
|
||||||
|
#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION)
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
static SemaphoreHandle_t tls_conn_lock;
|
||||||
|
static inline unsigned int esp_wolfssl_psk_client_cb(WOLFSSL* ssl, const char* hint, char* identity,
|
||||||
|
unsigned int id_max_len, unsigned char* key,unsigned int key_max_len);
|
||||||
|
static esp_err_t esp_wolfssl_set_cipher_list(WOLFSSL_CTX *ctx);
|
||||||
|
#ifdef WOLFSSL_TLS13
|
||||||
|
#define PSK_MAX_ID_LEN 128
|
||||||
|
#else
|
||||||
|
#define PSK_MAX_ID_LEN 64
|
||||||
|
#endif
|
||||||
|
#define PSK_MAX_KEY_LEN 64
|
||||||
|
|
||||||
|
static char psk_id_str[PSK_MAX_ID_LEN];
|
||||||
|
static uint8_t psk_key_array[PSK_MAX_KEY_LEN];
|
||||||
|
static uint8_t psk_key_max_len = 0;
|
||||||
|
#endif /* CONFIG_ESP_TLS_PSK_VERIFICATION */
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
|
static esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls);
|
||||||
|
#endif /* CONFIG_ESP_TLS_SERVER */
|
||||||
|
|
||||||
|
typedef enum x509_file_type {
|
||||||
|
FILE_TYPE_CA_CERT = 0, /* CA certificate to authenticate entity at other end */
|
||||||
|
FILE_TYPE_SELF_CERT, /* Self certificate of the entity */
|
||||||
|
FILE_TYPE_SELF_KEY, /* Private key in the self cert-key pair */
|
||||||
|
} x509_file_type_t;
|
||||||
|
|
||||||
|
/* Checks whether the certificate provided is in pem format or not */
|
||||||
|
static esp_err_t esp_load_wolfssl_verify_buffer(esp_tls_t *tls, const unsigned char *cert_buf, unsigned int cert_len, x509_file_type_t type, int *err_ret)
|
||||||
|
{
|
||||||
|
int wolf_fileformat = WOLFSSL_FILETYPE_DEFAULT;
|
||||||
|
if (type == FILE_TYPE_SELF_KEY) {
|
||||||
|
if (cert_buf[cert_len - 1] == '\0' && strstr( (const char *) cert_buf, "-----BEGIN " )) {
|
||||||
|
wolf_fileformat = WOLFSSL_FILETYPE_PEM;
|
||||||
|
} else {
|
||||||
|
wolf_fileformat = WOLFSSL_FILETYPE_ASN1;
|
||||||
|
}
|
||||||
|
if ((*err_ret = wolfSSL_CTX_use_PrivateKey_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
} else {
|
||||||
|
if (cert_buf[cert_len - 1] == '\0' && strstr( (const char *) cert_buf, "-----BEGIN CERTIFICATE-----" )) {
|
||||||
|
wolf_fileformat = WOLFSSL_FILETYPE_PEM;
|
||||||
|
} else {
|
||||||
|
wolf_fileformat = WOLFSSL_FILETYPE_ASN1;
|
||||||
|
}
|
||||||
|
if (type == FILE_TYPE_SELF_CERT) {
|
||||||
|
if ((*err_ret = wolfSSL_CTX_use_certificate_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
} else if (type == FILE_TYPE_CA_CERT) {
|
||||||
|
if ((*err_ret = wolfSSL_CTX_load_verify_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cert_buf, cert_len, wolf_fileformat)) == WOLFSSL_SUCCESS) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
return ESP_FAIL;
|
||||||
|
} else {
|
||||||
|
/* Wrong file type provided */
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_create_wolfssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_ESP_DEBUG_WOLFSSL
|
#ifdef CONFIG_ESP_DEBUG_WOLFSSL
|
||||||
wolfSSL_Debugging_ON();
|
wolfSSL_Debugging_ON();
|
||||||
#endif
|
#endif
|
||||||
const esp_tls_cfg_t *cfg = cfg1;
|
|
||||||
assert(cfg != NULL);
|
assert(cfg != NULL);
|
||||||
assert(tls != NULL);
|
assert(tls != NULL);
|
||||||
|
|
||||||
|
esp_err_t esp_ret = ESP_FAIL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = wolfSSL_Init();
|
ret = wolfSSL_Init();
|
||||||
if (ret != WOLFSSL_SUCCESS) {
|
if (ret != WOLFSSL_SUCCESS) {
|
||||||
ESP_LOGE(TAG, "Init wolfSSL failed: %d", ret);
|
ESP_LOGE(TAG, "Init wolfSSL failed: %d", ret);
|
||||||
@ -48,71 +118,208 @@ int esp_create_wolfssl_handle(const char *hostname, size_t hostlen, const void *
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tls->role == ESP_TLS_CLIENT) {
|
||||||
|
esp_ret = set_client_config(hostname, hostlen, (esp_tls_cfg_t *)cfg, tls);
|
||||||
|
if (esp_ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set client configurations");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
} else if (tls->role == ESP_TLS_SERVER) {
|
||||||
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
|
esp_ret = set_server_config((esp_tls_cfg_server_t *) cfg, tls);
|
||||||
|
if (esp_ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set server configurations");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ESP_LOGE(TAG, "ESP_TLS_SERVER Not enabled in menuconfig");
|
||||||
|
goto exit;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ESP_LOGE(TAG, "tls->role is not valid");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
exit:
|
||||||
|
esp_wolfssl_cleanup(tls);
|
||||||
|
return esp_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t *cfg, esp_tls_t *tls)
|
||||||
|
{
|
||||||
|
int ret = WOLFSSL_FAILURE;
|
||||||
tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_2_client_method());
|
tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_2_client_method());
|
||||||
if (!tls->priv_ctx) {
|
if (!tls->priv_ctx) {
|
||||||
ESP_LOGE(TAG, "Set wolfSSL ctx failed");
|
ESP_LOGE(TAG, "Set wolfSSL ctx failed");
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
||||||
goto exit;
|
return ESP_ERR_WOLFSSL_CTX_SETUP_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_ALPN
|
if (cfg->use_global_ca_store == true) {
|
||||||
if (cfg->alpn_protos) {
|
if ((esp_load_wolfssl_verify_buffer(tls, global_cacert, global_cacert_pem_bytes, FILE_TYPE_CA_CERT, &ret)) != ESP_OK) {
|
||||||
char **alpn_list = (char **)cfg->alpn_protos;
|
ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d", ret);
|
||||||
for (; *alpn_list != NULL; alpn_list ++) {
|
return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED;
|
||||||
if (wolfSSL_UseALPN( (WOLFSSL *)tls->priv_ssl, *alpn_list, strlen(*alpn_list), WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) {
|
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
|
||||||
ESP_LOGE(TAG, "Use wolfSSL ALPN failed");
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_PEER, NULL);
|
||||||
#endif
|
} else if (cfg->cacert_buf != NULL) {
|
||||||
|
if ((esp_load_wolfssl_verify_buffer(tls, cfg->cacert_buf, cfg->cacert_bytes, FILE_TYPE_CA_CERT, &ret)) != ESP_OK) {
|
||||||
if ( cfg->use_global_ca_store == true) {
|
ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d", ret);
|
||||||
wolfSSL_CTX_load_verify_buffer( (WOLFSSL_CTX *)tls->priv_ctx, global_cacert, global_cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
|
return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED;
|
||||||
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, SSL_VERIFY_PEER, NULL);
|
}
|
||||||
} else if (cfg->cacert_pem_buf != NULL) {
|
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_PEER, NULL);
|
||||||
wolfSSL_CTX_load_verify_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cfg->cacert_pem_buf, cfg->cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
|
|
||||||
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, SSL_VERIFY_PEER, NULL);
|
|
||||||
} else if (cfg->psk_hint_key) {
|
} else if (cfg->psk_hint_key) {
|
||||||
ESP_LOGE(TAG,"psk_hint_key not supported in wolfssl");
|
#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION)
|
||||||
goto exit;
|
/*** PSK encryption mode is configured only if no certificate supplied and psk pointer not null ***/
|
||||||
|
if(cfg->psk_hint_key->key == NULL || cfg->psk_hint_key->hint == NULL || cfg->psk_hint_key->key_size <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Please provide appropriate key, keysize and hint to use PSK");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
/* mutex is given back when call back function executes or in case of failure (at cleanup) */
|
||||||
|
if ((xSemaphoreTake(tls_conn_lock, 1000/portTICK_PERIOD_MS) != pdTRUE)) {
|
||||||
|
ESP_LOGE(TAG, "tls_conn_lock could not be obtained in specified time");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "setting psk configurations");
|
||||||
|
if((cfg->psk_hint_key->key_size > PSK_MAX_KEY_LEN) || (strlen(cfg->psk_hint_key->hint) > PSK_MAX_ID_LEN)) {
|
||||||
|
ESP_LOGE(TAG, "psk key length should be <= %d and identity hint length should be <= %d", PSK_MAX_KEY_LEN, PSK_MAX_ID_LEN);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
psk_key_max_len = cfg->psk_hint_key->key_size;
|
||||||
|
memset(psk_key_array, 0, sizeof(psk_key_array));
|
||||||
|
memset(psk_id_str, 0, sizeof(psk_id_str));
|
||||||
|
memcpy(psk_key_array, cfg->psk_hint_key->key, psk_key_max_len);
|
||||||
|
memcpy(psk_id_str, cfg->psk_hint_key->hint, strlen(cfg->psk_hint_key->hint));
|
||||||
|
wolfSSL_CTX_set_psk_client_callback( (WOLFSSL_CTX *)tls->priv_ctx, esp_wolfssl_psk_client_cb);
|
||||||
|
if(esp_wolfssl_set_cipher_list( (WOLFSSL_CTX *)tls->priv_ctx) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "error in setting cipher-list");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ESP_LOGE(TAG, "psk_hint_key configured but not enabled in menuconfig: Please enable ESP_TLS_PSK_VERIFICATION option");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, SSL_VERIFY_NONE, NULL);
|
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_NONE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg->clientcert_pem_buf != NULL && cfg->clientkey_pem_buf != NULL) {
|
if (cfg->clientcert_buf != NULL && cfg->clientkey_buf != NULL) {
|
||||||
wolfSSL_CTX_use_certificate_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cfg->clientcert_pem_buf, cfg->clientcert_pem_bytes, WOLFSSL_FILETYPE_PEM);
|
if ((esp_load_wolfssl_verify_buffer(tls,cfg->clientcert_buf, cfg->clientcert_bytes, FILE_TYPE_SELF_CERT, &ret)) != ESP_OK) {
|
||||||
wolfSSL_CTX_use_PrivateKey_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cfg->clientkey_pem_buf, cfg->clientkey_pem_bytes, WOLFSSL_FILETYPE_PEM);
|
ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d", ret);
|
||||||
} else if (cfg->clientcert_pem_buf != NULL || cfg->clientkey_pem_buf != NULL) {
|
return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED;
|
||||||
ESP_LOGE(TAG, "You have to provide both clientcert_pem_buf and clientkey_pem_buf for mutual authentication\n\n");
|
}
|
||||||
goto exit;
|
if ((esp_load_wolfssl_verify_buffer(tls,cfg->clientkey_buf, cfg->clientkey_bytes, FILE_TYPE_SELF_KEY, &ret)) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error in loading private key verify buffer, returned %d", ret);
|
||||||
|
return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED;
|
||||||
|
}
|
||||||
|
} else if (cfg->clientcert_buf != NULL || cfg->clientkey_buf != NULL) {
|
||||||
|
ESP_LOGE(TAG, "You have to provide both clientcert_buf and clientkey_buf for mutual authentication\n\n");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->crt_bundle_attach != NULL) {
|
||||||
|
ESP_LOGE(TAG,"use_crt_bundle not supported in wolfssl");
|
||||||
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx);
|
tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx);
|
||||||
if (!tls->priv_ssl) {
|
if (!tls->priv_ssl) {
|
||||||
ESP_LOGE(TAG, "Create wolfSSL failed");
|
ESP_LOGE(TAG, "Create wolfSSL failed");
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
||||||
goto exit;
|
return ESP_ERR_WOLFSSL_SSL_SETUP_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cfg->skip_common_name) {
|
||||||
#ifdef HAVE_SNI
|
#ifdef HAVE_SNI
|
||||||
/* Hostname set here should match CN in server certificate */
|
char *use_host = NULL;
|
||||||
char *use_host = strndup(hostname, hostlen);
|
if (cfg->common_name != NULL) {
|
||||||
if (!use_host) {
|
use_host = strdup(cfg->common_name);
|
||||||
goto exit;
|
} else {
|
||||||
|
use_host = strndup(hostname, hostlen);
|
||||||
|
}
|
||||||
|
if (use_host == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
/* Hostname set here should match CN in server certificate */
|
||||||
|
if ((ret = wolfSSL_set_tlsext_host_name( (WOLFSSL *)tls->priv_ssl, use_host))!= WOLFSSL_SUCCESS) {
|
||||||
|
ESP_LOGE(TAG, "wolfSSL_set_tlsext_host_name returned -0x%x", -ret);
|
||||||
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
||||||
|
free(use_host);
|
||||||
|
return ESP_ERR_WOLFSSL_SSL_SET_HOSTNAME_FAILED;
|
||||||
|
}
|
||||||
|
free(use_host);
|
||||||
|
#endif /* HAVE_SNI */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->alpn_protos) {
|
||||||
|
#ifdef CONFIG_WOLFSSL_HAVE_ALPN
|
||||||
|
char **alpn_list = (char **)cfg->alpn_protos;
|
||||||
|
for (; *alpn_list != NULL; alpn_list ++) {
|
||||||
|
ESP_LOGD(TAG, "alpn protocol is %s", *alpn_list);
|
||||||
|
if ((ret = wolfSSL_UseALPN( (WOLFSSL *)tls->priv_ssl, *alpn_list, strlen(*alpn_list), WOLFSSL_ALPN_FAILED_ON_MISMATCH)) != WOLFSSL_SUCCESS) {
|
||||||
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
||||||
|
ESP_LOGE(TAG, "wolfSSL UseALPN failed, returned %d", ret);
|
||||||
|
return ESP_ERR_WOLFSSL_SSL_CONF_ALPN_PROTOCOLS_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ESP_LOGE(TAG, "CONFIG_WOLFSSL_HAVE_ALPN not enabled in menuconfig");
|
||||||
|
return ESP_FAIL;
|
||||||
|
#endif /* CONFIG_WOLFSSL_HAVE_ALPN */
|
||||||
}
|
}
|
||||||
wolfSSL_set_tlsext_host_name( (WOLFSSL *)tls->priv_ssl, use_host);
|
|
||||||
free(use_host);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
wolfSSL_set_fd((WOLFSSL *)tls->priv_ssl, tls->sockfd);
|
wolfSSL_set_fd((WOLFSSL *)tls->priv_ssl, tls->sockfd);
|
||||||
return 0;
|
return ESP_OK;
|
||||||
exit:
|
|
||||||
esp_wolfssl_cleanup(tls);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
|
static esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls)
|
||||||
|
{
|
||||||
|
int ret = WOLFSSL_FAILURE;
|
||||||
|
tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_2_server_method());
|
||||||
|
if (!tls->priv_ctx) {
|
||||||
|
ESP_LOGE(TAG, "Set wolfSSL ctx failed");
|
||||||
|
return ESP_ERR_WOLFSSL_CTX_SETUP_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->cacert_buf != NULL) {
|
||||||
|
if ((esp_load_wolfssl_verify_buffer(tls,cfg->cacert_buf, cfg->cacert_bytes, FILE_TYPE_CA_CERT, &ret)) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d", ret);
|
||||||
|
return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED;
|
||||||
|
}
|
||||||
|
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
||||||
|
ESP_LOGD(TAG," Verify Client for Mutual Auth");
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG," Not verifying Client ");
|
||||||
|
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, WOLFSSL_VERIFY_NONE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->servercert_buf != NULL && cfg->serverkey_buf != NULL) {
|
||||||
|
if ((esp_load_wolfssl_verify_buffer(tls,cfg->servercert_buf, cfg->servercert_bytes, FILE_TYPE_SELF_CERT, &ret)) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error in loading certificate verify buffer, returned %d", ret);
|
||||||
|
return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED;
|
||||||
|
}
|
||||||
|
if ((esp_load_wolfssl_verify_buffer(tls,cfg->serverkey_buf, cfg->serverkey_bytes, FILE_TYPE_SELF_KEY, &ret)) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Error in loading private key verify buffer, returned %d", ret);
|
||||||
|
return ESP_ERR_WOLFSSL_CERT_VERIFY_SETUP_FAILED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "You have to provide both servercert_buf and serverkey_buf for https_server\n\n");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx);
|
||||||
|
if (!tls->priv_ssl) {
|
||||||
|
ESP_LOGE(TAG, "Create wolfSSL failed");
|
||||||
|
return ESP_ERR_WOLFSSL_SSL_SETUP_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
wolfSSL_set_fd((WOLFSSL *)tls->priv_ssl, tls->sockfd);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int esp_wolfssl_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
|
int esp_wolfssl_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -125,8 +332,8 @@ int esp_wolfssl_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
|
|||||||
if (err != ESP_TLS_ERR_SSL_WANT_READ && err != ESP_TLS_ERR_SSL_WANT_WRITE) {
|
if (err != ESP_TLS_ERR_SSL_WANT_READ && err != ESP_TLS_ERR_SSL_WANT_WRITE) {
|
||||||
ESP_LOGE(TAG, "wolfSSL_connect returned -0x%x", -ret);
|
ESP_LOGE(TAG, "wolfSSL_connect returned -0x%x", -ret);
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
||||||
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_WOLFSSL_SSL_HANDSHAKE_FAILED);
|
||||||
if (cfg->cacert_pem_buf != NULL || cfg->use_global_ca_store == true) {
|
if (cfg->cacert_buf != NULL || cfg->use_global_ca_store == true) {
|
||||||
/* This is to check whether handshake failed due to invalid certificate*/
|
/* This is to check whether handshake failed due to invalid certificate*/
|
||||||
esp_wolfssl_verify_certificate(tls);
|
esp_wolfssl_verify_certificate(tls);
|
||||||
}
|
}
|
||||||
@ -164,7 +371,9 @@ ssize_t esp_wolfssl_write(esp_tls_t *tls, const char *data, size_t datalen)
|
|||||||
ret = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret);
|
ret = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret);
|
||||||
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
|
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
||||||
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_WOLFSSL_SSL_WRITE_FAILED);
|
||||||
ESP_LOGE(TAG, "write error :%d:", ret);
|
ESP_LOGE(TAG, "write error :%d:", ret);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -174,7 +383,7 @@ void esp_wolfssl_verify_certificate(esp_tls_t *tls)
|
|||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
if ((flags = wolfSSL_get_verify_result( (WOLFSSL *)tls->priv_ssl)) != WOLFSSL_SUCCESS) {
|
if ((flags = wolfSSL_get_verify_result( (WOLFSSL *)tls->priv_ssl)) != WOLFSSL_SUCCESS) {
|
||||||
ESP_LOGE(TAG, "Failed to verify peer certificate %d!", flags);
|
ESP_LOGE(TAG, "Failed to verify peer certificate , returned %d!", flags);
|
||||||
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL_CERT_FLAGS, flags);
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL_CERT_FLAGS, flags);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "Certificate verified.");
|
ESP_LOGI(TAG, "Certificate verified.");
|
||||||
@ -202,12 +411,61 @@ void esp_wolfssl_cleanup(esp_tls_t *tls)
|
|||||||
if (!tls) {
|
if (!tls) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_ESP_TLS_PSK_VERIFICATION
|
||||||
|
xSemaphoreGive(tls_conn_lock);
|
||||||
|
#endif /* CONFIG_ESP_TLS_PSK_VERIFICATION */
|
||||||
wolfSSL_shutdown( (WOLFSSL *)tls->priv_ssl);
|
wolfSSL_shutdown( (WOLFSSL *)tls->priv_ssl);
|
||||||
wolfSSL_free( (WOLFSSL *)tls->priv_ssl);
|
wolfSSL_free( (WOLFSSL *)tls->priv_ssl);
|
||||||
|
tls->priv_ssl = NULL;
|
||||||
wolfSSL_CTX_free( (WOLFSSL_CTX *)tls->priv_ctx);
|
wolfSSL_CTX_free( (WOLFSSL_CTX *)tls->priv_ctx);
|
||||||
|
tls->priv_ctx = NULL;
|
||||||
wolfSSL_Cleanup();
|
wolfSSL_Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
|
/**
|
||||||
|
* @brief Create TLS/SSL server session
|
||||||
|
*/
|
||||||
|
int esp_wolfssl_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls)
|
||||||
|
{
|
||||||
|
if (tls == NULL || cfg == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
tls->role = ESP_TLS_SERVER;
|
||||||
|
tls->sockfd = sockfd;
|
||||||
|
esp_err_t esp_ret = esp_create_wolfssl_handle(NULL, 0, cfg, tls);
|
||||||
|
if (esp_ret != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "create_ssl_handle failed");
|
||||||
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
|
||||||
|
tls->conn_state = ESP_TLS_FAIL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
tls->_read = esp_wolfssl_read;
|
||||||
|
tls->_write = esp_wolfssl_write;
|
||||||
|
int ret;
|
||||||
|
while ((ret = wolfSSL_accept((WOLFSSL *)tls->priv_ssl)) != WOLFSSL_SUCCESS) {
|
||||||
|
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
|
||||||
|
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
|
||||||
|
ESP_LOGE(TAG, "wolfSSL_handshake_server returned %d", ret);
|
||||||
|
tls->conn_state = ESP_TLS_FAIL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Close the server side TLS/SSL connection and free any allocated resources.
|
||||||
|
*/
|
||||||
|
void esp_wolfssl_server_session_delete(esp_tls_t *tls)
|
||||||
|
{
|
||||||
|
if (tls != NULL) {
|
||||||
|
esp_wolfssl_cleanup(tls);
|
||||||
|
free(tls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_TLS_SERVER */
|
||||||
|
|
||||||
esp_err_t esp_wolfssl_init_global_ca_store(void)
|
esp_err_t esp_wolfssl_init_global_ca_store(void)
|
||||||
{
|
{
|
||||||
/* This function is just to provide consistancy between function calls of esp_tls.h and wolfssl */
|
/* This function is just to provide consistancy between function calls of esp_tls.h and wolfssl */
|
||||||
@ -242,3 +500,56 @@ void esp_wolfssl_free_global_ca_store(void)
|
|||||||
global_cacert_pem_bytes = 0;
|
global_cacert_pem_bytes = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION)
|
||||||
|
static esp_err_t esp_wolfssl_set_cipher_list(WOLFSSL_CTX *ctx)
|
||||||
|
{
|
||||||
|
const char *defaultCipherList;
|
||||||
|
int ret;
|
||||||
|
#if defined(HAVE_AESGCM) && !defined(NO_DH)
|
||||||
|
#ifdef WOLFSSL_TLS13
|
||||||
|
defaultCipherList = "DHE-PSK-AES128-GCM-SHA256:"
|
||||||
|
"TLS13-AES128-GCM-SHA256";
|
||||||
|
#else
|
||||||
|
defaultCipherList = "DHE-PSK-AES128-GCM-SHA256";
|
||||||
|
#endif
|
||||||
|
#elif defined(HAVE_NULL_CIPHER)
|
||||||
|
defaultCipherList = "PSK-NULL-SHA256";
|
||||||
|
#else
|
||||||
|
defaultCipherList = "PSK-AES128-CBC-SHA256";
|
||||||
|
#endif
|
||||||
|
ESP_LOGD(TAG, "cipher list is %s", defaultCipherList);
|
||||||
|
if ((ret = wolfSSL_CTX_set_cipher_list(ctx,defaultCipherList)) != WOLFSSL_SUCCESS) {
|
||||||
|
wolfSSL_CTX_free(ctx);
|
||||||
|
ESP_LOGE(TAG, "can't set cipher list, returned %02x", ret);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize the mutex before app_main() when using PSK */
|
||||||
|
static void __attribute__((constructor))
|
||||||
|
espt_tls_wolfssl_init_conn_lock (void)
|
||||||
|
{
|
||||||
|
if ((tls_conn_lock = xSemaphoreCreateMutex()) == NULL) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "mutex for tls psk connection could not be created");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Some callback functions required by PSK */
|
||||||
|
static inline unsigned int esp_wolfssl_psk_client_cb(WOLFSSL* ssl, const char* hint,
|
||||||
|
char* identity, unsigned int id_max_len, unsigned char* key,
|
||||||
|
unsigned int key_max_len)
|
||||||
|
{
|
||||||
|
(void)key_max_len;
|
||||||
|
|
||||||
|
/* see internal.h MAX_PSK_ID_LEN for PSK identity limit */
|
||||||
|
memcpy(identity, psk_id_str, id_max_len);
|
||||||
|
for(int count = 0; count < psk_key_max_len; count ++) {
|
||||||
|
key[count] = psk_key_array[count];
|
||||||
|
}
|
||||||
|
xSemaphoreGive(tls_conn_lock);
|
||||||
|
return psk_key_max_len;
|
||||||
|
/* return length of key in octets or 0 or for error */
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ESP_TLS_PSK_VERIFICATION */
|
||||||
|
@ -70,3 +70,17 @@ void esp_wolfssl_free_global_ca_store(void);
|
|||||||
* Callback function for Initializing the global ca store for TLS?SSL using wolfssl
|
* Callback function for Initializing the global ca store for TLS?SSL using wolfssl
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_wolfssl_init_global_ca_store(void);
|
esp_err_t esp_wolfssl_init_global_ca_store(void);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_TLS_SERVER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to Create ESP-TLS Server session with wolfssl Stack
|
||||||
|
*/
|
||||||
|
int esp_wolfssl_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete Server Session
|
||||||
|
*/
|
||||||
|
void esp_wolfssl_server_session_delete(esp_tls_t *tls);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#ifndef _HTTP_UTILS_H_
|
#ifndef _HTTP_UTILS_H_
|
||||||
#define _HTTP_UTILS_H_
|
#define _HTTP_UTILS_H_
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include "esp_transport_utils.h"
|
|
||||||
/**
|
/**
|
||||||
* @brief Assign new_str to *str pointer, and realloc *str if it not NULL
|
* @brief Assign new_str to *str pointer, and realloc *str if it not NULL
|
||||||
*
|
*
|
||||||
@ -80,7 +80,9 @@ char *http_utils_join_string(const char *first_str, int len_first, const char *s
|
|||||||
int http_utils_str_starts_with(const char *str, const char *start);
|
int http_utils_str_starts_with(const char *str, const char *start);
|
||||||
|
|
||||||
|
|
||||||
#define HTTP_MEM_CHECK(TAG, a, action) ESP_TRANSPORT_MEM_CHECK(TAG, a, action)
|
#define HTTP_MEM_CHECK(TAG, a, action) if (!(a)) { \
|
||||||
|
ESP_LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \
|
||||||
|
action; \
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#include "lwip/sockets.h"
|
#include "lwip/sockets.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
#if !LWIP_POSIX_SOCKETS_IO_NAMES
|
#if !LWIP_POSIX_SOCKETS_IO_NAMES
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -133,7 +133,7 @@ esp_err_t esp_transport_set_default_port(esp_transport_handle_t t, int port);
|
|||||||
* @param t The transport handle
|
* @param t The transport handle
|
||||||
* @param[in] host Hostname
|
* @param[in] host Hostname
|
||||||
* @param[in] port Port
|
* @param[in] port Port
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout milliseconds (-1 indicates wait forever)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - socket for will use by this transport
|
* - socket for will use by this transport
|
||||||
@ -147,7 +147,7 @@ int esp_transport_connect(esp_transport_handle_t t, const char *host, int port,
|
|||||||
* @param t The transport handle
|
* @param t The transport handle
|
||||||
* @param[in] host Hostname
|
* @param[in] host Hostname
|
||||||
* @param[in] port Port
|
* @param[in] port Port
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout milliseconds (-1 indicates wait forever)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - socket for will use by this transport
|
* - socket for will use by this transport
|
||||||
@ -161,7 +161,7 @@ int esp_transport_connect_async(esp_transport_handle_t t, const char *host, int
|
|||||||
* @param t The transport handle
|
* @param t The transport handle
|
||||||
* @param buffer The buffer
|
* @param buffer The buffer
|
||||||
* @param[in] len The length
|
* @param[in] len The length
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout milliseconds (-1 indicates wait forever)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - Number of bytes was read
|
* - Number of bytes was read
|
||||||
@ -173,7 +173,7 @@ int esp_transport_read(esp_transport_handle_t t, char *buffer, int len, int time
|
|||||||
* @brief Poll the transport until readable or timeout
|
* @brief Poll the transport until readable or timeout
|
||||||
*
|
*
|
||||||
* @param[in] t The transport handle
|
* @param[in] t The transport handle
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout milliseconds (-1 indicates wait forever)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - 0 Timeout
|
* - 0 Timeout
|
||||||
@ -188,7 +188,7 @@ int esp_transport_poll_read(esp_transport_handle_t t, int timeout_ms);
|
|||||||
* @param t The transport handle
|
* @param t The transport handle
|
||||||
* @param buffer The buffer
|
* @param buffer The buffer
|
||||||
* @param[in] len The length
|
* @param[in] len The length
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout milliseconds (-1 indicates wait forever)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - Number of bytes was written
|
* - Number of bytes was written
|
||||||
@ -200,7 +200,7 @@ int esp_transport_write(esp_transport_handle_t t, const char *buffer, int len, i
|
|||||||
* @brief Poll the transport until writeable or timeout
|
* @brief Poll the transport until writeable or timeout
|
||||||
*
|
*
|
||||||
* @param[in] t The transport handle
|
* @param[in] t The transport handle
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout milliseconds (-1 indicates wait forever)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - 0 Timeout
|
* - 0 Timeout
|
||||||
|
@ -92,6 +92,16 @@ void esp_transport_ssl_set_client_cert_data_der(esp_transport_handle_t t, const
|
|||||||
*/
|
*/
|
||||||
void esp_transport_ssl_set_client_key_data(esp_transport_handle_t t, const char *data, int len);
|
void esp_transport_ssl_set_client_key_data(esp_transport_handle_t t, const char *data, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set SSL client key password if the key is password protected. The configured
|
||||||
|
* password is passed to the underlying TLS stack to decrypt the client key
|
||||||
|
*
|
||||||
|
* @param t ssl transport
|
||||||
|
* @param[in] password Pointer to the password
|
||||||
|
* @param[in] password_len Password length
|
||||||
|
*/
|
||||||
|
void esp_transport_ssl_set_client_key_password(esp_transport_handle_t t, const char *password, int password_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set SSL client key data for mutual authentication (as DER format).
|
* @brief Set SSL client key data for mutual authentication (as DER format).
|
||||||
* Note that, this function stores the pointer to data, rather than making a copy.
|
* Note that, this function stores the pointer to data, rather than making a copy.
|
||||||
@ -103,6 +113,16 @@ void esp_transport_ssl_set_client_key_data(esp_transport_handle_t t, const char
|
|||||||
*/
|
*/
|
||||||
void esp_transport_ssl_set_client_key_data_der(esp_transport_handle_t t, const char *data, int len);
|
void esp_transport_ssl_set_client_key_data_der(esp_transport_handle_t t, const char *data, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the list of supported application protocols to be used with ALPN.
|
||||||
|
* Note that, this function stores the pointer to data, rather than making a copy.
|
||||||
|
* So this data must remain valid until after the connection is cleaned up
|
||||||
|
*
|
||||||
|
* @param t ssl transport
|
||||||
|
* @param[in] alpn_porot The list of ALPN protocols, the last entry must be NULL
|
||||||
|
*/
|
||||||
|
void esp_transport_ssl_set_alpn_protocol(esp_transport_handle_t t, const char **alpn_protos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Skip validation of certificate's common name field
|
* @brief Skip validation of certificate's common name field
|
||||||
*
|
*
|
||||||
|
@ -14,6 +14,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum ws_transport_opcodes {
|
typedef enum ws_transport_opcodes {
|
||||||
|
WS_TRANSPORT_OPCODES_CONT = 0x00,
|
||||||
WS_TRANSPORT_OPCODES_TEXT = 0x01,
|
WS_TRANSPORT_OPCODES_TEXT = 0x01,
|
||||||
WS_TRANSPORT_OPCODES_BINARY = 0x02,
|
WS_TRANSPORT_OPCODES_BINARY = 0x02,
|
||||||
WS_TRANSPORT_OPCODES_CLOSE = 0x08,
|
WS_TRANSPORT_OPCODES_CLOSE = 0x08,
|
||||||
@ -50,6 +51,30 @@ void esp_transport_ws_set_path(esp_transport_handle_t t, const char *path);
|
|||||||
*/
|
*/
|
||||||
esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char *sub_protocol);
|
esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char *sub_protocol);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set websocket user-agent header
|
||||||
|
*
|
||||||
|
* @param t websocket transport handle
|
||||||
|
* @param sub_protocol user-agent string
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - One of the error codes
|
||||||
|
*/
|
||||||
|
esp_err_t esp_transport_ws_set_user_agent(esp_transport_handle_t t, const char *user_agent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set websocket additional headers
|
||||||
|
*
|
||||||
|
* @param t websocket transport handle
|
||||||
|
* @param sub_protocol additional header strings each terminated with \r\n
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - One of the error codes
|
||||||
|
*/
|
||||||
|
esp_err_t esp_transport_ws_set_headers(esp_transport_handle_t t, const char *headers);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sends websocket raw message with custom opcode and payload
|
* @brief Sends websocket raw message with custom opcode and payload
|
||||||
*
|
*
|
||||||
@ -63,7 +88,7 @@ esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char
|
|||||||
* @param[in] opcode ws operation code
|
* @param[in] opcode ws operation code
|
||||||
* @param[in] buffer The buffer
|
* @param[in] buffer The buffer
|
||||||
* @param[in] len The length
|
* @param[in] len The length
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout milliseconds (-1 indicates block forever)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - Number of bytes was written
|
* - Number of bytes was written
|
||||||
@ -81,6 +106,16 @@ int esp_transport_ws_send_raw(esp_transport_handle_t t, ws_transport_opcodes_t o
|
|||||||
*/
|
*/
|
||||||
ws_transport_opcodes_t esp_transport_ws_get_read_opcode(esp_transport_handle_t t);
|
ws_transport_opcodes_t esp_transport_ws_get_read_opcode(esp_transport_handle_t t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns payload length of the last received data
|
||||||
|
*
|
||||||
|
* @param t websocket transport handle
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - Number of bytes in the payload
|
||||||
|
*/
|
||||||
|
int esp_transport_ws_get_read_payload_len(esp_transport_handle_t t);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,17 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert milliseconds to timeval struct
|
* @brief Convert milliseconds to timeval struct for valid timeouts, otherwise
|
||||||
|
* (if "wait forever" requested by timeout_ms=-1) timeval structure is not updated and NULL returned
|
||||||
*
|
*
|
||||||
* @param[in] timeout_ms The timeout milliseconds
|
* @param[in] timeout_ms The timeout value in milliseconds or -1 to waiting forever
|
||||||
* @param[out] tv Timeval struct
|
* @param[out] tv Pointer to timeval struct
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - NULL if timeout_ms=-1 (wait forever)
|
||||||
|
* - pointer to the updated timeval structure (provided as "tv" argument) with recalculated timeout value
|
||||||
*/
|
*/
|
||||||
void esp_transport_utils_ms_to_timeval(int timeout_ms, struct timeval *tv);
|
struct timeval* esp_transport_utils_ms_to_timeval(int timeout_ms, struct timeval *tv);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
@ -1,5 +1,3 @@
|
|||||||
set(COMPONENT_SRCDIRS ".")
|
idf_component_register(SRC_DIRS "."
|
||||||
set(COMPONENT_PRIV_INCLUDEDIRS "../private_include" ".")
|
PRIV_INCLUDE_DIRS "../private_include" "."
|
||||||
set(COMPONENT_PRIV_REQUIRES unity test_utils tcp_transport)
|
PRIV_REQUIRES unity test_utils tcp_transport)
|
||||||
|
|
||||||
register_component()
|
|
@ -71,7 +71,7 @@ static int ssl_connect(esp_transport_handle_t t, const char *host, int port, int
|
|||||||
ssl->cfg.timeout_ms = timeout_ms;
|
ssl->cfg.timeout_ms = timeout_ms;
|
||||||
ssl->ssl_initialized = true;
|
ssl->ssl_initialized = true;
|
||||||
ssl->tls = esp_tls_init();
|
ssl->tls = esp_tls_init();
|
||||||
if (esp_tls_conn_new_sync(host, strlen(host), port, &ssl->cfg, ssl->tls) < 0) {
|
if (esp_tls_conn_new_sync(host, strlen(host), port, &ssl->cfg, ssl->tls) <= 0) {
|
||||||
ESP_LOGE(TAG, "Failed to open a new connection");
|
ESP_LOGE(TAG, "Failed to open a new connection");
|
||||||
esp_transport_set_errors(t, ssl->tls->error_handle);
|
esp_transport_set_errors(t, ssl->tls->error_handle);
|
||||||
esp_tls_conn_delete(ssl->tls);
|
esp_tls_conn_delete(ssl->tls);
|
||||||
@ -86,16 +86,15 @@ static int ssl_poll_read(esp_transport_handle_t t, int timeout_ms)
|
|||||||
{
|
{
|
||||||
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
struct timeval timeout;
|
||||||
fd_set readset;
|
fd_set readset;
|
||||||
fd_set errset;
|
fd_set errset;
|
||||||
FD_ZERO(&readset);
|
FD_ZERO(&readset);
|
||||||
FD_ZERO(&errset);
|
FD_ZERO(&errset);
|
||||||
FD_SET(ssl->tls->sockfd, &readset);
|
FD_SET(ssl->tls->sockfd, &readset);
|
||||||
FD_SET(ssl->tls->sockfd, &errset);
|
FD_SET(ssl->tls->sockfd, &errset);
|
||||||
struct timeval timeout;
|
|
||||||
esp_transport_utils_ms_to_timeval(timeout_ms, &timeout);
|
|
||||||
|
|
||||||
ret = select(ssl->tls->sockfd + 1, &readset, NULL, &errset, &timeout);
|
ret = select(ssl->tls->sockfd + 1, &readset, NULL, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
|
||||||
if (ret > 0 && FD_ISSET(ssl->tls->sockfd, &errset)) {
|
if (ret > 0 && FD_ISSET(ssl->tls->sockfd, &errset)) {
|
||||||
int sock_errno = 0;
|
int sock_errno = 0;
|
||||||
uint32_t optlen = sizeof(sock_errno);
|
uint32_t optlen = sizeof(sock_errno);
|
||||||
@ -110,15 +109,14 @@ static int ssl_poll_write(esp_transport_handle_t t, int timeout_ms)
|
|||||||
{
|
{
|
||||||
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
struct timeval timeout;
|
||||||
fd_set writeset;
|
fd_set writeset;
|
||||||
fd_set errset;
|
fd_set errset;
|
||||||
FD_ZERO(&writeset);
|
FD_ZERO(&writeset);
|
||||||
FD_ZERO(&errset);
|
FD_ZERO(&errset);
|
||||||
FD_SET(ssl->tls->sockfd, &writeset);
|
FD_SET(ssl->tls->sockfd, &writeset);
|
||||||
FD_SET(ssl->tls->sockfd, &errset);
|
FD_SET(ssl->tls->sockfd, &errset);
|
||||||
struct timeval timeout;
|
ret = select(ssl->tls->sockfd + 1, NULL, &writeset, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
|
||||||
esp_transport_utils_ms_to_timeval(timeout_ms, &timeout);
|
|
||||||
ret = select(ssl->tls->sockfd + 1, NULL, &writeset, &errset, &timeout);
|
|
||||||
if (ret > 0 && FD_ISSET(ssl->tls->sockfd, &errset)) {
|
if (ret > 0 && FD_ISSET(ssl->tls->sockfd, &errset)) {
|
||||||
int sock_errno = 0;
|
int sock_errno = 0;
|
||||||
uint32_t optlen = sizeof(sock_errno);
|
uint32_t optlen = sizeof(sock_errno);
|
||||||
@ -247,6 +245,15 @@ void esp_transport_ssl_set_client_key_data(esp_transport_handle_t t, const char
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void esp_transport_ssl_set_client_key_password(esp_transport_handle_t t, const char *password, int password_len)
|
||||||
|
{
|
||||||
|
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
||||||
|
if (t && ssl) {
|
||||||
|
ssl->cfg.clientkey_password = (void *)password;
|
||||||
|
ssl->cfg.clientkey_password_len = password_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void esp_transport_ssl_set_client_key_data_der(esp_transport_handle_t t, const char *data, int len)
|
void esp_transport_ssl_set_client_key_data_der(esp_transport_handle_t t, const char *data, int len)
|
||||||
{
|
{
|
||||||
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
||||||
@ -256,6 +263,14 @@ void esp_transport_ssl_set_client_key_data_der(esp_transport_handle_t t, const c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void esp_transport_ssl_set_alpn_protocol(esp_transport_handle_t t, const char **alpn_protos)
|
||||||
|
{
|
||||||
|
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
||||||
|
if (t && ssl) {
|
||||||
|
ssl->cfg.alpn_protos = alpn_protos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void esp_transport_ssl_skip_common_name_check(esp_transport_handle_t t)
|
void esp_transport_ssl_skip_common_name_check(esp_transport_handle_t t)
|
||||||
{
|
{
|
||||||
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
transport_ssl_t *ssl = esp_transport_get_context_data(t);
|
||||||
|
@ -52,7 +52,7 @@ static int resolve_dns(const char *host, struct sockaddr_in *ip) {
|
|||||||
static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
|
static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
|
||||||
{
|
{
|
||||||
struct sockaddr_in remote_ip;
|
struct sockaddr_in remote_ip;
|
||||||
struct timeval tv;
|
struct timeval tv = { 0 };
|
||||||
transport_tcp_t *tcp = esp_transport_get_context_data(t);
|
transport_tcp_t *tcp = esp_transport_get_context_data(t);
|
||||||
|
|
||||||
bzero(&remote_ip, sizeof(struct sockaddr_in));
|
bzero(&remote_ip, sizeof(struct sockaddr_in));
|
||||||
@ -74,7 +74,7 @@ static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int
|
|||||||
remote_ip.sin_family = AF_INET;
|
remote_ip.sin_family = AF_INET;
|
||||||
remote_ip.sin_port = htons(port);
|
remote_ip.sin_port = htons(port);
|
||||||
|
|
||||||
esp_transport_utils_ms_to_timeval(timeout_ms, &tv);
|
esp_transport_utils_ms_to_timeval(timeout_ms, &tv); // if timeout=-1, tv is unchanged, 0, i.e. waits forever
|
||||||
|
|
||||||
setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||||
setsockopt(tcp->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
setsockopt(tcp->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
||||||
@ -117,15 +117,15 @@ static int tcp_poll_read(esp_transport_handle_t t, int timeout_ms)
|
|||||||
{
|
{
|
||||||
transport_tcp_t *tcp = esp_transport_get_context_data(t);
|
transport_tcp_t *tcp = esp_transport_get_context_data(t);
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
struct timeval timeout;
|
||||||
fd_set readset;
|
fd_set readset;
|
||||||
fd_set errset;
|
fd_set errset;
|
||||||
FD_ZERO(&readset);
|
FD_ZERO(&readset);
|
||||||
FD_ZERO(&errset);
|
FD_ZERO(&errset);
|
||||||
FD_SET(tcp->sock, &readset);
|
FD_SET(tcp->sock, &readset);
|
||||||
FD_SET(tcp->sock, &errset);
|
FD_SET(tcp->sock, &errset);
|
||||||
struct timeval timeout;
|
|
||||||
esp_transport_utils_ms_to_timeval(timeout_ms, &timeout);
|
ret = select(tcp->sock + 1, &readset, NULL, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
|
||||||
ret = select(tcp->sock + 1, &readset, NULL, &errset, &timeout);
|
|
||||||
if (ret > 0 && FD_ISSET(tcp->sock, &errset)) {
|
if (ret > 0 && FD_ISSET(tcp->sock, &errset)) {
|
||||||
int sock_errno = 0;
|
int sock_errno = 0;
|
||||||
uint32_t optlen = sizeof(sock_errno);
|
uint32_t optlen = sizeof(sock_errno);
|
||||||
@ -140,15 +140,15 @@ static int tcp_poll_write(esp_transport_handle_t t, int timeout_ms)
|
|||||||
{
|
{
|
||||||
transport_tcp_t *tcp = esp_transport_get_context_data(t);
|
transport_tcp_t *tcp = esp_transport_get_context_data(t);
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
struct timeval timeout;
|
||||||
fd_set writeset;
|
fd_set writeset;
|
||||||
fd_set errset;
|
fd_set errset;
|
||||||
FD_ZERO(&writeset);
|
FD_ZERO(&writeset);
|
||||||
FD_ZERO(&errset);
|
FD_ZERO(&errset);
|
||||||
FD_SET(tcp->sock, &writeset);
|
FD_SET(tcp->sock, &writeset);
|
||||||
FD_SET(tcp->sock, &errset);
|
FD_SET(tcp->sock, &errset);
|
||||||
struct timeval timeout;
|
|
||||||
esp_transport_utils_ms_to_timeval(timeout_ms, &timeout);
|
ret = select(tcp->sock + 1, NULL, &writeset, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
|
||||||
ret = select(tcp->sock + 1, NULL, &writeset, &errset, &timeout);
|
|
||||||
if (ret > 0 && FD_ISSET(tcp->sock, &errset)) {
|
if (ret > 0 && FD_ISSET(tcp->sock, &errset)) {
|
||||||
int sock_errno = 0;
|
int sock_errno = 0;
|
||||||
uint32_t optlen = sizeof(sock_errno);
|
uint32_t optlen = sizeof(sock_errno);
|
||||||
|
@ -6,8 +6,12 @@
|
|||||||
|
|
||||||
#include "esp_transport_utils.h"
|
#include "esp_transport_utils.h"
|
||||||
|
|
||||||
void esp_transport_utils_ms_to_timeval(int timeout_ms, struct timeval *tv)
|
struct timeval* esp_transport_utils_ms_to_timeval(int timeout_ms, struct timeval *tv)
|
||||||
{
|
{
|
||||||
|
if (timeout_ms == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
tv->tv_sec = timeout_ms / 1000;
|
tv->tv_sec = timeout_ms / 1000;
|
||||||
tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000;
|
tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000;
|
||||||
|
return tv;
|
||||||
}
|
}
|
@ -2,7 +2,6 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <sys/random.h>
|
#include <sys/random.h>
|
||||||
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_transport.h"
|
#include "esp_transport.h"
|
||||||
#include "esp_transport_tcp.h"
|
#include "esp_transport_tcp.h"
|
||||||
@ -15,11 +14,13 @@ static const char *TAG = "TRANSPORT_WS";
|
|||||||
|
|
||||||
#define DEFAULT_WS_BUFFER (1024)
|
#define DEFAULT_WS_BUFFER (1024)
|
||||||
#define WS_FIN 0x80
|
#define WS_FIN 0x80
|
||||||
|
#define WS_OPCODE_CONT 0x00
|
||||||
#define WS_OPCODE_TEXT 0x01
|
#define WS_OPCODE_TEXT 0x01
|
||||||
#define WS_OPCODE_BINARY 0x02
|
#define WS_OPCODE_BINARY 0x02
|
||||||
#define WS_OPCODE_CLOSE 0x08
|
#define WS_OPCODE_CLOSE 0x08
|
||||||
#define WS_OPCODE_PING 0x09
|
#define WS_OPCODE_PING 0x09
|
||||||
#define WS_OPCODE_PONG 0x0a
|
#define WS_OPCODE_PONG 0x0a
|
||||||
|
|
||||||
// Second byte
|
// Second byte
|
||||||
#define WS_MASK 0x80
|
#define WS_MASK 0x80
|
||||||
#define WS_SIZE16 126
|
#define WS_SIZE16 126
|
||||||
@ -27,11 +28,21 @@ static const char *TAG = "TRANSPORT_WS";
|
|||||||
#define MAX_WEBSOCKET_HEADER_SIZE 16
|
#define MAX_WEBSOCKET_HEADER_SIZE 16
|
||||||
#define WS_RESPONSE_OK 101
|
#define WS_RESPONSE_OK 101
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t opcode;
|
||||||
|
char mask_key[4]; /*!< Mask key for this payload */
|
||||||
|
int payload_len; /*!< Total length of the payload */
|
||||||
|
int bytes_remaining; /*!< Bytes left to read of the payload */
|
||||||
|
} ws_transport_frame_state_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *path;
|
char *path;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
char *sub_protocol;
|
char *sub_protocol;
|
||||||
uint8_t read_opcode;
|
char *user_agent;
|
||||||
|
char *headers;
|
||||||
|
ws_transport_frame_state_t frame_state;
|
||||||
esp_transport_handle_t parent;
|
esp_transport_handle_t parent;
|
||||||
} transport_ws_t;
|
} transport_ws_t;
|
||||||
|
|
||||||
@ -43,6 +54,11 @@ static inline uint8_t ws_get_bin_opcode(ws_transport_opcodes_t opcode)
|
|||||||
static esp_transport_handle_t ws_get_payload_transport_handle(esp_transport_handle_t t)
|
static esp_transport_handle_t ws_get_payload_transport_handle(esp_transport_handle_t t)
|
||||||
{
|
{
|
||||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
|
||||||
|
/* Reading parts of a frame directly will disrupt the WS internal frame state,
|
||||||
|
reset bytes_remaining to prepare for reading a new frame */
|
||||||
|
ws->frame_state.bytes_remaining = 0;
|
||||||
|
|
||||||
return ws->parent;
|
return ws->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,24 +112,27 @@ static int ws_connect(esp_transport_handle_t t, const char *host, int port, int
|
|||||||
// Size of base64 coded string is equal '((input_size * 4) / 3) + (input_size / 96) + 6' including Z-term
|
// Size of base64 coded string is equal '((input_size * 4) / 3) + (input_size / 96) + 6' including Z-term
|
||||||
unsigned char client_key[28] = {0};
|
unsigned char client_key[28] = {0};
|
||||||
|
|
||||||
|
const char *user_agent_ptr = (ws->user_agent)?(ws->user_agent):"ESP32 Websocket Client";
|
||||||
|
|
||||||
size_t outlen = 0;
|
size_t outlen = 0;
|
||||||
mbedtls_base64_encode(client_key, sizeof(client_key), &outlen, random_key, sizeof(random_key));
|
mbedtls_base64_encode(client_key, sizeof(client_key), &outlen, random_key, sizeof(random_key));
|
||||||
int len = snprintf(ws->buffer, DEFAULT_WS_BUFFER,
|
int len = snprintf(ws->buffer, DEFAULT_WS_BUFFER,
|
||||||
"GET %s HTTP/1.1\r\n"
|
"GET %s HTTP/1.1\r\n"
|
||||||
"Connection: Upgrade\r\n"
|
"Connection: Upgrade\r\n"
|
||||||
"Host: %s:%d\r\n"
|
"Host: %s:%d\r\n"
|
||||||
|
"User-Agent: %s\r\n"
|
||||||
"Upgrade: websocket\r\n"
|
"Upgrade: websocket\r\n"
|
||||||
"Sec-WebSocket-Version: 13\r\n"
|
"Sec-WebSocket-Version: 13\r\n"
|
||||||
"Sec-WebSocket-Key: %s\r\n"
|
"Sec-WebSocket-Key: %s\r\n",
|
||||||
"User-Agent: ESP32 Websocket Client\r\n",
|
|
||||||
ws->path,
|
ws->path,
|
||||||
host, port,
|
host, port, user_agent_ptr,
|
||||||
client_key);
|
client_key);
|
||||||
if (len <= 0 || len >= DEFAULT_WS_BUFFER) {
|
if (len <= 0 || len >= DEFAULT_WS_BUFFER) {
|
||||||
ESP_LOGE(TAG, "Error in request generation, %d", len);
|
ESP_LOGE(TAG, "Error in request generation, %d", len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (ws->sub_protocol) {
|
if (ws->sub_protocol) {
|
||||||
|
ESP_LOGD(TAG, "sub_protocol: %s", ws->sub_protocol);
|
||||||
int r = snprintf(ws->buffer + len, DEFAULT_WS_BUFFER - len, "Sec-WebSocket-Protocol: %s\r\n", ws->sub_protocol);
|
int r = snprintf(ws->buffer + len, DEFAULT_WS_BUFFER - len, "Sec-WebSocket-Protocol: %s\r\n", ws->sub_protocol);
|
||||||
len += r;
|
len += r;
|
||||||
if (r <= 0 || len >= DEFAULT_WS_BUFFER) {
|
if (r <= 0 || len >= DEFAULT_WS_BUFFER) {
|
||||||
@ -122,6 +141,16 @@ static int ws_connect(esp_transport_handle_t t, const char *host, int port, int
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ws->headers) {
|
||||||
|
ESP_LOGD(TAG, "headers: %s", ws->headers);
|
||||||
|
int r = snprintf(ws->buffer + len, DEFAULT_WS_BUFFER - len, "%s", ws->headers);
|
||||||
|
len += r;
|
||||||
|
if (r <= 0 || len >= DEFAULT_WS_BUFFER) {
|
||||||
|
ESP_LOGE(TAG, "Error in request generation"
|
||||||
|
"(strncpy of headers returned %d, desired request len: %d, buffer size: %d", r, len, DEFAULT_WS_BUFFER);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
int r = snprintf(ws->buffer + len, DEFAULT_WS_BUFFER - len, "\r\n");
|
int r = snprintf(ws->buffer + len, DEFAULT_WS_BUFFER - len, "\r\n");
|
||||||
len += r;
|
len += r;
|
||||||
if (r <= 0 || len >= DEFAULT_WS_BUFFER) {
|
if (r <= 0 || len >= DEFAULT_WS_BUFFER) {
|
||||||
@ -260,12 +289,46 @@ static int ws_write(esp_transport_handle_t t, const char *b, int len, int timeou
|
|||||||
return _ws_write(t, WS_OPCODE_BINARY | WS_FIN, WS_MASK, b, len, timeout_ms);
|
return _ws_write(t, WS_OPCODE_BINARY | WS_FIN, WS_MASK, b, len, timeout_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
|
|
||||||
|
static int ws_read_payload(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
|
||||||
|
{
|
||||||
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
|
||||||
|
int bytes_to_read;
|
||||||
|
int rlen = 0;
|
||||||
|
|
||||||
|
if (ws->frame_state.bytes_remaining > len) {
|
||||||
|
ESP_LOGD(TAG, "Actual data to receive (%d) are longer than ws buffer (%d)", ws->frame_state.bytes_remaining, len);
|
||||||
|
bytes_to_read = len;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
bytes_to_read = ws->frame_state.bytes_remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive and process payload
|
||||||
|
if (bytes_to_read != 0 && (rlen = esp_transport_read(ws->parent, buffer, bytes_to_read, timeout_ms)) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Error read data");
|
||||||
|
return rlen;
|
||||||
|
}
|
||||||
|
ws->frame_state.bytes_remaining -= rlen;
|
||||||
|
|
||||||
|
if (ws->frame_state.mask_key) {
|
||||||
|
for (int i = 0; i < bytes_to_read; i++) {
|
||||||
|
buffer[i] = (buffer[i] ^ ws->frame_state.mask_key[i % 4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read and parse the WS header, determine length of payload */
|
||||||
|
static int ws_read_header(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
|
||||||
{
|
{
|
||||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
int payload_len;
|
int payload_len;
|
||||||
|
|
||||||
char ws_header[MAX_WEBSOCKET_HEADER_SIZE];
|
char ws_header[MAX_WEBSOCKET_HEADER_SIZE];
|
||||||
char *data_ptr = ws_header, mask, *mask_key = NULL;
|
char *data_ptr = ws_header, mask;
|
||||||
int rlen;
|
int rlen;
|
||||||
int poll_read;
|
int poll_read;
|
||||||
if ((poll_read = esp_transport_poll_read(ws->parent, timeout_ms)) <= 0) {
|
if ((poll_read = esp_transport_poll_read(ws->parent, timeout_ms)) <= 0) {
|
||||||
@ -274,16 +337,17 @@ static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_
|
|||||||
|
|
||||||
// Receive and process header first (based on header size)
|
// Receive and process header first (based on header size)
|
||||||
int header = 2;
|
int header = 2;
|
||||||
|
int mask_len = 4;
|
||||||
if ((rlen = esp_transport_read(ws->parent, data_ptr, header, timeout_ms)) <= 0) {
|
if ((rlen = esp_transport_read(ws->parent, data_ptr, header, timeout_ms)) <= 0) {
|
||||||
ESP_LOGE(TAG, "Error read data");
|
ESP_LOGE(TAG, "Error read data");
|
||||||
return rlen;
|
return rlen;
|
||||||
}
|
}
|
||||||
ws->read_opcode = (*data_ptr & 0x0F);
|
ws->frame_state.opcode = (*data_ptr & 0x0F);
|
||||||
data_ptr ++;
|
data_ptr ++;
|
||||||
mask = ((*data_ptr >> 7) & 0x01);
|
mask = ((*data_ptr >> 7) & 0x01);
|
||||||
payload_len = (*data_ptr & 0x7F);
|
payload_len = (*data_ptr & 0x7F);
|
||||||
data_ptr++;
|
data_ptr++;
|
||||||
ESP_LOGD(TAG, "Opcode: %d, mask: %d, len: %d\r\n", ws->read_opcode, mask, payload_len);
|
ESP_LOGD(TAG, "Opcode: %d, mask: %d, len: %d\r\n", ws->frame_state.opcode, mask, payload_len);
|
||||||
if (payload_len == 126) {
|
if (payload_len == 126) {
|
||||||
// headerLen += 2;
|
// headerLen += 2;
|
||||||
if ((rlen = esp_transport_read(ws->parent, data_ptr, header, timeout_ms)) <= 0) {
|
if ((rlen = esp_transport_read(ws->parent, data_ptr, header, timeout_ms)) <= 0) {
|
||||||
@ -307,27 +371,48 @@ static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload_len > len) {
|
|
||||||
ESP_LOGD(TAG, "Actual data to receive (%d) are longer than ws buffer (%d)", payload_len, len);
|
|
||||||
payload_len = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then receive and process payload
|
|
||||||
if (payload_len != 0 && (rlen = esp_transport_read(ws->parent, buffer, payload_len, timeout_ms)) <= 0) {
|
|
||||||
ESP_LOGE(TAG, "Error read data");
|
|
||||||
return rlen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mask) {
|
if (mask) {
|
||||||
mask_key = buffer;
|
// Read and store mask
|
||||||
data_ptr = buffer + 4;
|
if (payload_len != 0 && (rlen = esp_transport_read(ws->parent, buffer, mask_len, timeout_ms)) <= 0) {
|
||||||
for (int i = 0; i < payload_len; i++) {
|
ESP_LOGE(TAG, "Error read data");
|
||||||
buffer[i] = (data_ptr[i] ^ mask_key[i % 4]);
|
return rlen;
|
||||||
}
|
}
|
||||||
|
memcpy(ws->frame_state.mask_key, buffer, mask_len);
|
||||||
|
} else {
|
||||||
|
memset(ws->frame_state.mask_key, 0, mask_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ws->frame_state.payload_len = payload_len;
|
||||||
|
ws->frame_state.bytes_remaining = payload_len;
|
||||||
|
|
||||||
return payload_len;
|
return payload_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ws_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
|
||||||
|
{
|
||||||
|
int rlen = 0;
|
||||||
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
|
||||||
|
// If message exceeds buffer len then subsequent reads will skip reading header and read whatever is left of the payload
|
||||||
|
if (ws->frame_state.bytes_remaining <= 0) {
|
||||||
|
if ( (rlen = ws_read_header(t, buffer, len, timeout_ms)) <= 0) {
|
||||||
|
// If something when wrong then we prepare for reading a new header
|
||||||
|
ws->frame_state.bytes_remaining = 0;
|
||||||
|
return rlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ws->frame_state.payload_len) {
|
||||||
|
if ( (rlen = ws_read_payload(t, buffer, len, timeout_ms)) <= 0) {
|
||||||
|
ESP_LOGE(TAG, "Error reading payload data");
|
||||||
|
ws->frame_state.bytes_remaining = 0;
|
||||||
|
return rlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int ws_poll_read(esp_transport_handle_t t, int timeout_ms)
|
static int ws_poll_read(esp_transport_handle_t t, int timeout_ms)
|
||||||
{
|
{
|
||||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
@ -352,6 +437,8 @@ static esp_err_t ws_destroy(esp_transport_handle_t t)
|
|||||||
free(ws->buffer);
|
free(ws->buffer);
|
||||||
free(ws->path);
|
free(ws->path);
|
||||||
free(ws->sub_protocol);
|
free(ws->sub_protocol);
|
||||||
|
free(ws->user_agent);
|
||||||
|
free(ws->headers);
|
||||||
free(ws);
|
free(ws);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -409,8 +496,56 @@ esp_err_t esp_transport_ws_set_subprotocol(esp_transport_handle_t t, const char
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_transport_ws_set_user_agent(esp_transport_handle_t t, const char *user_agent)
|
||||||
|
{
|
||||||
|
if (t == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
if (ws->user_agent) {
|
||||||
|
free(ws->user_agent);
|
||||||
|
}
|
||||||
|
if (user_agent == NULL) {
|
||||||
|
ws->user_agent = NULL;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
ws->user_agent = strdup(user_agent);
|
||||||
|
if (ws->user_agent == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_transport_ws_set_headers(esp_transport_handle_t t, const char *headers)
|
||||||
|
{
|
||||||
|
if (t == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
if (ws->headers) {
|
||||||
|
free(ws->headers);
|
||||||
|
}
|
||||||
|
if (headers == NULL) {
|
||||||
|
ws->headers = NULL;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
ws->headers = strdup(headers);
|
||||||
|
if (ws->headers == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
ws_transport_opcodes_t esp_transport_ws_get_read_opcode(esp_transport_handle_t t)
|
ws_transport_opcodes_t esp_transport_ws_get_read_opcode(esp_transport_handle_t t)
|
||||||
{
|
{
|
||||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
return ws->read_opcode;
|
return ws->frame_state.opcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int esp_transport_ws_get_read_payload_len(esp_transport_handle_t t)
|
||||||
|
{
|
||||||
|
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||||
|
return ws->frame_state.payload_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user