diff --git a/deps/cares/CMakeLists.txt b/deps/cares/CMakeLists.txt index cde5f3c07d8800..2505967d352fc5 100644 --- a/deps/cares/CMakeLists.txt +++ b/deps/cares/CMakeLists.txt @@ -12,7 +12,7 @@ INCLUDE (CheckCSourceCompiles) INCLUDE (CheckStructHasMember) INCLUDE (CheckLibraryExists) -PROJECT (c-ares LANGUAGES C VERSION "1.32.2" ) +PROJECT (c-ares LANGUAGES C VERSION "1.32.3" ) # Set this version before release SET (CARES_VERSION "${PROJECT_VERSION}") @@ -30,7 +30,7 @@ INCLUDE (GNUInstallDirs) # include this *AFTER* PROJECT(), otherwise paths are w # For example, a version of 4:0:2 would generate output such as: # libname.so -> libname.so.2 # libname.so.2 -> libname.so.2.2.0 -SET (CARES_LIB_VERSIONINFO "19:2:17") +SET (CARES_LIB_VERSIONINFO "19:3:17") OPTION (CARES_STATIC "Build as a static library" OFF) diff --git a/deps/cares/RELEASE-NOTES.md b/deps/cares/RELEASE-NOTES.md index 808c9244dcbf6d..69e6a475799e7f 100644 --- a/deps/cares/RELEASE-NOTES.md +++ b/deps/cares/RELEASE-NOTES.md @@ -1,3 +1,29 @@ +## c-ares version 1.32.3 - July 24 2024 + +This is a bugfix release. + +Changes: +* Prevent complex recursion during query requeuing and connection cleanup for + stability. [e8b32b8](https://github.com/c-ares/c-ares/commit/e8b32b8) +* Better propagate error codes on requeue situations. + [a9bc0a2](https://github.com/c-ares/c-ares/commit/a9bc0a2) +* Try to prevent SIGPIPE from being generated and delivered to integrations. + [de01baa](https://github.com/c-ares/c-ares/commit/de01baa) + +Bugfixes: +* Missing manpage for `ares_dns_record_set_id()` + [aa462b3](https://github.com/c-ares/c-ares/commit/aa462b3) +* Memory leak in `ares__hosts_entry_to_hostent()` due to allocation strategy. + [PR #824](https://github.com/c-ares/c-ares/pull/824) +* UDP write failure detected via ICMP unreachable should trigger faster + failover. [PR #821](https://github.com/c-ares/c-ares/pull/821) +* Fix pycares test case regression due to wrong error code being returned. + Regression from 1.31.0. [PR #820](https://github.com/c-ares/c-ares/pull/820) +* Fix possible Windows crash during `ares_destroy()` when using event threads. + [5609bd4](https://github.com/c-ares/c-ares/commit/5609bd4) +* `ARES_OPT_MAXTIMEOUTMS` wasn't being honored in all cases. + [a649c60](https://github.com/c-ares/c-ares/commit/a649c60) + ## c-ares version 1.32.2 - July 15 2024 This is a bugfix release. diff --git a/deps/cares/aminclude_static.am b/deps/cares/aminclude_static.am index 1cd572e037aafe..d542444c4e23c8 100644 --- a/deps/cares/aminclude_static.am +++ b/deps/cares/aminclude_static.am @@ -1,6 +1,6 @@ # aminclude_static.am generated automatically by Autoconf -# from AX_AM_MACROS_STATIC on Mon Jul 15 09:00:09 EDT 2024 +# from AX_AM_MACROS_STATIC on Wed Jul 24 05:40:58 EDT 2024 # Code coverage diff --git a/deps/cares/configure b/deps/cares/configure index e50f992c7a76ee..f5fc16a49bf934 100755 --- a/deps/cares/configure +++ b/deps/cares/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for c-ares 1.32.2. +# Generated by GNU Autoconf 2.72 for c-ares 1.32.3. # # Report bugs to . # @@ -614,8 +614,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='c-ares' PACKAGE_TARNAME='c-ares' -PACKAGE_VERSION='1.32.2' -PACKAGE_STRING='c-ares 1.32.2' +PACKAGE_VERSION='1.32.3' +PACKAGE_STRING='c-ares 1.32.3' PACKAGE_BUGREPORT='c-ares mailing list: http://lists.haxx.se/listinfo/c-ares' PACKAGE_URL='' @@ -1415,7 +1415,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures c-ares 1.32.2 to adapt to many kinds of systems. +'configure' configures c-ares 1.32.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1486,7 +1486,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of c-ares 1.32.2:";; + short | recursive ) echo "Configuration of c-ares 1.32.3:";; esac cat <<\_ACEOF @@ -1623,7 +1623,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -c-ares configure 1.32.2 +c-ares configure 1.32.3 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -2267,7 +2267,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by c-ares $as_me 1.32.2, which was +It was created by c-ares $as_me 1.32.3, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -3259,7 +3259,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu -CARES_VERSION_INFO="19:2:17" +CARES_VERSION_INFO="19:3:17" @@ -5999,7 +5999,7 @@ fi # Define the identity of the package. PACKAGE='c-ares' - VERSION='1.32.2' + VERSION='1.32.3' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -26339,7 +26339,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by c-ares $as_me 1.32.2, which was +This file was extended by c-ares $as_me 1.32.3, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -26407,7 +26407,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -c-ares config.status 1.32.2 +c-ares config.status 1.32.3 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" diff --git a/deps/cares/configure.ac b/deps/cares/configure.ac index f110698a90e776..7abf80f68bd716 100644 --- a/deps/cares/configure.ac +++ b/deps/cares/configure.ac @@ -2,10 +2,10 @@ dnl Copyright (C) The c-ares project and its contributors dnl SPDX-License-Identifier: MIT AC_PREREQ([2.69]) -AC_INIT([c-ares], [1.32.2], +AC_INIT([c-ares], [1.32.3], [c-ares mailing list: http://lists.haxx.se/listinfo/c-ares]) -CARES_VERSION_INFO="19:2:17" +CARES_VERSION_INFO="19:3:17" dnl This flag accepts an argument of the form current[:revision[:age]]. So, dnl passing -version-info 3:12:1 sets current to 3, revision to 12, and age to dnl 1. diff --git a/deps/cares/docs/Makefile.in b/deps/cares/docs/Makefile.in index 6abb0e7f6bd62f..0b44dbd8c53ba0 100644 --- a/deps/cares/docs/Makefile.in +++ b/deps/cares/docs/Makefile.in @@ -374,6 +374,7 @@ MANPAGES = ares_cancel.3 \ ares_dns_record_rr_del.3 \ ares_dns_record_rr_get.3 \ ares_dns_record_rr_get_const.3 \ + ares_dns_record_set_id.3 \ ares_dns_rec_type_fromstr.3 \ ares_dns_rec_type_tostr.3 \ ares_dns_rec_type_t.3 \ diff --git a/deps/cares/docs/Makefile.inc b/deps/cares/docs/Makefile.inc index 83d85ebf1d426e..46e30346cfb86a 100644 --- a/deps/cares/docs/Makefile.inc +++ b/deps/cares/docs/Makefile.inc @@ -36,6 +36,7 @@ MANPAGES = ares_cancel.3 \ ares_dns_record_rr_del.3 \ ares_dns_record_rr_get.3 \ ares_dns_record_rr_get_const.3 \ + ares_dns_record_set_id.3 \ ares_dns_rec_type_fromstr.3 \ ares_dns_rec_type_tostr.3 \ ares_dns_rec_type_t.3 \ diff --git a/deps/cares/docs/ares_dns_record.3 b/deps/cares/docs/ares_dns_record.3 index 11c59a64864ba9..47ca95b057a246 100644 --- a/deps/cares/docs/ares_dns_record.3 +++ b/deps/cares/docs/ares_dns_record.3 @@ -32,6 +32,9 @@ ares_dns_record_t *ares_dns_record_duplicate(const ares_dns_record_t *dnsrec); unsigned short ares_dns_record_get_id(const ares_dns_record_t *dnsrec); +ares_bool_t ares_dns_record_set_id(ares_dns_record_t *dnsrec, + unsigned short id); + unsigned short ares_dns_record_get_flags(const ares_dns_record_t *dnsrec); ares_dns_opcode_t ares_dns_record_get_opcode(const ares_dns_record_t *dnsrec); @@ -343,6 +346,14 @@ message id from the DNS record provided in the .IR dnsrec parameter. +The \fIares_dns_record_set_id(3)\fP function is used to set the DNS +message id in the +.IR id +parameter from the DNS record provided in the +.IR dnsrec +parameter. This id will be overwritten when passing the record to c-ares, +so mostly exists for external purposes. + The \fIares_dns_record_get_flags(3)\fP function is used to retrieve the DNS message flags from the DNS record provided in the .IR dnsrec @@ -428,12 +439,12 @@ is returned on out of memory, .B ARES_EFORMERR is returned on misuse. -\fIares_dns_record_get_id(3)\fP, \fIares_dns_record_get_flags(3)\fP, -\fIares_dns_record_get_opcode(3)\fP, \fIares_dns_record_get_rcode(3)\fP, and -\fIares_dns_record_query_cnt(3)\fP all returned their prescribed datatype -values and in general can't fail except for misuse cases, in which a 0 may -be returned, however 0 can also be a valid return value for most of these -functions. +\fIares_dns_record_get_id(3)\fP, \fIares_dns_record_set_id(3)\fP, +\fIares_dns_record_get_flags(3)\fP, \fIares_dns_record_get_opcode(3)\fP, +\fIares_dns_record_get_rcode(3)\fP, and \fIares_dns_record_query_cnt(3)\fP +all returned their prescribed datatype values and in general can't fail except +for misuse cases, in which a 0 may be returned, however 0 can also be a valid +return value for most of these functions. .SH AVAILABILITY diff --git a/deps/cares/docs/ares_dns_record_set_id.3 b/deps/cares/docs/ares_dns_record_set_id.3 new file mode 100644 index 00000000000000..4acc581d29789c --- /dev/null +++ b/deps/cares/docs/ares_dns_record_set_id.3 @@ -0,0 +1,3 @@ +.\" Copyright (C) 2023 The c-ares project and its contributors. +.\" SPDX-License-Identifier: MIT +.so man3/ares_dns_record.3 diff --git a/deps/cares/include/ares_version.h b/deps/cares/include/ares_version.h index 80a5551b32c884..62541c31ba9f23 100644 --- a/deps/cares/include/ares_version.h +++ b/deps/cares/include/ares_version.h @@ -32,11 +32,11 @@ #define ARES_VERSION_MAJOR 1 #define ARES_VERSION_MINOR 32 -#define ARES_VERSION_PATCH 2 +#define ARES_VERSION_PATCH 3 #define ARES_VERSION \ ((ARES_VERSION_MAJOR << 16) | (ARES_VERSION_MINOR << 8) | \ (ARES_VERSION_PATCH)) -#define ARES_VERSION_STR "1.32.2" +#define ARES_VERSION_STR "1.32.3" #define CARES_HAVE_ARES_LIBRARY_INIT 1 #define CARES_HAVE_ARES_LIBRARY_CLEANUP 1 diff --git a/deps/cares/src/lib/Makefile.in b/deps/cares/src/lib/Makefile.in index befbdc7a3a9876..d1758eb8adb791 100644 --- a/deps/cares/src/lib/Makefile.in +++ b/deps/cares/src/lib/Makefile.in @@ -15,7 +15,7 @@ @SET_MAKE@ # aminclude_static.am generated automatically by Autoconf -# from AX_AM_MACROS_STATIC on Mon Jul 15 09:00:09 EDT 2024 +# from AX_AM_MACROS_STATIC on Wed Jul 24 05:40:58 EDT 2024 # Copyright (C) The c-ares project and its contributors # SPDX-License-Identifier: MIT diff --git a/deps/cares/src/lib/ares__close_sockets.c b/deps/cares/src/lib/ares__close_sockets.c index 8d7334b4c4dc11..33a030c9d23d48 100644 --- a/deps/cares/src/lib/ares__close_sockets.c +++ b/deps/cares/src/lib/ares__close_sockets.c @@ -28,7 +28,8 @@ #include "ares_private.h" #include -static void ares__requeue_queries(struct server_connection *conn) +static void ares__requeue_queries(struct server_connection *conn, + ares_status_t requeue_status) { struct query *query; ares_timeval_t now; @@ -36,11 +37,12 @@ static void ares__requeue_queries(struct server_connection *conn) ares__tvnow(&now); while ((query = ares__llist_first_val(conn->queries_to_conn)) != NULL) { - ares__requeue_query(query, &now); + ares__requeue_query(query, &now, requeue_status); } } -void ares__close_connection(struct server_connection *conn) +void ares__close_connection(struct server_connection *conn, + ares_status_t requeue_status) { struct server_state *server = conn->server; ares_channel_t *channel = server->channel; @@ -58,7 +60,7 @@ void ares__close_connection(struct server_connection *conn) } /* Requeue queries to other connections */ - ares__requeue_queries(conn); + ares__requeue_queries(conn, requeue_status); ares__llist_destroy(conn->queries_to_conn); @@ -74,45 +76,63 @@ void ares__close_sockets(struct server_state *server) while ((node = ares__llist_node_first(server->connections)) != NULL) { struct server_connection *conn = ares__llist_node_val(node); - ares__close_connection(conn); + ares__close_connection(conn, ARES_SUCCESS); } } -void ares__check_cleanup_conn(const ares_channel_t *channel, - struct server_connection *conn) +void ares__check_cleanup_conns(const ares_channel_t *channel) { - ares_bool_t do_cleanup = ARES_FALSE; + ares__slist_node_t *snode; - if (channel == NULL || conn == NULL) { + if (channel == NULL) { return; /* LCOV_EXCL_LINE: DefensiveCoding */ } - if (ares__llist_len(conn->queries_to_conn)) { - return; + /* Iterate across each server */ + for (snode = ares__slist_node_first(channel->servers); snode != NULL; + snode = ares__slist_node_next(snode)) { + + struct server_state *server = ares__slist_node_val(snode); + ares__llist_node_t *cnode; + + /* Iterate across each connection */ + cnode = ares__llist_node_first(server->connections); + while (cnode != NULL) { + ares__llist_node_t *next = ares__llist_node_next(cnode); + struct server_connection *conn = ares__llist_node_val(cnode); + ares_bool_t do_cleanup = ARES_FALSE; + cnode = next; + + /* Has connections, not eligible */ + if (ares__llist_len(conn->queries_to_conn)) { + continue; + } + + /* If we are configured not to stay open, close it out */ + if (!(channel->flags & ARES_FLAG_STAYOPEN)) { + do_cleanup = ARES_TRUE; + } + + /* If the associated server has failures, close it out. Resetting the + * connection (and specifically the source port number) can help resolve + * situations where packets are being dropped. + */ + if (conn->server->consec_failures > 0) { + do_cleanup = ARES_TRUE; + } + + /* If the udp connection hit its max queries, always close it */ + if (!conn->is_tcp && channel->udp_max_queries > 0 && + conn->total_queries >= channel->udp_max_queries) { + do_cleanup = ARES_TRUE; + } + + if (!do_cleanup) { + continue; + } + + /* Clean it up */ + ares__close_connection(conn, ARES_SUCCESS); + } } - - /* If we are configured not to stay open, close it out */ - if (!(channel->flags & ARES_FLAG_STAYOPEN)) { - do_cleanup = ARES_TRUE; - } - - /* If the associated server has failures, close it out. Resetting the - * connection (and specifically the source port number) can help resolve - * situations where packets are being dropped. - */ - if (conn->server->consec_failures > 0) { - do_cleanup = ARES_TRUE; - } - - /* If the udp connection hit its max queries, always close it */ - if (!conn->is_tcp && channel->udp_max_queries > 0 && - conn->total_queries >= channel->udp_max_queries) { - do_cleanup = ARES_TRUE; - } - - if (!do_cleanup) { - return; - } - - ares__close_connection(conn); } diff --git a/deps/cares/src/lib/ares__hosts_file.c b/deps/cares/src/lib/ares__hosts_file.c index 271386e7b2524d..ae9c071d7aa90b 100644 --- a/deps/cares/src/lib/ares__hosts_file.c +++ b/deps/cares/src/lib/ares__hosts_file.c @@ -767,126 +767,6 @@ ares_status_t ares__hosts_search_host(ares_channel_t *channel, return ARES_SUCCESS; } -ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry, - int family, struct hostent **hostent) -{ - ares_status_t status; - size_t naliases; - ares__llist_node_t *node; - size_t idx; - - *hostent = ares_malloc_zero(sizeof(**hostent)); - if (*hostent == NULL) { - status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ - goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ - } - - (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)family; - - /* Copy IP addresses that match the address family */ - idx = 0; - for (node = ares__llist_node_first(entry->ips); node != NULL; - node = ares__llist_node_next(node)) { - struct ares_addr addr; - const void *ptr = NULL; - size_t ptr_len = 0; - const char *ipaddr = ares__llist_node_val(node); - char **temp = NULL; - - memset(&addr, 0, sizeof(addr)); - - addr.family = family; - ptr = ares_dns_pton(ipaddr, &addr, &ptr_len); - if (ptr == NULL) { - continue; - } - - /* If family == AF_UNSPEC, then we want to inherit this for future - * conversions as we can only support a single address class */ - if (family == AF_UNSPEC) { - family = addr.family; - (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)addr.family; - } - - temp = ares_realloc_zero((*hostent)->h_addr_list, - (idx + 1) * sizeof(*(*hostent)->h_addr_list), - (idx + 2) * sizeof(*(*hostent)->h_addr_list)); - if (temp == NULL) { - status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ - goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ - } - - (*hostent)->h_addr_list = temp; - - (*hostent)->h_addr_list[idx] = ares_malloc(ptr_len); - if ((*hostent)->h_addr_list[idx] == NULL) { - status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ - goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ - } - - memcpy((*hostent)->h_addr_list[idx], ptr, ptr_len); - idx++; - (*hostent)->h_length = (HOSTENT_LENGTH_TYPE)ptr_len; - } - - /* entry didn't match address class */ - if (idx == 0) { - status = ARES_ENOTFOUND; - goto fail; - } - - /* Copy main hostname */ - (*hostent)->h_name = ares_strdup(ares__llist_first_val(entry->hosts)); - if ((*hostent)->h_name == NULL) { - status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ - goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ - } - - /* Copy aliases */ - naliases = ares__llist_len(entry->hosts) - 1; - - /* Cap at 100, some people use https://github.com/StevenBlack/hosts and we - * don't need 200k+ aliases */ - if (naliases > 100) { - naliases = 100; /* LCOV_EXCL_LINE: DefensiveCoding */ - } - - (*hostent)->h_aliases = - ares_malloc_zero((naliases + 1) * sizeof(*(*hostent)->h_aliases)); - if ((*hostent)->h_aliases == NULL) { - status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ - goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ - } - - /* Copy all entries to the alias except the first */ - idx = 0; - node = ares__llist_node_first(entry->hosts); - node = ares__llist_node_next(node); - while (node != NULL) { - (*hostent)->h_aliases[idx] = ares_strdup(ares__llist_node_val(node)); - if ((*hostent)->h_aliases[idx] == NULL) { - status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ - goto fail; /* LCOV_EXCL_LINE: OutOfMemory */ - } - idx++; - - /* Break out if artificially capped */ - if (idx == naliases) { - break; - } - node = ares__llist_node_next(node); - } - - return ARES_SUCCESS; - -/* LCOV_EXCL_START: defensive coding */ -fail: - ares_free_hostent(*hostent); - *hostent = NULL; - return status; - /* LCOV_EXCL_STOP */ -} - static ares_status_t ares__hosts_ai_append_cnames(const ares_hosts_entry_t *entry, struct ares_addrinfo_cname **cnames_out) @@ -980,10 +860,12 @@ ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry, return ARES_EBADFAMILY; /* LCOV_EXCL_LINE: DefensiveCoding */ } - ai->name = ares_strdup(name); - if (ai->name == NULL) { - status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ - goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + if (name != NULL) { + ai->name = ares_strdup(name); + if (ai->name == NULL) { + status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ + goto done; /* LCOV_EXCL_LINE: OutOfMemory */ + } } for (node = ares__llist_node_first(entry->ips); node != NULL; @@ -1031,3 +913,35 @@ ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry, return status; } + +ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry, + int family, struct hostent **hostent) +{ + ares_status_t status; + struct ares_addrinfo *ai = ares_malloc_zero(sizeof(*ai)); + + *hostent = NULL; + + if (ai == NULL) { + return ARES_ENOMEM; + } + + status = ares__hosts_entry_to_addrinfo(entry, NULL, family, 0, ARES_TRUE, ai); + if (status != ARES_SUCCESS) { + goto done; + } + + status = ares__addrinfo2hostent(ai, family, hostent); + if (status != ARES_SUCCESS) { + goto done; + } + +done: + ares_freeaddrinfo(ai); + if (status != ARES_SUCCESS) { + ares_free_hostent(*hostent); + *hostent = NULL; + } + + return status; +} diff --git a/deps/cares/src/lib/ares__socket.c b/deps/cares/src/lib/ares__socket.c index 7796ab2cbef9d4..3db19f7e8232f4 100644 --- a/deps/cares/src/lib/ares__socket.c +++ b/deps/cares/src/lib/ares__socket.c @@ -184,6 +184,14 @@ static int configure_socket(ares_socket_t s, struct server_state *server) } #endif + /* No need to emit SIGPIPE on socket errors */ +#if defined(SO_NOSIGPIPE) + { + int opt = 1; + setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt)); + } +#endif + /* Set the socket's send and receive buffer sizes. */ if ((channel->socket_send_buffer_size > 0) && setsockopt(s, SOL_SOCKET, SO_SNDBUF, diff --git a/deps/cares/src/lib/ares_cancel.c b/deps/cares/src/lib/ares_cancel.c index c47bdd8a2b7988..4312b5e188e567 100644 --- a/deps/cares/src/lib/ares_cancel.c +++ b/deps/cares/src/lib/ares_cancel.c @@ -60,28 +60,26 @@ void ares_cancel(ares_channel_t *channel) node = ares__llist_node_first(list_copy); while (node != NULL) { struct query *query; - struct server_connection *conn; /* Cache next since this node is being deleted */ next = ares__llist_node_next(node); query = ares__llist_node_claim(node); - conn = query->conn; query->node_all_queries = NULL; /* NOTE: its possible this may enqueue new queries */ query->callback(query->arg, ARES_ECANCELLED, 0, NULL); ares__free_query(query); - /* See if the connection should be cleaned up */ - ares__check_cleanup_conn(channel, conn); - node = next; } ares__llist_destroy(list_copy); } + /* See if the connections should be cleaned up */ + ares__check_cleanup_conns(channel); + ares_queue_notify_empty(channel); done: diff --git a/deps/cares/src/lib/ares_dns_private.h b/deps/cares/src/lib/ares_dns_private.h index c82fb6a9abe23f..2dd468a5559d06 100644 --- a/deps/cares/src/lib/ares_dns_private.h +++ b/deps/cares/src/lib/ares_dns_private.h @@ -26,6 +26,8 @@ #ifndef __ARES_DNS_PRIVATE_H #define __ARES_DNS_PRIVATE_H +ares_status_t ares_dns_record_duplicate_ex(ares_dns_record_t **dest, + const ares_dns_record_t *src); ares_bool_t ares_dns_rec_type_allow_name_compression(ares_dns_rec_type_t type); ares_bool_t ares_dns_opcode_isvalid(ares_dns_opcode_t opcode); ares_bool_t ares_dns_rcode_isvalid(ares_dns_rcode_t rcode); diff --git a/deps/cares/src/lib/ares_dns_record.c b/deps/cares/src/lib/ares_dns_record.c index 0131743e4621d8..8545abecf74602 100644 --- a/deps/cares/src/lib/ares_dns_record.c +++ b/deps/cares/src/lib/ares_dns_record.c @@ -296,6 +296,7 @@ ares_status_t ares_dns_record_query_set_name(ares_dns_record_t *dnsrec, if (dnsrec == NULL || idx >= dnsrec->qdcount || name == NULL) { return ARES_EFORMERR; } + orig_name = dnsrec->qd[idx].name; dnsrec->qd[idx].name = ares_strdup(name); if (dnsrec->qd[idx].name == NULL) { @@ -1622,26 +1623,34 @@ ares_status_t return status; } -ares_dns_record_t *ares_dns_record_duplicate(const ares_dns_record_t *dnsrec) +ares_status_t ares_dns_record_duplicate_ex(ares_dns_record_t **dest, + const ares_dns_record_t *src) { unsigned char *data = NULL; size_t data_len = 0; - ares_dns_record_t *out = NULL; ares_status_t status; - if (dnsrec == NULL) { - return NULL; + if (dest == NULL || src == NULL) { + return ARES_EFORMERR; } - status = ares_dns_write(dnsrec, &data, &data_len); + *dest = NULL; + + status = ares_dns_write(src, &data, &data_len); if (status != ARES_SUCCESS) { - return NULL; + return status; } - status = ares_dns_parse(data, data_len, 0, &out); + status = ares_dns_parse(data, data_len, 0, dest); ares_free(data); - if (status != ARES_SUCCESS) { - return NULL; - } - return out; + + return status; +} + +ares_dns_record_t *ares_dns_record_duplicate(const ares_dns_record_t *dnsrec) +{ + ares_dns_record_t *dest = NULL; + + ares_dns_record_duplicate_ex(&dest, dnsrec); + return dest; } diff --git a/deps/cares/src/lib/ares_event_select.c b/deps/cares/src/lib/ares_event_select.c index 980652002c3372..e1266ea99056a3 100644 --- a/deps/cares/src/lib/ares_event_select.c +++ b/deps/cares/src/lib/ares_event_select.c @@ -81,12 +81,14 @@ static size_t ares_evsys_select_wait(ares_event_thread_t *e, size_t i; fd_set read_fds; fd_set write_fds; + fd_set except_fds; int nfds = 0; struct timeval tv; struct timeval *tout = NULL; FD_ZERO(&read_fds); FD_ZERO(&write_fds); + FD_ZERO(&except_fds); for (i = 0; i < num_fds; i++) { const ares_event_t *ev = @@ -97,6 +99,7 @@ static size_t ares_evsys_select_wait(ares_event_thread_t *e, if (ev->flags & ARES_EVENT_FLAG_WRITE) { FD_SET(ev->fd, &write_fds); } + FD_SET(ev->fd, &except_fds); if (ev->fd + 1 > nfds) { nfds = ev->fd + 1; } @@ -108,7 +111,7 @@ static size_t ares_evsys_select_wait(ares_event_thread_t *e, tout = &tv; } - rv = select(nfds, &read_fds, &write_fds, NULL, tout); + rv = select(nfds, &read_fds, &write_fds, &except_fds, tout); if (rv > 0) { for (i = 0; i < num_fds; i++) { ares_event_t *ev; @@ -119,7 +122,7 @@ static size_t ares_evsys_select_wait(ares_event_thread_t *e, continue; /* LCOV_EXCL_LINE: DefensiveCoding */ } - if (FD_ISSET(fdlist[i], &read_fds)) { + if (FD_ISSET(fdlist[i], &read_fds) || FD_ISSET(fdlist[i], &except_fds)) { flags |= ARES_EVENT_FLAG_READ; } diff --git a/deps/cares/src/lib/ares_event_win32.c b/deps/cares/src/lib/ares_event_win32.c index ac41038b7254ec..173d0ca9c9d1a9 100644 --- a/deps/cares/src/lib/ares_event_win32.c +++ b/deps/cares/src/lib/ares_event_win32.c @@ -337,7 +337,9 @@ typedef struct { static void ares_afd_handle_destroy(void *arg) { ares_afd_handle_t *hnd = arg; - CloseHandle(hnd->afd_handle); + if (hnd != NULL && hnd->afd_handle != NULL) { + CloseHandle(hnd->afd_handle); + } ares_free(hnd); } @@ -791,7 +793,11 @@ static void ares_evsys_win32_event_mod(ares_event_t *event, static ares_bool_t ares_evsys_win32_process_other_event( ares_evsys_win32_t *ew, ares_evsys_win32_eventdata_t *ed, size_t i) { - ares_event_t *event = ed->event; + ares_event_t *event; + + /* NOTE: do NOT dereference 'ed' if during shutdown as this could be an + * invalid pointer if the signal handle was cleaned up, but there was still a + * pending event! */ if (ew->is_shutdown) { CARES_DEBUG_LOG("\t\t** i=%lu, skip non-socket handle during shutdown\n", @@ -799,6 +805,7 @@ static ares_bool_t ares_evsys_win32_process_other_event( return ARES_FALSE; } + event = ed->event; CARES_DEBUG_LOG("\t\t** i=%lu, ed=%p (data)\n", (unsigned long)i, (void *)ed); event->cb(event->e, event->fd, event->data, ARES_EVENT_FLAG_OTHER); diff --git a/deps/cares/src/lib/ares_metrics.c b/deps/cares/src/lib/ares_metrics.c index 4b40677239fdbe..78f93bc1b7d649 100644 --- a/deps/cares/src/lib/ares_metrics.c +++ b/deps/cares/src/lib/ares_metrics.c @@ -211,7 +211,7 @@ size_t ares_metrics_server_timeout(const struct server_state *server, const ares_channel_t *channel = server->channel; ares_server_bucket_t i; size_t timeout_ms = 0; - + size_t max_timeout_ms; for (i = 0; i < ARES_METRIC_COUNT; i++) { time_t ts = ares_metric_timestamp(i, now, ARES_FALSE); @@ -252,10 +252,9 @@ size_t ares_metrics_server_timeout(const struct server_state *server, } /* don't go above upper bounds */ - if (channel->maxtimeout && timeout_ms > channel->maxtimeout) { - timeout_ms = channel->maxtimeout; - } else if (timeout_ms > MAX_TIMEOUT_MS) { - timeout_ms = MAX_TIMEOUT_MS; + max_timeout_ms = channel->maxtimeout?channel->maxtimeout:MAX_TIMEOUT_MS; + if (timeout_ms > max_timeout_ms) { + timeout_ms = max_timeout_ms; } return timeout_ms; diff --git a/deps/cares/src/lib/ares_private.h b/deps/cares/src/lib/ares_private.h index e389b5700401aa..5506416d8efefb 100644 --- a/deps/cares/src/lib/ares_private.h +++ b/deps/cares/src/lib/ares_private.h @@ -410,7 +410,8 @@ ares_bool_t ares__timedout(const ares_timeval_t *now, /* Returns one of the normal ares status codes like ARES_SUCCESS */ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now); ares_status_t ares__requeue_query(struct query *query, - const ares_timeval_t *now); + const ares_timeval_t *now, + ares_status_t status); /*! Retrieve a list of names to use for searching. The first successful * query in the list wins. This function also uses the HOSTSALIASES file @@ -438,10 +439,10 @@ void *ares__dnsrec_convert_arg(ares_callback callback, void *arg); void ares__dnsrec_convert_cb(void *arg, ares_status_t status, size_t timeouts, const ares_dns_record_t *dnsrec); -void ares__close_connection(struct server_connection *conn); +void ares__close_connection(struct server_connection *conn, + ares_status_t requeue_status); void ares__close_sockets(struct server_state *server); -void ares__check_cleanup_conn(const ares_channel_t *channel, - struct server_connection *conn); +void ares__check_cleanup_conns(const ares_channel_t *channel); void ares__free_query(struct query *query); ares_rand_state *ares__init_rand_state(void); diff --git a/deps/cares/src/lib/ares_process.c b/deps/cares/src/lib/ares_process.c index 562d6b5e1b1e3c..01d9e6a49d3431 100644 --- a/deps/cares/src/lib/ares_process.c +++ b/deps/cares/src/lib/ares_process.c @@ -58,7 +58,8 @@ static ares_status_t process_answer(ares_channel_t *channel, struct server_connection *conn, ares_bool_t tcp, const ares_timeval_t *now); static void handle_conn_error(struct server_connection *conn, - ares_bool_t critical_failure); + ares_bool_t critical_failure, + ares_status_t failure_status); static ares_bool_t same_questions(const struct query *query, const ares_dns_record_t *arec); @@ -68,6 +69,17 @@ static void end_query(ares_channel_t *channel, struct server_state *server, struct query *query, ares_status_t status, const ares_dns_record_t *dnsrec); + +static void ares__query_disassociate_from_conn(struct query *query) +{ + /* If its not part of a connection, it can't be tracked for timeouts either */ + ares__slist_node_destroy(query->node_queries_by_timeout); + ares__llist_node_destroy(query->node_queries_to_conn); + query->node_queries_by_timeout = NULL; + query->node_queries_to_conn = NULL; + query->conn = NULL; +} + /* Invoke the server state callback after a success or failure */ static void invoke_server_state_cb(const struct server_state *server, ares_bool_t success, int flags) @@ -202,6 +214,8 @@ static void processfds(ares_channel_t *channel, fd_set *read_fds, /* Write last as the other 2 operations might have triggered writes */ write_tcp_data(channel, write_fds, write_fd); + /* See if any connections should be cleaned up */ + ares__check_cleanup_conns(channel); ares__channel_unlock(channel); } @@ -301,7 +315,7 @@ static void write_tcp_data(ares_channel_t *channel, fd_set *write_fds, count = ares__socket_write(channel, server->tcp_conn->fd, data, data_len); if (count <= 0) { if (!try_again(SOCKERRNO)) { - handle_conn_error(server->tcp_conn, ARES_TRUE); + handle_conn_error(server->tcp_conn, ARES_TRUE, ARES_ECONNREFUSED); } continue; } @@ -334,7 +348,7 @@ static void read_tcp_data(ares_channel_t *channel, ptr = ares__buf_append_start(server->tcp_parser, &ptr_len); if (ptr == NULL) { - handle_conn_error(conn, ARES_FALSE /* not critical to connection */); + handle_conn_error(conn, ARES_FALSE /* not critical to connection */, ARES_SUCCESS); return; /* bail out on malloc failure. TODO: make this function return error codes */ } @@ -344,7 +358,7 @@ static void read_tcp_data(ares_channel_t *channel, if (count <= 0) { ares__buf_append_finish(server->tcp_parser, 0); if (!(count == -1 && try_again(SOCKERRNO))) { - handle_conn_error(conn, ARES_TRUE); + handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED); } return; } @@ -388,15 +402,13 @@ static void read_tcp_data(ares_channel_t *channel, /* We finished reading this answer; process it */ status = process_answer(channel, data, data_len, conn, ARES_TRUE, now); if (status != ARES_SUCCESS) { - handle_conn_error(conn, ARES_TRUE); + handle_conn_error(conn, ARES_TRUE, status); return; } /* Since we processed the answer, clear the tag so space can be reclaimed */ ares__buf_tag_clear(server->tcp_parser); } - - ares__check_cleanup_conn(channel, conn); } static int socket_list_append(ares_socket_t **socketlist, ares_socket_t fd, @@ -503,7 +515,7 @@ static void read_udp_packets_fd(ares_channel_t *channel, break; } - handle_conn_error(conn, ARES_TRUE); + handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED); return; #ifdef HAVE_RECVFROM } else if (!same_address(&from.sa, &conn->server->addr)) { @@ -520,8 +532,6 @@ static void read_udp_packets_fd(ares_channel_t *channel, /* Try to read again only if *we* set up the socket, otherwise it may be * a blocking socket and would cause recvfrom to hang. */ } while (read_len >= 0 && channel->sock_funcs == NULL); - - ares__check_cleanup_conn(channel, conn); } static void read_packets(ares_channel_t *channel, fd_set *read_fds, @@ -594,28 +604,26 @@ static void read_packets(ares_channel_t *channel, fd_set *read_fds, /* If any queries have timed out, note the timeout and move them on. */ static void process_timeouts(ares_channel_t *channel, const ares_timeval_t *now) { - ares__slist_node_t *node = - ares__slist_node_first(channel->queries_by_timeout); - while (node != NULL) { + ares__slist_node_t *node; + + /* Just keep popping off the first as this list will re-sort as things come + * and go. We don't want to try to rely on 'next' as some operation might + * cause a cleanup of that pointer and would become invalid */ + while ((node = ares__slist_node_first(channel->queries_by_timeout)) != NULL) { struct query *query = ares__slist_node_val(node); - /* Node might be removed, cache next */ - ares__slist_node_t *next = ares__slist_node_next(node); struct server_connection *conn; + /* Since this is sorted, as soon as we hit a query that isn't timed out, * break */ if (!ares__timedout(now, &query->timeout)) { break; } - query->error_status = ARES_ETIMEOUT; query->timeouts++; conn = query->conn; server_increment_failures(conn->server, query->using_tcp); - ares__requeue_query(query, now); - ares__check_cleanup_conn(channel, conn); - - node = next; + ares__requeue_query(query, now, ARES_ETIMEOUT); } } @@ -733,20 +741,20 @@ static ares_status_t process_answer(ares_channel_t *channel, rcode == ARES_RCODE_REFUSED) { switch (rcode) { case ARES_RCODE_SERVFAIL: - query->error_status = ARES_ESERVFAIL; + status = ARES_ESERVFAIL; break; case ARES_RCODE_NOTIMP: - query->error_status = ARES_ENOTIMP; + status = ARES_ENOTIMP; break; case ARES_RCODE_REFUSED: - query->error_status = ARES_EREFUSED; + status = ARES_EREFUSED; break; default: break; } server_increment_failures(server, query->using_tcp); - ares__requeue_query(query, now); + ares__requeue_query(query, now, status); /* Should any of these cause a connection termination? * Maybe SERVER_FAILURE? */ @@ -776,7 +784,8 @@ static ares_status_t process_answer(ares_channel_t *channel, } static void handle_conn_error(struct server_connection *conn, - ares_bool_t critical_failure) + ares_bool_t critical_failure, + ares_status_t failure_status) { struct server_state *server = conn->server; @@ -787,17 +796,23 @@ static void handle_conn_error(struct server_connection *conn, } /* This will requeue any connections automatically */ - ares__close_connection(conn); + ares__close_connection(conn, failure_status); } ares_status_t ares__requeue_query(struct query *query, - const ares_timeval_t *now) + const ares_timeval_t *now, + ares_status_t status) { ares_channel_t *channel = query->channel; size_t max_tries = ares__slist_len(channel->servers) * channel->tries; - query->try_count++; + ares__query_disassociate_from_conn(query); + if (status != ARES_SUCCESS) { + query->error_status = status; + } + + query->try_count++; if (query->try_count < max_tries && !query->no_retries) { return ares__send_query(query, now); } @@ -947,7 +962,14 @@ static ares_status_t ares__write_udpbuf(ares_channel_t *channel, } if (ares__socket_write(channel, fd, qbuf, qbuf_len) == -1) { - status = ARES_ESERVFAIL; + if (try_again(SOCKERRNO)) { + status = ARES_ESERVFAIL; + } else { + /* UDP is connection-less, but we might receive an ICMP unreachable which + * means we can't talk to the remote host at all and that will be + * reflected here */ + status = ARES_ECONNREFUSED; + } } else { status = ARES_SUCCESS; } @@ -1018,8 +1040,6 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now) ares_status_t status; ares_bool_t new_connection = ARES_FALSE; - query->conn = NULL; - /* Choose the server to send the query to */ if (channel->rotate) { /* Pull random server */ @@ -1052,8 +1072,7 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now) case ARES_ECONNREFUSED: case ARES_EBADFAMILY: server_increment_failures(server, query->using_tcp); - query->error_status = status; - return ares__requeue_query(query, now); + return ares__requeue_query(query, now, status); /* Anything else is not retryable, likely ENOMEM */ default: @@ -1073,7 +1092,7 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now) /* Only safe to kill connection if it was new, otherwise it should be * cleaned up by another process later */ if (new_connection) { - ares__close_connection(conn); + ares__close_connection(conn, status); } return status; } @@ -1111,8 +1130,7 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now) case ARES_ECONNREFUSED: case ARES_EBADFAMILY: server_increment_failures(server, query->using_tcp); - query->error_status = status; - return ares__requeue_query(query, now); + return ares__requeue_query(query, now, status); /* Anything else is not retryable, likely ENOMEM */ default: @@ -1132,14 +1150,27 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now) return status; } - /* FIXME: Handle EAGAIN here since it likely can happen. */ + if (status == ARES_ECONNREFUSED) { + handle_conn_error(conn, ARES_TRUE, status); + + /* This query wasn't yet bound to the connection, need to manually + * requeue it and return an appropriate error */ + status = ares__requeue_query(query, now, status); + if (status == ARES_ETIMEOUT) { + status = ARES_ECONNREFUSED; + } + return status; + } + + /* FIXME: Handle EAGAIN here since it likely can happen. Right now we + * just requeue to a different server/connection. */ server_increment_failures(server, query->using_tcp); - status = ares__requeue_query(query, now); + status = ares__requeue_query(query, now, status); /* Only safe to kill connection if it was new, otherwise it should be * cleaned up by another process later */ if (new_connection) { - ares__close_connection(conn); + ares__close_connection(conn, status); } return status; @@ -1162,7 +1193,7 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now) /* Only safe to kill connection if it was new, otherwise it should be * cleaned up by another process later */ if (new_connection) { - ares__close_connection(conn); + ares__close_connection(conn, ARES_SUCCESS); } return ARES_ENOMEM; /* LCOV_EXCL_STOP */ @@ -1180,7 +1211,7 @@ ares_status_t ares__send_query(struct query *query, const ares_timeval_t *now) /* Only safe to kill connection if it was new, otherwise it should be * cleaned up by another process later */ if (new_connection) { - ares__close_connection(conn); + ares__close_connection(conn, ARES_SUCCESS); } return ARES_ENOMEM; /* LCOV_EXCL_STOP */ @@ -1283,12 +1314,9 @@ static ares_bool_t same_address(const struct sockaddr *sa, static void ares_detach_query(struct query *query) { /* Remove the query from all the lists in which it is linked */ + ares__query_disassociate_from_conn(query); ares__htable_szvp_remove(query->channel->queries_by_qid, query->qid); - ares__slist_node_destroy(query->node_queries_by_timeout); - ares__llist_node_destroy(query->node_queries_to_conn); ares__llist_node_destroy(query->node_all_queries); - query->node_queries_by_timeout = NULL; - query->node_queries_to_conn = NULL; query->node_all_queries = NULL; } diff --git a/deps/cares/src/lib/ares_send.c b/deps/cares/src/lib/ares_send.c index 94e7e12b42d0ce..e07ef5b51feafe 100644 --- a/deps/cares/src/lib/ares_send.c +++ b/deps/cares/src/lib/ares_send.c @@ -145,11 +145,11 @@ ares_status_t ares_send_nolock(ares_channel_t *channel, query->using_tcp = (channel->flags & ARES_FLAG_USEVC)?ARES_TRUE:ARES_FALSE; /* Duplicate Query */ - query->query = ares_dns_record_duplicate(dnsrec); - if (query->query == NULL) { + status = ares_dns_record_duplicate_ex(&query->query, dnsrec); + if (status != ARES_SUCCESS) { ares_free(query); - callback(arg, ARES_ENOMEM, 0, NULL); - return ARES_ENOMEM; + callback(arg, status, 0, NULL); + return status; } ares_dns_record_set_id(query->query, id); diff --git a/deps/cares/src/tools/CMakeLists.txt b/deps/cares/src/tools/CMakeLists.txt index 0016ca4c0b1a0a..e23d0f23c781f7 100644 --- a/deps/cares/src/tools/CMakeLists.txt +++ b/deps/cares/src/tools/CMakeLists.txt @@ -32,6 +32,9 @@ IF (CARES_BUILD_TOOLS) # Build adig ADD_EXECUTABLE (adig adig.c ${SAMPLESOURCES}) + # Don't build adig and ahost in parallel. This is to prevent a Windows MSVC + # build error due to them both using the same source files. + ADD_DEPENDENCIES(adig ahost) TARGET_INCLUDE_DIRECTORIES (adig PUBLIC "$" "$"