From 1897896e9739f93654430b172b7048134177a0a9 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Fri, 5 Apr 2019 15:00:55 +0300 Subject: [PATCH] net: sockets: Make sure that getaddrinfo() cannot hang forever If for some reason the DNS resolver callback is not called properly then make sure that semaphore will not block forever. Fixes #15197 Signed-off-by: Jukka Rissanen --- subsys/net/lib/sockets/Kconfig | 10 +++++ subsys/net/lib/sockets/getaddrinfo.c | 65 ++++++++++++++-------------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/subsys/net/lib/sockets/Kconfig b/subsys/net/lib/sockets/Kconfig index 22b662a20c2..2c3cc16b3c6 100644 --- a/subsys/net/lib/sockets/Kconfig +++ b/subsys/net/lib/sockets/Kconfig @@ -29,6 +29,16 @@ config NET_SOCKETS_POLL_MAX help Maximum number of entries supported for poll() call. +config NET_SOCKETS_DNS_TIMEOUT + int "Timeout value in milliseconds for DNS queries" + default 2000 + range 1000 300000 + depends on DNS_RESOLVER + help + This variable specifies time in milliseconds after which DNS + query is considered timeout. Minimum timeout is 1 second and + maximum timeout is 5 min. + config NET_SOCKETS_SOCKOPT_TLS bool "Enable TCP TLS socket option support [EXPERIMENTAL]" select TLS_CREDENTIALS diff --git a/subsys/net/lib/sockets/getaddrinfo.c b/subsys/net/lib/sockets/getaddrinfo.c index 7853ae863e6..64a1c0bb76e 100644 --- a/subsys/net/lib/sockets/getaddrinfo.c +++ b/subsys/net/lib/sockets/getaddrinfo.c @@ -79,6 +79,19 @@ static void dns_resolve_cb(enum dns_resolve_status status, state->idx++; } +static int exec_query(const char *host, int family, + struct getaddrinfo_state *ai_state) +{ + enum dns_query_type qtype = DNS_QUERY_TYPE_A; + + if (IS_ENABLED(CONFIG_NET_IPV6) && family != AF_INET) { + qtype = DNS_QUERY_TYPE_AAAA; + } + + return dns_get_addr_info(host, qtype, NULL, + dns_resolve_cb, ai_state, + CONFIG_NET_SOCKETS_DNS_TIMEOUT); +} int z_impl_z_zsock_getaddrinfo_internal(const char *host, const char *service, const struct zsock_addrinfo *hints, @@ -111,43 +124,31 @@ int z_impl_z_zsock_getaddrinfo_internal(const char *host, const char *service, /* Link entries in advance */ ai_state.ai_arr[0].ai_next = &ai_state.ai_arr[1]; - /* Execute if AF_UNSPEC or AF_INET4 */ - if (family != AF_INET6) { - ret = dns_get_addr_info(host, DNS_QUERY_TYPE_A, NULL, - dns_resolve_cb, &ai_state, 1000); - if (ret == 0) { - k_sem_take(&ai_state.sem, K_FOREVER); - st1 = ai_state.status; - } else { - errno = -ret; - st1 = DNS_EAI_SYSTEM; + ret = exec_query(host, family, &ai_state); + if (ret == 0) { + /* If the DNS query for reason fails so that the + * dns_resolve_cb() would not be called, then we want the + * semaphore to timeout so that we will not hang forever. + * So make the sem timeout longer than the DNS timeout so that + * we do not need to start to cancel any pending DNS queries. + */ + int ret = k_sem_take(&ai_state.sem, + CONFIG_NET_SOCKETS_DNS_TIMEOUT + + K_MSEC(100)); + if (ret == -EAGAIN) { + return DNS_EAI_AGAIN; } - if (ai_state.idx > 0) { - ai_addr = &ai_state.ai_arr[ai_state.idx - 1]._ai_addr; - net_sin(ai_addr)->sin_port = htons(port); - } + st1 = ai_state.status; + } else { + errno = -ret; + st1 = DNS_EAI_SYSTEM; } -#if defined(CONFIG_NET_IPV6) - /* Execute if AF_UNSPEC or AF_INET6 */ - if (family != AF_INET) { - ret = dns_get_addr_info(host, DNS_QUERY_TYPE_AAAA, NULL, - dns_resolve_cb, &ai_state, 1000); - if (ret == 0) { - k_sem_take(&ai_state.sem, K_FOREVER); - st2 = ai_state.status; - } else { - errno = -ret; - st2 = DNS_EAI_SYSTEM; - } - - if (ai_state.idx > 0) { - ai_addr = &ai_state.ai_arr[ai_state.idx - 1]._ai_addr; - net_sin6(ai_addr)->sin6_port = htons(port); - } + if (ai_state.idx > 0) { + ai_addr = &ai_state.ai_arr[ai_state.idx - 1]._ai_addr; + net_sin(ai_addr)->sin_port = htons(port); } -#endif /* If both attempts failed, it's error */ if (st1 && st2) {