tcp_transport/esp_tls: Use common TCP transport to reduce code duplication

For high level review of the changes.
This commit is contained in:
David Cermak 2021-01-23 09:46:06 +01:00
parent 391d7bf271
commit 2c28ffffee
7 changed files with 74 additions and 356 deletions

View File

@ -276,7 +276,7 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
switch (tls->conn_state) {
case ESP_TLS_INIT:
tls->sockfd = -1;
if (cfg != NULL) {
if (cfg != NULL && cfg->is_plain_tcp == false) {
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
mbedtls_net_init(&tls->server_fd);
#endif
@ -286,7 +286,7 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_ESP, esp_ret);
return -1;
}
if (!cfg) {
if (tls->is_tls == false) {
tls->read = tcp_read;
tls->write = tcp_write;
ESP_LOGD(TAG, "non-tls connection established");

View File

@ -170,6 +170,7 @@ typedef struct esp_tls_cfg {
bundle for server verification, must be enabled in menuconfig */
void *ds_data; /*!< Pointer for digital signature peripheral context */
bool is_plain_tcp;
} esp_tls_cfg_t;
#ifdef CONFIG_ESP_TLS_SERVER

View File

@ -1,6 +1,5 @@
idf_component_register(SRCS "transport.c"
"transport_ssl.c"
"transport_tcp.c"
"transport_ws.c"
"transport_utils.c"
INCLUDE_DIRS "include"

View File

@ -20,6 +20,8 @@
typedef int (*get_socket_func)(esp_transport_handle_t t);
struct transport_esp_tls;
/**
* Transport layer structure, which will provide functions, basic properties for transport types
*/
@ -40,6 +42,7 @@ struct esp_transport_item_t {
struct esp_transport_error_s* error_handle; /*!< Error handle (based on esp-tls error handle)
* extended with transport's specific errors */
esp_transport_keep_alive_t *keep_alive_cfg; /*!< TCP keep-alive config */
struct transport_esp_tls *foundation_transport;
STAILQ_ENTRY(esp_transport_item_t) next;
};
@ -86,4 +89,6 @@ int esp_transport_get_socket(esp_transport_handle_t t);
*/
void esp_transport_capture_errno(esp_transport_handle_t t, int sock_errno);
struct transport_esp_tls* esp_transport_init_foundation(void);
#endif //_ESP_TRANSPORT_INTERNAL_H_

View File

@ -23,7 +23,6 @@
#include "esp_transport.h"
#include "esp_transport_internal.h"
#include "esp_transport_utils.h"
#include "esp_tls_errors.h"
static const char *TAG = "TRANSPORT";
@ -43,12 +42,15 @@ struct esp_transport_error_s {
*/
STAILQ_HEAD(esp_transport_list_t, esp_transport_item_t);
struct transport_esp_tls;
/**
* Internal transport structure holding list of transports and other data common to all transports
*/
typedef struct esp_transport_internal {
struct esp_transport_list_t list; /*!< List of transports */
struct esp_transport_error_s* error_handle; /*!< Pointer to the transport error container */
struct transport_esp_tls *foundation_transport;
} esp_transport_internal_t;
static esp_transport_handle_t esp_transport_get_default_parent(esp_transport_handle_t t)
@ -65,6 +67,7 @@ esp_transport_list_handle_t esp_transport_list_init(void)
ESP_TRANSPORT_MEM_CHECK(TAG, transport, return NULL);
STAILQ_INIT(&transport->list);
transport->error_handle = calloc(1, sizeof(struct esp_transport_error_s));
transport->foundation_transport = esp_transport_init_foundation();
return transport;
}
@ -79,6 +82,7 @@ esp_err_t esp_transport_list_add(esp_transport_list_handle_t h, esp_transport_ha
STAILQ_INSERT_TAIL(&h->list, t, next);
// Each transport in a list to share the same error tracker
t->error_handle = h->error_handle;
t->foundation_transport = h->foundation_transport;
return ESP_OK;
}
@ -103,6 +107,7 @@ esp_err_t esp_transport_list_destroy(esp_transport_list_handle_t h)
{
esp_transport_list_clean(h);
free(h->error_handle);
free(h->foundation_transport); // TODO: make it destroy foundation
free(h);
return ESP_OK;
}

View File

@ -37,7 +37,7 @@ typedef enum {
/**
* mbedtls specific transport data
*/
typedef struct {
typedef struct transport_esp_tls {
esp_tls_t *tls;
esp_tls_cfg_t cfg;
bool ssl_initialized;
@ -48,7 +48,7 @@ static int ssl_close(esp_transport_handle_t t);
static int ssl_connect_async(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (ssl->conn_state == TRANS_SSL_INIT) {
ssl->cfg.timeout_ms = timeout_ms;
ssl->cfg.non_block = true;
@ -67,7 +67,7 @@ static int ssl_connect_async(esp_transport_handle_t t, const char *host, int por
static int ssl_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
ssl->cfg.timeout_ms = timeout_ms;
ssl->ssl_initialized = true;
@ -83,9 +83,29 @@ static int ssl_connect(esp_transport_handle_t t, const char *host, int port, int
return 0;
}
static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
transport_ssl_t *ssl = t->foundation_transport;
ssl->cfg.timeout_ms = timeout_ms;
ssl->cfg.is_plain_tcp = true;
ssl->ssl_initialized = true;
ssl->tls = esp_tls_init();
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_transport_set_errors(t, ssl->tls->error_handle);
esp_tls_conn_destroy(ssl->tls);
ssl->tls = NULL;
return -1;
}
return 0;
}
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 = t->foundation_transport;
int ret = -1;
int remain = 0;
struct timeval timeout;
@ -114,7 +134,7 @@ static int ssl_poll_read(esp_transport_handle_t t, int timeout_ms)
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 = t->foundation_transport;
int ret = -1;
struct timeval timeout;
fd_set writeset;
@ -138,7 +158,7 @@ static int ssl_poll_write(esp_transport_handle_t t, int timeout_ms)
static int ssl_write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
int poll, ret;
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if ((poll = esp_transport_poll_write(t, timeout_ms)) <= 0) {
ESP_LOGW(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d", strerror(errno), ssl->tls->sockfd, timeout_ms);
@ -155,7 +175,7 @@ static int ssl_write(esp_transport_handle_t t, const char *buffer, int len, int
static int ssl_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
{
int poll, ret;
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if ((poll = esp_transport_poll_read(t, timeout_ms)) <= 0) {
return poll;
@ -178,7 +198,7 @@ static int ssl_read(esp_transport_handle_t t, char *buffer, int len, int timeout
static int ssl_close(esp_transport_handle_t t)
{
int ret = -1;
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (ssl->ssl_initialized) {
ret = esp_tls_conn_destroy(ssl->tls);
ssl->conn_state = TRANS_SSL_INIT;
@ -189,7 +209,7 @@ static int ssl_close(esp_transport_handle_t t)
static int ssl_destroy(esp_transport_handle_t t)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
esp_transport_close(t);
free(ssl);
return 0;
@ -197,7 +217,7 @@ static int ssl_destroy(esp_transport_handle_t t)
void esp_transport_ssl_enable_global_ca_store(esp_transport_handle_t t)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.use_global_ca_store = true;
}
@ -205,7 +225,7 @@ void esp_transport_ssl_enable_global_ca_store(esp_transport_handle_t t)
void esp_transport_ssl_set_psk_key_hint(esp_transport_handle_t t, const psk_hint_key_t* psk_hint_key)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.psk_hint_key = psk_hint_key;
}
@ -213,7 +233,7 @@ void esp_transport_ssl_set_psk_key_hint(esp_transport_handle_t t, const psk_hint
void esp_transport_ssl_set_cert_data(esp_transport_handle_t t, const char *data, int len)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.cacert_pem_buf = (void *)data;
ssl->cfg.cacert_pem_bytes = len + 1;
@ -222,7 +242,7 @@ void esp_transport_ssl_set_cert_data(esp_transport_handle_t t, const char *data,
void esp_transport_ssl_set_cert_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 = t->foundation_transport;
if (t && ssl) {
ssl->cfg.cacert_buf = (void *)data;
ssl->cfg.cacert_bytes = len;
@ -231,7 +251,7 @@ void esp_transport_ssl_set_cert_data_der(esp_transport_handle_t t, const char *d
void esp_transport_ssl_set_client_cert_data(esp_transport_handle_t t, const char *data, int len)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.clientcert_pem_buf = (void *)data;
ssl->cfg.clientcert_pem_bytes = len + 1;
@ -240,7 +260,7 @@ void esp_transport_ssl_set_client_cert_data(esp_transport_handle_t t, const char
void esp_transport_ssl_set_client_cert_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 = t->foundation_transport;
if (t && ssl) {
ssl->cfg.clientcert_buf = (void *)data;
ssl->cfg.clientcert_bytes = len;
@ -249,7 +269,7 @@ 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)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.clientkey_pem_buf = (void *)data;
ssl->cfg.clientkey_pem_bytes = len + 1;
@ -258,7 +278,7 @@ 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);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.clientkey_password = (void *)password;
ssl->cfg.clientkey_password_len = password_len;
@ -267,7 +287,7 @@ void esp_transport_ssl_set_client_key_password(esp_transport_handle_t t, const c
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 = t->foundation_transport;
if (t && ssl) {
ssl->cfg.clientkey_buf = (void *)data;
ssl->cfg.clientkey_bytes = len;
@ -276,7 +296,7 @@ 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);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.alpn_protos = alpn_protos;
}
@ -284,7 +304,7 @@ void esp_transport_ssl_set_alpn_protocol(esp_transport_handle_t t, const char **
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 = t->foundation_transport;
if (t && ssl) {
ssl->cfg.skip_common_name = true;
}
@ -292,7 +312,7 @@ void esp_transport_ssl_skip_common_name_check(esp_transport_handle_t t)
void esp_transport_ssl_use_secure_element(esp_transport_handle_t t)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) {
ssl->cfg.use_secure_element = true;
}
@ -311,8 +331,8 @@ static int ssl_get_socket(esp_transport_handle_t t)
void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data)
{
transport_ssl_t *ssl = esp_transport_get_context_data(t);
if (t && ssl) {
transport_ssl_t *ssl = t->foundation_transport;
if (t && ssl) { // TODO: check t NULL first!
ssl->cfg.ds_data = ds_data;
}
}
@ -328,14 +348,25 @@ void esp_transport_ssl_set_keep_alive(esp_transport_handle_t t, esp_transport_ke
esp_transport_handle_t esp_transport_ssl_init(void)
{
esp_transport_handle_t t = esp_transport_init();
transport_ssl_t *ssl = calloc(1, sizeof(transport_ssl_t));
ESP_TRANSPORT_MEM_CHECK(TAG, ssl, {
esp_transport_destroy(t);
return NULL;
});
esp_transport_set_context_data(t, ssl);
esp_transport_set_context_data(t, NULL);
esp_transport_set_func(t, ssl_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy);
esp_transport_set_async_connect_func(t, ssl_connect_async);
t->_get_socket = ssl_get_socket;
return t;
}
struct transport_esp_tls* esp_transport_init_foundation(void)
{
transport_ssl_t *ssl = calloc(1, sizeof(transport_ssl_t));
return ssl;
}
esp_transport_handle_t esp_transport_tcp_init(void)
{
esp_transport_handle_t t = esp_transport_init();
esp_transport_set_context_data(t, NULL);
esp_transport_set_func(t, tcp_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy);
esp_transport_set_async_connect_func(t, ssl_connect_async); // TODO: tcp_connect_async()
t->_get_socket = ssl_get_socket;
return t;
}

View File

@ -1,323 +0,0 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_err.h"
#include "esp_transport_utils.h"
#include "esp_transport.h"
#include "esp_transport_internal.h"
#include "esp_tls_errors.h"
static const char *TAG = "TRANS_TCP";
typedef struct {
int sock;
} transport_tcp_t;
static int resolve_dns(const char *host, struct sockaddr_in *ip)
{
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
int err = getaddrinfo(host, NULL, &hints, &res);
if(err != 0 || res == NULL) {
ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
return ESP_FAIL;
}
ip->sin_family = AF_INET;
memcpy(&ip->sin_addr, &((struct sockaddr_in *)(res->ai_addr))->sin_addr, sizeof(ip->sin_addr));
freeaddrinfo(res);
return ESP_OK;
}
static int tcp_enable_keep_alive(int fd, esp_transport_keep_alive_t *keep_alive_cfg)
{
int keep_alive_enable = 1;
int keep_alive_idle = keep_alive_cfg->keep_alive_idle;
int keep_alive_interval = keep_alive_cfg->keep_alive_interval;
int keep_alive_count = keep_alive_cfg->keep_alive_count;
ESP_LOGD(TAG, "Enable TCP keep alive. idle: %d, interval: %d, count: %d", keep_alive_idle, keep_alive_interval, keep_alive_count);
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive_enable, sizeof(keep_alive_enable)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt SO_KEEPALIVE");
return -1;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_alive_idle, sizeof(keep_alive_idle)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPIDLE");
return -1;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keep_alive_interval, sizeof(keep_alive_interval)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPINTVL");
return -1;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_alive_count, sizeof(keep_alive_count)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPCNT");
return -1;
}
return 0;
}
static int tcp_connect(esp_transport_handle_t t, const char *host, int port, int timeout_ms)
{
struct sockaddr_in remote_ip;
struct timeval tv = { 0 };
transport_tcp_t *tcp = esp_transport_get_context_data(t);
bzero(&remote_ip, sizeof(struct sockaddr_in));
//if stream_host is not ip address, resolve it AF_INET,servername,&serveraddr.sin_addr
if (inet_pton(AF_INET, host, &remote_ip.sin_addr) != 1) {
if (resolve_dns(host, &remote_ip) < 0) {
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_CANNOT_RESOLVE_HOSTNAME);
return -1;
}
}
tcp->sock = socket(PF_INET, SOCK_STREAM, 0);
if (tcp->sock < 0) {
ESP_LOGE(TAG, "Error create socket");
return -1;
}
remote_ip.sin_family = AF_INET;
remote_ip.sin_port = htons(port);
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_SNDTIMEO, &tv, sizeof(tv));
// Set socket keep-alive option
if (t->keep_alive_cfg && t->keep_alive_cfg->keep_alive_enable) {
if (tcp_enable_keep_alive(tcp->sock, t->keep_alive_cfg) < 0) {
ESP_LOGE(TAG, "Error to set tcp [socket=%d] keep-alive", tcp->sock);
goto error;
}
}
// Set socket to non-blocking
int flags;
if ((flags = fcntl(tcp->sock, F_GETFL, NULL)) < 0) {
ESP_LOGE(TAG, "[sock=%d] get file flags error: %s", tcp->sock, strerror(errno));
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_SETOPT_FAILED);
goto error;
}
if (fcntl(tcp->sock, F_SETFL, flags |= O_NONBLOCK) < 0) {
ESP_LOGE(TAG, "[sock=%d] set nonblocking error: %s", tcp->sock, strerror(errno));
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_SETOPT_FAILED);
goto error;
}
ESP_LOGD(TAG, "[sock=%d] Connecting to server. IP: %s, Port: %d",
tcp->sock, ipaddr_ntoa((const ip_addr_t*)&remote_ip.sin_addr.s_addr), port);
if (connect(tcp->sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) < 0) {
if (errno == EINPROGRESS) {
fd_set fdset;
esp_transport_utils_ms_to_timeval(timeout_ms, &tv);
FD_ZERO(&fdset);
FD_SET(tcp->sock, &fdset);
int res = select(tcp->sock+1, NULL, &fdset, NULL, &tv);
if (res < 0) {
ESP_LOGE(TAG, "[sock=%d] select() error: %s", tcp->sock, strerror(errno));
esp_transport_capture_errno(t, errno);
goto error;
}
else if (res == 0) {
ESP_LOGE(TAG, "[sock=%d] select() timeout", tcp->sock);
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_CONNECTION_TIMEOUT);
goto error;
} else {
int sockerr;
socklen_t len = (socklen_t)sizeof(int);
if (getsockopt(tcp->sock, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &len) < 0) {
ESP_LOGE(TAG, "[sock=%d] getsockopt() error: %s", tcp->sock, strerror(errno));
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_SETOPT_FAILED);
goto error;
}
else if (sockerr) {
esp_transport_capture_errno(t, sockerr);
ESP_LOGE(TAG, "[sock=%d] delayed connect error: %s", tcp->sock, strerror(sockerr));
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_CONNECTION_FAILED);
goto error;
}
}
} else {
ESP_LOGE(TAG, "[sock=%d] connect() error: %s", tcp->sock, strerror(errno));
goto error;
}
}
// Reset socket to blocking
if ((flags = fcntl(tcp->sock, F_GETFL, NULL)) < 0) {
ESP_LOGE(TAG, "[sock=%d] get file flags error: %s", tcp->sock, strerror(errno));
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_SETOPT_FAILED);
goto error;
}
if (fcntl(tcp->sock, F_SETFL, flags & ~O_NONBLOCK) < 0) {
ESP_LOGE(TAG, "[sock=%d] reset blocking error: %s", tcp->sock, strerror(errno));
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_SETOPT_FAILED);
goto error;
}
return tcp->sock;
error:
close(tcp->sock);
tcp->sock = -1;
return -1;
}
static int tcp_write(esp_transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
int poll;
transport_tcp_t *tcp = esp_transport_get_context_data(t);
if ((poll = esp_transport_poll_write(t, timeout_ms)) <= 0) {
return poll;
}
return write(tcp->sock, buffer, len);
}
static int tcp_read(esp_transport_handle_t t, char *buffer, int len, int timeout_ms)
{
transport_tcp_t *tcp = esp_transport_get_context_data(t);
int poll = -1;
if ((poll = esp_transport_poll_read(t, timeout_ms)) <= 0) {
return poll;
}
int read_len = read(tcp->sock, buffer, len);
if (read_len == 0) {
if (poll > 0) {
// no error, socket reads 0 while previously detected as readable -> connection has been closed cleanly
capture_tcp_transport_error(t, ERR_TCP_TRANSPORT_CONNECTION_CLOSED_BY_FIN);
}
return -1;
}
return read_len;
}
static int tcp_poll_read(esp_transport_handle_t t, int timeout_ms)
{
transport_tcp_t *tcp = esp_transport_get_context_data(t);
int ret = -1;
struct timeval timeout;
fd_set readset;
fd_set errset;
FD_ZERO(&readset);
FD_ZERO(&errset);
FD_SET(tcp->sock, &readset);
FD_SET(tcp->sock, &errset);
ret = select(tcp->sock + 1, &readset, NULL, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
if (ret > 0 && FD_ISSET(tcp->sock, &errset)) {
int sock_errno = 0;
uint32_t optlen = sizeof(sock_errno);
getsockopt(tcp->sock, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
esp_transport_capture_errno(t, sock_errno);
ESP_LOGE(TAG, "tcp_poll_read select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), tcp->sock);
ret = -1;
}
return ret;
}
static int tcp_poll_write(esp_transport_handle_t t, int timeout_ms)
{
transport_tcp_t *tcp = esp_transport_get_context_data(t);
int ret = -1;
struct timeval timeout;
fd_set writeset;
fd_set errset;
FD_ZERO(&writeset);
FD_ZERO(&errset);
FD_SET(tcp->sock, &writeset);
FD_SET(tcp->sock, &errset);
ret = select(tcp->sock + 1, NULL, &writeset, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
if (ret > 0 && FD_ISSET(tcp->sock, &errset)) {
int sock_errno = 0;
uint32_t optlen = sizeof(sock_errno);
getsockopt(tcp->sock, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
esp_transport_capture_errno(t, sock_errno);
ESP_LOGE(TAG, "tcp_poll_write select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), tcp->sock);
ret = -1;
}
return ret;
}
static int tcp_close(esp_transport_handle_t t)
{
transport_tcp_t *tcp = esp_transport_get_context_data(t);
int ret = -1;
if (tcp->sock >= 0) {
ret = close(tcp->sock);
tcp->sock = -1;
}
return ret;
}
static esp_err_t tcp_destroy(esp_transport_handle_t t)
{
transport_tcp_t *tcp = esp_transport_get_context_data(t);
esp_transport_close(t);
free(tcp);
return 0;
}
static int tcp_get_socket(esp_transport_handle_t t)
{
if (t) {
transport_tcp_t *tcp = t->data;
if (tcp) {
return tcp->sock;
}
}
return -1;
}
void esp_transport_tcp_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg)
{
if (t && keep_alive_cfg) {
t->keep_alive_cfg = keep_alive_cfg;
}
}
esp_transport_handle_t esp_transport_tcp_init(void)
{
esp_transport_handle_t t = esp_transport_init();
transport_tcp_t *tcp = calloc(1, sizeof(transport_tcp_t));
ESP_TRANSPORT_MEM_CHECK(TAG, tcp, {
esp_transport_destroy(t);
return NULL;
});
tcp->sock = -1;
esp_transport_set_func(t, tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy);
esp_transport_set_context_data(t, tcp);
t->_get_socket = tcp_get_socket;
return t;
}