esp-tls: Rework tcp_connect() to use more subroutines

Refactored the esp_tcp_connect() functionality to break it down to
* dns-resolution + socket creation
* set configured socket options
* set/reset non-block mode
* the actual connection in non-blocking mode
This commit is contained in:
David Cermak 2021-02-04 16:50:56 +01:00
parent 1fa0db8d44
commit 2d25252746

View File

@ -133,8 +133,9 @@ esp_tls_t *esp_tls_init(void)
return tls;
}
static esp_err_t resolve_host_name(const char *host, size_t hostlen, struct addrinfo **address_info)
static esp_err_t esp_tls_hostname_to_fd(const char *host, size_t hostlen, int port, struct sockaddr_storage *address, int* fd)
{
struct addrinfo *address_info;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
@ -146,14 +147,40 @@ static esp_err_t resolve_host_name(const char *host, size_t hostlen, struct addr
}
ESP_LOGD(TAG, "host:%s: strlen %lu", use_host, (unsigned long)hostlen);
int res = getaddrinfo(use_host, NULL, &hints, address_info);
if (res != 0 || *address_info == NULL) {
int res = getaddrinfo(use_host, NULL, &hints, &address_info);
if (res != 0 || address_info == NULL) {
ESP_LOGE(TAG, "couldn't get hostname for :%s: "
"getaddrinfo() returns %d, addrinfo=%p", use_host, res, *address_info);
"getaddrinfo() returns %d, addrinfo=%p", use_host, res, address_info);
free(use_host);
return ESP_ERR_ESP_TLS_CANNOT_RESOLVE_HOSTNAME;
}
free(use_host);
*fd = socket(address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol);
if (*fd < 0) {
ESP_LOGE(TAG, "Failed to create socket (family %d socktype %d protocol %d)", address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol);
freeaddrinfo(address_info);
return ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET;
}
if (address_info->ai_family == AF_INET) {
struct sockaddr_in *p = (struct sockaddr_in *)address_info->ai_addr;
p->sin_port = htons(port);
ESP_LOGD(TAG, "[sock=%d] Resolved IPv4 address: %s", *fd, ipaddr_ntoa((const ip_addr_t*)&p->sin_addr.s_addr));
memcpy(address, p, sizeof(struct sockaddr ));
} else if (address_info->ai_family == AF_INET6) {
struct sockaddr_in6 *p = (struct sockaddr_in6 *)address_info->ai_addr;
p->sin6_port = htons(port);
p->sin6_family = AF_INET6;
ESP_LOGD(TAG, "[sock=%d] Resolved IPv6 address: %s", *fd, ip6addr_ntoa((const ip6_addr_t*)&p->sin6_addr));
memcpy(address, p, sizeof(struct sockaddr_in6 ));
} else {
ESP_LOGE(TAG, "Unsupported protocol family %d", address_info->ai_family);
close(*fd);
freeaddrinfo(address_info);
return ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY;
}
freeaddrinfo(address_info);
return ESP_OK;
}
@ -163,95 +190,93 @@ static void ms_to_timeval(int timeout_ms, struct timeval *tv)
tv->tv_usec = (timeout_ms % 1000) * 1000;
}
static int esp_tls_tcp_enable_keep_alive(int fd, tls_keep_alive_cfg_t *cfg)
static esp_err_t esp_tls_set_socket_options(int fd, const esp_tls_cfg_t *cfg)
{
int keep_alive_enable = 1;
int keep_alive_idle = cfg->keep_alive_idle;
int keep_alive_interval = cfg->keep_alive_interval;
int keep_alive_count = cfg->keep_alive_count;
if (cfg && cfg->timeout_ms >= 0) {
struct timeval tv;
ms_to_timeval(cfg->timeout_ms, &tv);
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt SO_RCVTIMEO");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt SO_SNDTIMEO");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (cfg->keep_alive_cfg && cfg->keep_alive_cfg->keep_alive_enable) {
int keep_alive_enable = 1;
int keep_alive_idle = cfg->keep_alive_cfg->keep_alive_idle;
int keep_alive_interval = cfg->keep_alive_cfg->keep_alive_interval;
int keep_alive_count = cfg->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;
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 ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_alive_enable, sizeof(keep_alive_enable)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPIDLE");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keep_alive_interval, sizeof(keep_alive_interval)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPINTVL");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_alive_count, sizeof(keep_alive_count)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPCNT");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
}
}
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 ESP_OK;
}
static esp_err_t esp_tls_set_socket_non_blocking(int fd, bool non_blocking)
{
int flags;
if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
ESP_LOGE(TAG, "[sock=%d] get file flags error: %s", fd, strerror(errno));
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
return 0;
if (non_blocking) {
flags |= O_NONBLOCK;
} else {
flags &= ~O_NONBLOCK;
}
if (fcntl(fd, F_SETFL, flags) < 0) {
ESP_LOGE(TAG, "[sock=%d] set blocking/nonblocking error: %s", fd, strerror(errno));
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
return ESP_OK;
}
static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *sockfd, const esp_tls_t *tls, const esp_tls_cfg_t *cfg)
{
esp_err_t ret;
struct addrinfo *addrinfo;
if ((ret = resolve_host_name(host, hostlen, &addrinfo)) != ESP_OK) {
struct sockaddr_storage address;
int fd;
esp_err_t ret = esp_tls_hostname_to_fd(host, hostlen, port, &address, &fd);
if (ret != ESP_OK) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_SYSTEM, errno);
return ret;
}
int fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
if (fd < 0) {
ESP_LOGE(TAG, "Failed to create socket (family %d socktype %d protocol %d)", addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_SYSTEM, errno);
ret = ESP_ERR_ESP_TLS_CANNOT_CREATE_SOCKET;
goto err_freeaddr;
// Set timeout options and keep-alive options if configured
ret = esp_tls_set_socket_options(fd, cfg);
if (ret != ESP_OK) {
goto err;
}
void *addr_ptr;
if (addrinfo->ai_family == AF_INET) {
struct sockaddr_in *p = (struct sockaddr_in *)addrinfo->ai_addr;
p->sin_port = htons(port);
ESP_LOGD(TAG, "[sock=%d] Resolved IPv4 address: %s", fd, ipaddr_ntoa((const ip_addr_t*)&p->sin_addr.s_addr));
addr_ptr = p;
} else if (addrinfo->ai_family == AF_INET6) {
struct sockaddr_in6 *p = (struct sockaddr_in6 *)addrinfo->ai_addr;
ESP_LOGD(TAG, "[sock=%d] Resolved IPv6 address: %s", fd, ip6addr_ntoa((const ip6_addr_t*)&p->sin6_addr));
p->sin6_port = htons(port);
p->sin6_family = AF_INET6;
addr_ptr = p;
} else {
ESP_LOGE(TAG, "Unsupported protocol family %d", addrinfo->ai_family);
ret = ESP_ERR_ESP_TLS_UNSUPPORTED_PROTOCOL_FAMILY;
goto err_freesocket;
}
if (cfg && cfg->timeout_ms >= 0) {
struct timeval tv;
ms_to_timeval(cfg->timeout_ms, &tv);
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
if (cfg->keep_alive_cfg && cfg->keep_alive_cfg->keep_alive_enable) {
if (esp_tls_tcp_enable_keep_alive(fd, cfg->keep_alive_cfg) < 0) {
ESP_LOGE(TAG, "Error setting keep-alive");
goto err_freesocket;
}
}
}
// Set socket to non-blocking
int flags;
if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
ESP_LOGE(TAG, "[sock=%d] get file flags error: %s", fd, strerror(errno));
goto err_freesocket;
}
if (fcntl(fd, F_SETFL, flags |= O_NONBLOCK) < 0) {
ESP_LOGE(TAG, "[sock=%d] set nonblocking error: %s", fd, strerror(errno));
goto err_freesocket;
// Set to non block before connecting to better control connection timeout
ret = esp_tls_set_socket_non_blocking(fd, true);
if (ret != ESP_OK) {
goto err;
}
ret = ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST;
ESP_LOGD(TAG, "[sock=%d] Connecting to server. HOST: %s, Port: %d", fd, host, port);
if (connect(fd, (struct sockaddr *)(addr_ptr), sizeof(struct sockaddr)) < 0) {
if (connect(fd, (struct sockaddr *)&address, sizeof(struct sockaddr)) < 0) {
if (errno == EINPROGRESS) {
fd_set fdset;
struct timeval tv = { .tv_usec = 0, .tv_sec = 10 }; // Default connection timeout is 10 s
@ -259,7 +284,6 @@ static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *s
if (cfg && cfg->non_block) {
// Non-blocking mode -> just return successfully at this stage
*sockfd = fd;
freeaddrinfo(addrinfo);
return ESP_OK;
}
@ -273,12 +297,12 @@ static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *s
if (res < 0) {
ESP_LOGE(TAG, "[sock=%d] select() error: %s", fd, strerror(errno));
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_SYSTEM, errno);
goto err_freesocket;
goto err;
}
else if (res == 0) {
ESP_LOGE(TAG, "[sock=%d] select() timeout", fd);
ret = ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST;
goto err_freesocket;
ret = ESP_ERR_ESP_TLS_CONNECTION_TIMEOUT;
goto err;
} else {
int sockerr;
socklen_t len = (socklen_t)sizeof(int);
@ -286,43 +310,33 @@ static esp_err_t esp_tcp_connect(const char *host, int hostlen, int port, int *s
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &len) < 0) {
ESP_LOGE(TAG, "[sock=%d] getsockopt() error: %s", fd, strerror(errno));
ret = ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
goto err_freesocket;
goto err;
}
else if (sockerr) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_SYSTEM, sockerr);
ESP_LOGE(TAG, "[sock=%d] delayed connect error: %s", fd, strerror(sockerr));
ret = ESP_ERR_ESP_TLS_FAILED_CONNECT_TO_HOST;
goto err_freesocket;
goto err;
}
}
} else {
ESP_LOGE(TAG, "[sock=%d] connect() error: %s", fd, strerror(errno));
goto err_freesocket;
goto err;
}
}
if (cfg && cfg->non_block == false) {
// Reset socket to blocking (unless non-blocking option set)
if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
ESP_LOGE(TAG, "[sock=%d] get file flags error: %s", fd, strerror(errno));
ret = ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
goto err_freesocket;
}
if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
ESP_LOGE(TAG, "[sock=%d] reset blocking error: %s", fd, strerror(errno));
ret = ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
goto err_freesocket;
// reset back to blocking mode (unless non_block configured)
ret = esp_tls_set_socket_non_blocking(fd, false);
if (ret != ESP_OK) {
goto err;
}
}
*sockfd = fd;
freeaddrinfo(addrinfo);
return ESP_OK;
err_freesocket:
err:
close(fd);
err_freeaddr:
freeaddrinfo(addrinfo);
return ret;
}