mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 17:19:09 -04:00
tcp_transport/esp_tls: Use common TCP transport to reduce code duplication
For high level review of the changes.
This commit is contained in:
parent
391d7bf271
commit
2c28ffffee
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user