visaljs 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/THIRD_PARTY_LICENSES +31 -1
- package/cli.js +3745 -890
- package/node/include/js_node.h +12 -1
- package/node/prelude/_http_common.js +29 -0
- package/node/prelude/async_hooks.js +50 -11
- package/node/prelude/child_process.js +31 -4
- package/node/prelude/console.js +311 -143
- package/node/prelude/crypto.js +6 -4
- package/node/prelude/dgram.js +1169 -0
- package/node/prelude/diagnostics_channel.js +3 -1
- package/node/prelude/dns.js +250 -45
- package/node/prelude/dns_promises.js +8 -2
- package/node/prelude/fs_promises.js +9 -1
- package/node/prelude/http.js +405 -94
- package/node/prelude/https.js +13 -4
- package/node/prelude/internal/console_class.js +7 -0
- package/node/prelude/internal/console_global.js +25 -0
- package/node/prelude/internal/crypto/key_encryption.js +217 -0
- package/node/prelude/internal/crypto/keys.js +161 -26
- package/node/prelude/internal/crypto/random.js +24 -0
- package/node/prelude/internal/crypto/util.js +2 -1
- package/node/prelude/internal/crypto_x509.js +161 -0
- package/node/prelude/internal/dns_classes.js +6 -0
- package/node/prelude/internal/dns_resolver.js +371 -0
- package/node/prelude/internal/errors.js +9 -0
- package/node/prelude/internal/events_on.js +82 -37
- package/node/prelude/internal/features.js +8 -4
- package/node/prelude/internal/fs_filehandle.js +10 -4
- package/node/prelude/internal/http_chars.ts +22 -0
- package/node/prelude/internal/perf_histogram.js +1 -1
- package/node/prelude/internal/process_more.js +32 -17
- package/node/prelude/internal/process_signals.js +8 -2
- package/node/prelude/internal/punycode.ts +163 -0
- package/node/prelude/internal/readline.js +648 -274
- package/node/prelude/internal/readline_editor.js +1240 -0
- package/node/prelude/internal/string_width.js +43 -0
- package/node/prelude/internal/tls/common.js +145 -33
- package/node/prelude/internal/tls/wrap.js +182 -49
- package/node/prelude/internal/tls/x509.js +4 -1
- package/node/prelude/internal/udp.js +177 -0
- package/node/prelude/net.js +30 -14
- package/node/prelude/os.js +24 -6
- package/node/prelude/perf_hooks.js +37 -2
- package/node/prelude/punycode.js +2 -130
- package/node/prelude/readline.js +113 -4
- package/node/prelude/readline_promises.js +56 -32
- package/node/prelude/tls.js +21 -4
- package/node/prelude/util.js +3 -16
- package/node/src/host_dns.c +623 -0
- package/node/src/host_http.c +12 -0
- package/node/src/host_net.c +46 -0
- package/node/src/host_os.c +1 -6
- package/node/src/host_sys.c +79 -0
- package/node/src/host_udp.c +418 -0
- package/node/third_party/c-ares/LICENSE.md +24 -0
- package/node/third_party/c-ares/config/darwin/ares_config.h +573 -0
- package/node/third_party/c-ares/config/freebsd/ares_config.h +573 -0
- package/node/third_party/c-ares/config/linux/ares_config.h +573 -0
- package/node/third_party/c-ares/config/openbsd/ares_config.h +583 -0
- package/node/third_party/c-ares/include/ares.h +1237 -0
- package/node/third_party/c-ares/include/ares_build.h +202 -0
- package/node/third_party/c-ares/include/ares_dns.h +127 -0
- package/node/third_party/c-ares/include/ares_dns_record.h +1119 -0
- package/node/third_party/c-ares/include/ares_nameser.h +510 -0
- package/node/third_party/c-ares/include/ares_version.h +47 -0
- package/node/third_party/c-ares/src/lib/ares_addrinfo2hostent.c +305 -0
- package/node/third_party/c-ares/src/lib/ares_addrinfo_localhost.c +245 -0
- package/node/third_party/c-ares/src/lib/ares_android.c +496 -0
- package/node/third_party/c-ares/src/lib/ares_android.h +38 -0
- package/node/third_party/c-ares/src/lib/ares_cancel.c +87 -0
- package/node/third_party/c-ares/src/lib/ares_close_sockets.c +137 -0
- package/node/third_party/c-ares/src/lib/ares_conn.c +511 -0
- package/node/third_party/c-ares/src/lib/ares_conn.h +202 -0
- package/node/third_party/c-ares/src/lib/ares_cookie.c +461 -0
- package/node/third_party/c-ares/src/lib/ares_data.c +168 -0
- package/node/third_party/c-ares/src/lib/ares_data.h +92 -0
- package/node/third_party/c-ares/src/lib/ares_destroy.c +159 -0
- package/node/third_party/c-ares/src/lib/ares_free_hostent.c +54 -0
- package/node/third_party/c-ares/src/lib/ares_free_string.c +33 -0
- package/node/third_party/c-ares/src/lib/ares_freeaddrinfo.c +67 -0
- package/node/third_party/c-ares/src/lib/ares_getaddrinfo.c +806 -0
- package/node/third_party/c-ares/src/lib/ares_getenv.c +38 -0
- package/node/third_party/c-ares/src/lib/ares_getenv.h +34 -0
- package/node/third_party/c-ares/src/lib/ares_gethostbyaddr.c +229 -0
- package/node/third_party/c-ares/src/lib/ares_gethostbyname.c +341 -0
- package/node/third_party/c-ares/src/lib/ares_getnameinfo.c +437 -0
- package/node/third_party/c-ares/src/lib/ares_hosts_file.c +960 -0
- package/node/third_party/c-ares/src/lib/ares_inet_net_pton.h +31 -0
- package/node/third_party/c-ares/src/lib/ares_init.c +631 -0
- package/node/third_party/c-ares/src/lib/ares_ipv6.h +115 -0
- package/node/third_party/c-ares/src/lib/ares_library_init.c +190 -0
- package/node/third_party/c-ares/src/lib/ares_metrics.c +261 -0
- package/node/third_party/c-ares/src/lib/ares_options.c +492 -0
- package/node/third_party/c-ares/src/lib/ares_parse_into_addrinfo.c +179 -0
- package/node/third_party/c-ares/src/lib/ares_private.h +648 -0
- package/node/third_party/c-ares/src/lib/ares_process.c +1649 -0
- package/node/third_party/c-ares/src/lib/ares_qcache.c +442 -0
- package/node/third_party/c-ares/src/lib/ares_query.c +151 -0
- package/node/third_party/c-ares/src/lib/ares_search.c +623 -0
- package/node/third_party/c-ares/src/lib/ares_send.c +300 -0
- package/node/third_party/c-ares/src/lib/ares_set_socket_functions.c +590 -0
- package/node/third_party/c-ares/src/lib/ares_setup.h +224 -0
- package/node/third_party/c-ares/src/lib/ares_socket.c +427 -0
- package/node/third_party/c-ares/src/lib/ares_socket.h +163 -0
- package/node/third_party/c-ares/src/lib/ares_sortaddrinfo.c +447 -0
- package/node/third_party/c-ares/src/lib/ares_strerror.c +91 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig.c +642 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig_files.c +851 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig_mac.c +373 -0
- package/node/third_party/c-ares/src/lib/ares_sysconfig_win.c +673 -0
- package/node/third_party/c-ares/src/lib/ares_timeout.c +162 -0
- package/node/third_party/c-ares/src/lib/ares_update_servers.c +1376 -0
- package/node/third_party/c-ares/src/lib/ares_version.c +36 -0
- package/node/third_party/c-ares/src/lib/config-dos.h +114 -0
- package/node/third_party/c-ares/src/lib/config-win32.h +396 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_array.c +409 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable.c +451 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable.h +174 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_asvp.c +224 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_dict.c +230 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_strvp.c +210 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_szvp.c +188 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpstr.c +186 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpvp.c +194 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_llist.c +385 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_slist.c +479 -0
- package/node/third_party/c-ares/src/lib/dsa/ares_slist.h +207 -0
- package/node/third_party/c-ares/src/lib/event/ares_event.h +191 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_configchg.c +746 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_epoll.c +192 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_kqueue.c +248 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_poll.c +140 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_select.c +159 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_thread.c +575 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_wake_pipe.c +172 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_win32.c +981 -0
- package/node/third_party/c-ares/src/lib/event/ares_event_win32.h +161 -0
- package/node/third_party/c-ares/src/lib/include/ares_array.h +276 -0
- package/node/third_party/c-ares/src/lib/include/ares_buf.h +732 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_asvp.h +130 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_dict.h +123 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_strvp.h +130 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_szvp.h +118 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_vpstr.h +111 -0
- package/node/third_party/c-ares/src/lib/include/ares_htable_vpvp.h +128 -0
- package/node/third_party/c-ares/src/lib/include/ares_llist.h +239 -0
- package/node/third_party/c-ares/src/lib/include/ares_mem.h +41 -0
- package/node/third_party/c-ares/src/lib/include/ares_str.h +261 -0
- package/node/third_party/c-ares/src/lib/inet_net_pton.c +451 -0
- package/node/third_party/c-ares/src/lib/inet_ntop.c +219 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_create_query.c +78 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_expand_name.c +99 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_expand_string.c +107 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_fds.c +80 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_getsock.c +85 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_a_reply.c +107 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_aaaa_reply.c +109 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_caa_reply.c +137 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_mx_reply.c +110 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_naptr_reply.c +132 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_ns_reply.c +154 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_ptr_reply.c +213 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_soa_reply.c +115 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_srv_reply.c +114 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_txt_reply.c +144 -0
- package/node/third_party/c-ares/src/lib/legacy/ares_parse_uri_reply.c +113 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_mapping.c +982 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.c +309 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.h +72 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_name.c +736 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_parse.c +1360 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_private.h +273 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_record.c +1701 -0
- package/node/third_party/c-ares/src/lib/record/ares_dns_write.c +1229 -0
- package/node/third_party/c-ares/src/lib/str/ares_buf.c +1510 -0
- package/node/third_party/c-ares/src/lib/str/ares_str.c +562 -0
- package/node/third_party/c-ares/src/lib/str/ares_strsplit.c +90 -0
- package/node/third_party/c-ares/src/lib/str/ares_strsplit.h +51 -0
- package/node/third_party/c-ares/src/lib/thirdparty/apple/dnsinfo.h +122 -0
- package/node/third_party/c-ares/src/lib/util/ares_iface_ips.c +640 -0
- package/node/third_party/c-ares/src/lib/util/ares_iface_ips.h +140 -0
- package/node/third_party/c-ares/src/lib/util/ares_math.c +167 -0
- package/node/third_party/c-ares/src/lib/util/ares_math.h +58 -0
- package/node/third_party/c-ares/src/lib/util/ares_rand.c +389 -0
- package/node/third_party/c-ares/src/lib/util/ares_rand.h +36 -0
- package/node/third_party/c-ares/src/lib/util/ares_threads.c +613 -0
- package/node/third_party/c-ares/src/lib/util/ares_threads.h +60 -0
- package/node/third_party/c-ares/src/lib/util/ares_time.h +49 -0
- package/node/third_party/c-ares/src/lib/util/ares_timeval.c +95 -0
- package/node/third_party/c-ares/src/lib/util/ares_uri.c +1627 -0
- package/node/third_party/c-ares/src/lib/util/ares_uri.h +252 -0
- package/node/third_party/c-ares/src/lib/windows_port.c +29 -0
- package/package.json +1 -1
- package/prebuilt/darwin-arm64/cares-a6286c65ca9374a3.a +0 -0
- package/prebuilt/darwin-x64/cares-a6286c65ca9374a3.a +0 -0
- package/prebuilt/linux-arm64/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/linux-arm64-musl/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/linux-x64/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/linux-x64-musl/cares-4ba05ac8e948af8e.a +0 -0
- package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
- package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
- package/prebuilt/win32-x64/cares-49c9ec95162b912d.a +0 -0
- package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
- package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
- package/runtime/include/js_buffer.h +185 -0
- package/runtime/include/js_builtins.h +58 -0
- package/runtime/include/js_crypto.h +11 -0
- package/runtime/include/js_exception.h +3 -0
- package/runtime/include/js_function.h +1 -1
- package/runtime/include/js_gc.h +13 -10
- package/runtime/include/js_jobs.h +2 -0
- package/runtime/include/js_object.h +79 -3
- package/runtime/include/js_ops.h +27 -0
- package/runtime/include/js_runtime.h +1 -0
- package/runtime/include/js_web.h +0 -3
- package/runtime/include/jscontext.h +8 -0
- package/runtime/src/builtins.c +17 -4
- package/runtime/src/crypto_internal.h +1 -0
- package/runtime/src/crypto_subtle.c +66 -7
- package/runtime/src/crypto_tls.c +265 -19
- package/runtime/src/function.c +37 -10
- package/runtime/src/gc.c +76 -93
- package/runtime/src/inspect.c +18 -1
- package/runtime/src/iterator.c +5 -1
- package/runtime/src/jobs.c +37 -22
- package/runtime/src/object.c +91 -8
- package/runtime/src/os.c +16 -6
- package/runtime/src/os.h +2 -0
- package/runtime/src/process.c +97 -10
- package/runtime/src/typedarray.c +23 -0
|
@@ -0,0 +1,1649 @@
|
|
|
1
|
+
/* MIT License
|
|
2
|
+
*
|
|
3
|
+
* Copyright (c) 1998 Massachusetts Institute of Technology
|
|
4
|
+
* Copyright (c) 2010 Daniel Stenberg
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice (including the next
|
|
14
|
+
* paragraph) shall be included in all copies or substantial portions of the
|
|
15
|
+
* Software.
|
|
16
|
+
*
|
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
* SOFTWARE.
|
|
24
|
+
*
|
|
25
|
+
* SPDX-License-Identifier: MIT
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
#include "ares_private.h"
|
|
29
|
+
|
|
30
|
+
#ifdef HAVE_STRINGS_H
|
|
31
|
+
# include <strings.h>
|
|
32
|
+
#endif
|
|
33
|
+
#ifdef HAVE_SYS_IOCTL_H
|
|
34
|
+
# include <sys/ioctl.h>
|
|
35
|
+
#endif
|
|
36
|
+
#ifdef NETWARE
|
|
37
|
+
# include <sys/filio.h>
|
|
38
|
+
#endif
|
|
39
|
+
#ifdef HAVE_STDINT_H
|
|
40
|
+
# include <stdint.h>
|
|
41
|
+
#endif
|
|
42
|
+
|
|
43
|
+
#include <assert.h>
|
|
44
|
+
#include <fcntl.h>
|
|
45
|
+
#include <limits.h>
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
static ares_status_t process_write(ares_channel_t *channel,
|
|
49
|
+
ares_socket_t write_fd);
|
|
50
|
+
static ares_status_t process_read(ares_channel_t *channel,
|
|
51
|
+
ares_socket_t read_fd,
|
|
52
|
+
const ares_timeval_t *now);
|
|
53
|
+
static ares_status_t process_timeouts(ares_channel_t *channel,
|
|
54
|
+
const ares_timeval_t *now);
|
|
55
|
+
static ares_status_t process_answer(ares_channel_t *channel,
|
|
56
|
+
const unsigned char *abuf, size_t alen,
|
|
57
|
+
ares_conn_t *conn,
|
|
58
|
+
const ares_timeval_t *now,
|
|
59
|
+
ares_array_t **requeue);
|
|
60
|
+
static void handle_conn_error(ares_conn_t *conn, ares_bool_t critical_failure,
|
|
61
|
+
ares_status_t failure_status);
|
|
62
|
+
static ares_bool_t same_questions(const ares_query_t *query,
|
|
63
|
+
const ares_dns_record_t *arec);
|
|
64
|
+
static void end_query(ares_channel_t *channel, ares_server_t *server,
|
|
65
|
+
ares_query_t *query, ares_status_t status,
|
|
66
|
+
ares_dns_record_t *dnsrec,
|
|
67
|
+
ares_array_t **requeue);
|
|
68
|
+
static ares_status_t ares_send_query_int(ares_server_t *requested_server,
|
|
69
|
+
ares_query_t *query,
|
|
70
|
+
const ares_timeval_t *now,
|
|
71
|
+
ares_array_t **requeue);
|
|
72
|
+
static void ares_detach_query(ares_query_t *query);
|
|
73
|
+
|
|
74
|
+
static void ares_query_remove_from_conn(ares_query_t *query)
|
|
75
|
+
{
|
|
76
|
+
/* If its not part of a connection, it can't be tracked for timeouts either */
|
|
77
|
+
ares_slist_node_destroy(query->node_queries_by_timeout);
|
|
78
|
+
ares_llist_node_destroy(query->node_queries_to_conn);
|
|
79
|
+
query->node_queries_by_timeout = NULL;
|
|
80
|
+
query->node_queries_to_conn = NULL;
|
|
81
|
+
query->conn = NULL;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Invoke the server state callback after a success or failure */
|
|
85
|
+
static void invoke_server_state_cb(const ares_server_t *server,
|
|
86
|
+
ares_bool_t success, int flags)
|
|
87
|
+
{
|
|
88
|
+
const ares_channel_t *channel = server->channel;
|
|
89
|
+
ares_buf_t *buf;
|
|
90
|
+
ares_status_t status;
|
|
91
|
+
char *server_string;
|
|
92
|
+
|
|
93
|
+
if (channel->server_state_cb == NULL) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
buf = ares_buf_create();
|
|
98
|
+
if (buf == NULL) {
|
|
99
|
+
return; /* LCOV_EXCL_LINE: OutOfMemory */
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
status = ares_get_server_addr(server, buf);
|
|
103
|
+
if (status != ARES_SUCCESS) {
|
|
104
|
+
ares_buf_destroy(buf); /* LCOV_EXCL_LINE: OutOfMemory */
|
|
105
|
+
return; /* LCOV_EXCL_LINE: OutOfMemory */
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
server_string = ares_buf_finish_str(buf, NULL);
|
|
109
|
+
buf = NULL;
|
|
110
|
+
if (server_string == NULL) {
|
|
111
|
+
return; /* LCOV_EXCL_LINE: OutOfMemory */
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
channel->server_state_cb(server_string, success, flags,
|
|
115
|
+
channel->server_state_cb_data);
|
|
116
|
+
ares_free(server_string);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static void server_increment_failures(ares_server_t *server,
|
|
120
|
+
ares_bool_t used_tcp)
|
|
121
|
+
{
|
|
122
|
+
ares_slist_node_t *node;
|
|
123
|
+
const ares_channel_t *channel = server->channel;
|
|
124
|
+
ares_timeval_t next_retry_time;
|
|
125
|
+
|
|
126
|
+
node = ares_slist_node_find(channel->servers, server);
|
|
127
|
+
if (node == NULL) {
|
|
128
|
+
return; /* LCOV_EXCL_LINE: DefensiveCoding */
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Cap the failure count. Only the relative order matters for server
|
|
132
|
+
* selection, and an uncapped count would require an unbounded number of
|
|
133
|
+
* failures on other servers before a server that failed during an
|
|
134
|
+
* extended outage could be selected again. */
|
|
135
|
+
if (server->consec_failures < SERVER_CONSEC_FAILURES_CAP) {
|
|
136
|
+
server->consec_failures++;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Must update the retry time before reinserting since the sort uses it to
|
|
140
|
+
* order servers with the same failure count */
|
|
141
|
+
ares_tvnow(&next_retry_time);
|
|
142
|
+
ares_timeval_add(&next_retry_time, channel->server_retry_delay);
|
|
143
|
+
server->next_retry_time = next_retry_time;
|
|
144
|
+
|
|
145
|
+
ares_slist_node_reinsert(node);
|
|
146
|
+
|
|
147
|
+
invoke_server_state_cb(server, ARES_FALSE,
|
|
148
|
+
used_tcp == ARES_TRUE ? ARES_SERV_STATE_TCP
|
|
149
|
+
: ARES_SERV_STATE_UDP);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static void server_set_good(ares_server_t *server, ares_bool_t used_tcp)
|
|
153
|
+
{
|
|
154
|
+
ares_slist_node_t *node;
|
|
155
|
+
const ares_channel_t *channel = server->channel;
|
|
156
|
+
|
|
157
|
+
node = ares_slist_node_find(channel->servers, server);
|
|
158
|
+
if (node == NULL) {
|
|
159
|
+
return; /* LCOV_EXCL_LINE: DefensiveCoding */
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (server->consec_failures > 0) {
|
|
163
|
+
server->consec_failures = 0;
|
|
164
|
+
/* Must update the retry time before reinserting since the sort uses it
|
|
165
|
+
* to order servers with the same failure count */
|
|
166
|
+
server->next_retry_time.sec = 0;
|
|
167
|
+
server->next_retry_time.usec = 0;
|
|
168
|
+
ares_slist_node_reinsert(node);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
invoke_server_state_cb(server, ARES_TRUE,
|
|
172
|
+
used_tcp == ARES_TRUE ? ARES_SERV_STATE_TCP
|
|
173
|
+
: ARES_SERV_STATE_UDP);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* return true if now is exactly check time or later */
|
|
177
|
+
ares_bool_t ares_timedout(const ares_timeval_t *now,
|
|
178
|
+
const ares_timeval_t *check)
|
|
179
|
+
{
|
|
180
|
+
ares_int64_t secs = (now->sec - check->sec);
|
|
181
|
+
|
|
182
|
+
if (secs > 0) {
|
|
183
|
+
return ARES_TRUE; /* yes, timed out */
|
|
184
|
+
}
|
|
185
|
+
if (secs < 0) {
|
|
186
|
+
return ARES_FALSE; /* nope, not timed out */
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* if the full seconds were identical, check the sub second parts */
|
|
190
|
+
return ((ares_int64_t)now->usec - (ares_int64_t)check->usec) >= 0
|
|
191
|
+
? ARES_TRUE
|
|
192
|
+
: ARES_FALSE;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
static ares_status_t ares_process_fds_nolock(ares_channel_t *channel,
|
|
196
|
+
const ares_fd_events_t *events,
|
|
197
|
+
size_t nevents, unsigned int flags)
|
|
198
|
+
{
|
|
199
|
+
ares_timeval_t now;
|
|
200
|
+
size_t i;
|
|
201
|
+
ares_status_t status = ARES_SUCCESS;
|
|
202
|
+
|
|
203
|
+
if (channel == NULL || (events == NULL && nevents != 0)) {
|
|
204
|
+
return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
ares_tvnow(&now);
|
|
208
|
+
|
|
209
|
+
/* Process write events */
|
|
210
|
+
for (i = 0; i < nevents; i++) {
|
|
211
|
+
if (events[i].fd == ARES_SOCKET_BAD ||
|
|
212
|
+
!(events[i].events & ARES_FD_EVENT_WRITE)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
status = process_write(channel, events[i].fd);
|
|
216
|
+
/* We only care about ENOMEM, anything else is handled via connection
|
|
217
|
+
* retries, etc */
|
|
218
|
+
if (status == ARES_ENOMEM) {
|
|
219
|
+
goto done;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Process read events */
|
|
224
|
+
for (i = 0; i < nevents; i++) {
|
|
225
|
+
if (events[i].fd == ARES_SOCKET_BAD ||
|
|
226
|
+
!(events[i].events & ARES_FD_EVENT_READ)) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
status = process_read(channel, events[i].fd, &now);
|
|
230
|
+
if (status == ARES_ENOMEM) {
|
|
231
|
+
goto done;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!(flags & ARES_PROCESS_FLAG_SKIP_NON_FD)) {
|
|
236
|
+
status = process_timeouts(channel, &now);
|
|
237
|
+
if (status == ARES_ENOMEM) {
|
|
238
|
+
goto done;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* Cleanup should be done after processing timeouts as it may invalidate
|
|
242
|
+
* connections */
|
|
243
|
+
ares_check_cleanup_conns(channel);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
done:
|
|
247
|
+
if (status == ARES_ENOMEM) {
|
|
248
|
+
return ARES_ENOMEM;
|
|
249
|
+
}
|
|
250
|
+
return ARES_SUCCESS;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
ares_status_t ares_process_fds(ares_channel_t *channel,
|
|
254
|
+
const ares_fd_events_t *events, size_t nevents,
|
|
255
|
+
unsigned int flags)
|
|
256
|
+
{
|
|
257
|
+
ares_status_t status;
|
|
258
|
+
|
|
259
|
+
if (channel == NULL) {
|
|
260
|
+
return ARES_EFORMERR;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
ares_channel_lock(channel);
|
|
264
|
+
status = ares_process_fds_nolock(channel, events, nevents, flags);
|
|
265
|
+
ares_channel_unlock(channel);
|
|
266
|
+
return status;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
void ares_process_fd(ares_channel_t *channel, ares_socket_t read_fd,
|
|
270
|
+
ares_socket_t write_fd)
|
|
271
|
+
{
|
|
272
|
+
ares_fd_events_t events[2];
|
|
273
|
+
size_t nevents = 0;
|
|
274
|
+
|
|
275
|
+
memset(events, 0, sizeof(events));
|
|
276
|
+
|
|
277
|
+
if (read_fd != ARES_SOCKET_BAD) {
|
|
278
|
+
nevents++;
|
|
279
|
+
events[nevents - 1].fd = read_fd;
|
|
280
|
+
events[nevents - 1].events |= ARES_FD_EVENT_READ;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (write_fd != ARES_SOCKET_BAD) {
|
|
284
|
+
if (write_fd != read_fd) {
|
|
285
|
+
nevents++;
|
|
286
|
+
}
|
|
287
|
+
events[nevents - 1].fd = write_fd;
|
|
288
|
+
events[nevents - 1].events |= ARES_FD_EVENT_WRITE;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
ares_process_fds(channel, events, nevents, ARES_PROCESS_FLAG_NONE);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
static ares_socket_t *channel_socket_list(const ares_channel_t *channel,
|
|
295
|
+
size_t *num)
|
|
296
|
+
{
|
|
297
|
+
ares_slist_node_t *snode;
|
|
298
|
+
ares_array_t *arr = ares_array_create(sizeof(ares_socket_t), NULL);
|
|
299
|
+
|
|
300
|
+
*num = 0;
|
|
301
|
+
|
|
302
|
+
if (arr == NULL) {
|
|
303
|
+
return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for (snode = ares_slist_node_first(channel->servers); snode != NULL;
|
|
307
|
+
snode = ares_slist_node_next(snode)) {
|
|
308
|
+
ares_server_t *server = ares_slist_node_val(snode);
|
|
309
|
+
ares_llist_node_t *node;
|
|
310
|
+
|
|
311
|
+
for (node = ares_llist_node_first(server->connections); node != NULL;
|
|
312
|
+
node = ares_llist_node_next(node)) {
|
|
313
|
+
const ares_conn_t *conn = ares_llist_node_val(node);
|
|
314
|
+
ares_socket_t *sptr;
|
|
315
|
+
ares_status_t status;
|
|
316
|
+
|
|
317
|
+
if (conn->fd == ARES_SOCKET_BAD) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
status = ares_array_insert_last((void **)&sptr, arr);
|
|
322
|
+
if (status != ARES_SUCCESS) {
|
|
323
|
+
ares_array_destroy(arr); /* LCOV_EXCL_LINE: OutOfMemory */
|
|
324
|
+
return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
|
|
325
|
+
}
|
|
326
|
+
*sptr = conn->fd;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return ares_array_finish(arr, num);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* Something interesting happened on the wire, or there was a timeout.
|
|
334
|
+
* See what's up and respond accordingly.
|
|
335
|
+
*/
|
|
336
|
+
void ares_process(ares_channel_t *channel, fd_set *read_fds, fd_set *write_fds)
|
|
337
|
+
{
|
|
338
|
+
size_t i;
|
|
339
|
+
size_t num_sockets;
|
|
340
|
+
ares_socket_t *socketlist;
|
|
341
|
+
ares_fd_events_t *events = NULL;
|
|
342
|
+
size_t nevents = 0;
|
|
343
|
+
|
|
344
|
+
if (channel == NULL) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
ares_channel_lock(channel);
|
|
349
|
+
|
|
350
|
+
/* There is no good way to iterate across an fd_set, instead we must pull a
|
|
351
|
+
* list of all known fds, and iterate across that checking against the fd_set.
|
|
352
|
+
*/
|
|
353
|
+
socketlist = channel_socket_list(channel, &num_sockets);
|
|
354
|
+
|
|
355
|
+
/* Lets create an events array, maximum number is the number of sockets in
|
|
356
|
+
* the list, so we'll use that and just track entries with nevents */
|
|
357
|
+
if (num_sockets) {
|
|
358
|
+
events = ares_malloc_zero(sizeof(*events) * num_sockets);
|
|
359
|
+
if (events == NULL) {
|
|
360
|
+
goto done;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
for (i = 0; i < num_sockets; i++) {
|
|
365
|
+
ares_bool_t had_read = ARES_FALSE;
|
|
366
|
+
if (read_fds && FD_ISSET(socketlist[i], read_fds)) {
|
|
367
|
+
nevents++;
|
|
368
|
+
events[nevents - 1].fd = socketlist[i];
|
|
369
|
+
events[nevents - 1].events |= ARES_FD_EVENT_READ;
|
|
370
|
+
had_read = ARES_TRUE;
|
|
371
|
+
}
|
|
372
|
+
if (write_fds && FD_ISSET(socketlist[i], write_fds)) {
|
|
373
|
+
if (!had_read) {
|
|
374
|
+
nevents++;
|
|
375
|
+
}
|
|
376
|
+
events[nevents - 1].fd = socketlist[i];
|
|
377
|
+
events[nevents - 1].events |= ARES_FD_EVENT_WRITE;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
done:
|
|
382
|
+
ares_process_fds_nolock(channel, events, nevents, ARES_PROCESS_FLAG_NONE);
|
|
383
|
+
ares_free(events);
|
|
384
|
+
ares_free(socketlist);
|
|
385
|
+
ares_channel_unlock(channel);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
static ares_status_t process_write(ares_channel_t *channel,
|
|
389
|
+
ares_socket_t write_fd)
|
|
390
|
+
{
|
|
391
|
+
ares_conn_t *conn = ares_conn_from_fd(channel, write_fd);
|
|
392
|
+
ares_status_t status;
|
|
393
|
+
|
|
394
|
+
if (conn == NULL) {
|
|
395
|
+
return ARES_SUCCESS;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* Mark as connected if we got here and TFO Initial not set */
|
|
399
|
+
if (!(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) {
|
|
400
|
+
conn->state_flags |= ARES_CONN_STATE_CONNECTED;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
status = ares_conn_flush(conn);
|
|
404
|
+
if (status != ARES_SUCCESS) {
|
|
405
|
+
handle_conn_error(conn, ARES_TRUE, status);
|
|
406
|
+
}
|
|
407
|
+
return status;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
void ares_process_pending_write(ares_channel_t *channel)
|
|
411
|
+
{
|
|
412
|
+
ares_slist_node_t *node;
|
|
413
|
+
|
|
414
|
+
if (channel == NULL) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
ares_channel_lock(channel);
|
|
419
|
+
if (!channel->notify_pending_write) {
|
|
420
|
+
ares_channel_unlock(channel);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/* Set as untriggerd before calling into ares_conn_flush(), this is
|
|
425
|
+
* because its possible ares_conn_flush() might cause additional data to
|
|
426
|
+
* be enqueued if there is some form of exception so it will need to recurse.
|
|
427
|
+
*/
|
|
428
|
+
channel->notify_pending_write = ARES_FALSE;
|
|
429
|
+
|
|
430
|
+
for (node = ares_slist_node_first(channel->servers); node != NULL;
|
|
431
|
+
node = ares_slist_node_next(node)) {
|
|
432
|
+
ares_server_t *server = ares_slist_node_val(node);
|
|
433
|
+
ares_conn_t *conn = server->tcp_conn;
|
|
434
|
+
ares_status_t status;
|
|
435
|
+
|
|
436
|
+
if (conn == NULL) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* Enqueue any pending data if there is any */
|
|
441
|
+
status = ares_conn_flush(conn);
|
|
442
|
+
if (status != ARES_SUCCESS) {
|
|
443
|
+
handle_conn_error(conn, ARES_TRUE, status);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
ares_channel_unlock(channel);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
static ares_status_t read_conn_packets(ares_conn_t *conn,
|
|
451
|
+
ares_bool_t *conn_error)
|
|
452
|
+
{
|
|
453
|
+
ares_bool_t read_again;
|
|
454
|
+
ares_conn_err_t err;
|
|
455
|
+
const ares_channel_t *channel = conn->server->channel;
|
|
456
|
+
|
|
457
|
+
if (conn_error == NULL) {
|
|
458
|
+
return ARES_EFORMERR;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
*conn_error = ARES_FALSE;
|
|
462
|
+
|
|
463
|
+
do {
|
|
464
|
+
size_t count;
|
|
465
|
+
size_t len = 65535;
|
|
466
|
+
unsigned char *ptr;
|
|
467
|
+
size_t start_len = ares_buf_len(conn->in_buf);
|
|
468
|
+
|
|
469
|
+
/* If UDP, lets write out a placeholder for the length indicator */
|
|
470
|
+
if (!(conn->flags & ARES_CONN_FLAG_TCP) &&
|
|
471
|
+
ares_buf_append_be16(conn->in_buf, 0) != ARES_SUCCESS) {
|
|
472
|
+
handle_conn_error(conn, ARES_FALSE /* not critical to connection */,
|
|
473
|
+
ARES_SUCCESS);
|
|
474
|
+
return ARES_ENOMEM;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Get a buffer of sufficient size */
|
|
478
|
+
ptr = ares_buf_append_start(conn->in_buf, &len);
|
|
479
|
+
|
|
480
|
+
if (ptr == NULL) {
|
|
481
|
+
handle_conn_error(conn, ARES_FALSE /* not critical to connection */,
|
|
482
|
+
ARES_SUCCESS);
|
|
483
|
+
return ARES_ENOMEM;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Read from socket */
|
|
487
|
+
err = ares_conn_read(conn, ptr, len, &count);
|
|
488
|
+
|
|
489
|
+
if (err != ARES_CONN_ERR_SUCCESS) {
|
|
490
|
+
ares_buf_append_finish(conn->in_buf, 0);
|
|
491
|
+
if (!(conn->flags & ARES_CONN_FLAG_TCP)) {
|
|
492
|
+
ares_buf_set_length(conn->in_buf, start_len);
|
|
493
|
+
}
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/* Record amount of data read */
|
|
498
|
+
ares_buf_append_finish(conn->in_buf, count);
|
|
499
|
+
|
|
500
|
+
/* Only loop if sockets support non-blocking operation, and are using UDP
|
|
501
|
+
* or are using TCP and read the maximum buffer size */
|
|
502
|
+
read_again = ARES_FALSE;
|
|
503
|
+
if (channel->sock_funcs.flags & ARES_SOCKFUNC_FLAG_NONBLOCKING &&
|
|
504
|
+
(!(conn->flags & ARES_CONN_FLAG_TCP) || count == len)) {
|
|
505
|
+
read_again = ARES_TRUE;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* If UDP, overwrite length */
|
|
509
|
+
if (!(conn->flags & ARES_CONN_FLAG_TCP)) {
|
|
510
|
+
/* The read buffer is grown in powers of two, so a single recvfrom() can
|
|
511
|
+
* return more than the 2-byte length prefix is able to represent. A
|
|
512
|
+
* datagram that large can't be a valid DNS message, and writing the
|
|
513
|
+
* truncated (unsigned short)count would desync the framing of everything
|
|
514
|
+
* buffered after it, so discard it. Standard UDP can't actually deliver
|
|
515
|
+
* a payload this large (max is 65507 IPv4 / 65527 IPv6); the only vector
|
|
516
|
+
* is IPv6 jumbograms (RFC 2675), which DNS never uses. This is purely
|
|
517
|
+
* defense-in-depth. */
|
|
518
|
+
if (count > 65535) {
|
|
519
|
+
ares_buf_set_length(conn->in_buf, start_len);
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
len = ares_buf_len(conn->in_buf);
|
|
523
|
+
ares_buf_set_length(conn->in_buf, start_len);
|
|
524
|
+
ares_buf_append_be16(conn->in_buf, (unsigned short)count);
|
|
525
|
+
ares_buf_set_length(conn->in_buf, len);
|
|
526
|
+
}
|
|
527
|
+
/* Try to read again only if *we* set up the socket, otherwise it may be
|
|
528
|
+
* a blocking socket and would cause recvfrom to hang. */
|
|
529
|
+
} while (read_again);
|
|
530
|
+
|
|
531
|
+
if (err != ARES_CONN_ERR_SUCCESS && err != ARES_CONN_ERR_WOULDBLOCK) {
|
|
532
|
+
/* If there is no packet data buffered, preserve the historical
|
|
533
|
+
* immediate connection-failure behavior so retries happen promptly.
|
|
534
|
+
* Only defer if there is buffered data to parse first. */
|
|
535
|
+
if (ares_buf_len(conn->in_buf) == 0) {
|
|
536
|
+
handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED);
|
|
537
|
+
return ARES_ECONNREFUSED;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
*conn_error = ARES_TRUE;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return ARES_SUCCESS;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
typedef enum {
|
|
547
|
+
REQUEUE_REQUEUE = 1,
|
|
548
|
+
REQUEUE_ENDQUERY = 2
|
|
549
|
+
} requeue_type_t;
|
|
550
|
+
|
|
551
|
+
/* Simple data structure to store a query that needs to be requeued with
|
|
552
|
+
* optional server */
|
|
553
|
+
typedef struct {
|
|
554
|
+
requeue_type_t type; /* type of entry, requeue or endquery */
|
|
555
|
+
unsigned short qid; /* query id */
|
|
556
|
+
ares_server_t *server; /* requeue only: optional */
|
|
557
|
+
ares_status_t status; /* endquery only */
|
|
558
|
+
ares_dns_record_t *dnsrec; /* endquery only: optional */
|
|
559
|
+
} ares_requeue_t;
|
|
560
|
+
|
|
561
|
+
static ares_status_t ares_append_requeue_int(ares_array_t **requeue,
|
|
562
|
+
requeue_type_t type,
|
|
563
|
+
ares_query_t *query,
|
|
564
|
+
ares_server_t *server,
|
|
565
|
+
ares_status_t status,
|
|
566
|
+
ares_dns_record_t *dnsrec)
|
|
567
|
+
{
|
|
568
|
+
ares_requeue_t entry;
|
|
569
|
+
|
|
570
|
+
if (*requeue == NULL) {
|
|
571
|
+
*requeue = ares_array_create(sizeof(ares_requeue_t), NULL);
|
|
572
|
+
if (*requeue == NULL) {
|
|
573
|
+
return ARES_ENOMEM;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
ares_query_remove_from_conn(query);
|
|
578
|
+
|
|
579
|
+
entry.type = type;
|
|
580
|
+
entry.qid = query->qid;
|
|
581
|
+
entry.server = server;
|
|
582
|
+
entry.status = status;
|
|
583
|
+
entry.dnsrec = dnsrec;
|
|
584
|
+
return ares_array_insertdata_last(*requeue, &entry);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
static ares_status_t ares_append_requeue(ares_array_t **requeue,
|
|
588
|
+
ares_query_t *query,
|
|
589
|
+
ares_server_t *server)
|
|
590
|
+
{
|
|
591
|
+
return ares_append_requeue_int(requeue, REQUEUE_REQUEUE, query, server, 0,
|
|
592
|
+
NULL);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
static ares_status_t ares_append_endqueue(ares_array_t **requeue,
|
|
596
|
+
ares_query_t *query,
|
|
597
|
+
ares_status_t status,
|
|
598
|
+
ares_dns_record_t *dnsrec)
|
|
599
|
+
{
|
|
600
|
+
return ares_append_requeue_int(requeue, REQUEUE_ENDQUERY, query, NULL, status,
|
|
601
|
+
dnsrec);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/* Drain the deferred requeue/endqueue list iteratively. All flush sites
|
|
605
|
+
* (read_answers(), process_timeouts(), and ares_send_query()) funnel through
|
|
606
|
+
* here so that:
|
|
607
|
+
* 1. Retries are re-dispatched by appending to this same list and looping,
|
|
608
|
+
* rather than recursing ares_requeue_query() -> ares_send_query() until
|
|
609
|
+
* the stack is exhausted (issue #1043).
|
|
610
|
+
* 2. A query is fully detached from all lookup lists before its callback is
|
|
611
|
+
* invoked, so a reentrant ares_cancel() from within that callback cannot
|
|
612
|
+
* find and free the same query, which would otherwise double-free it
|
|
613
|
+
* (CVE-2026-33630 / GHSA-6wfj-rwm7-3542).
|
|
614
|
+
*
|
|
615
|
+
* On return the list has been fully processed, an empty-queue notification has
|
|
616
|
+
* been sent if appropriate, and *requeue has been destroyed and set to NULL. */
|
|
617
|
+
static ares_status_t ares_flush_requeue(ares_channel_t *channel,
|
|
618
|
+
const ares_timeval_t *now,
|
|
619
|
+
ares_array_t **requeue)
|
|
620
|
+
{
|
|
621
|
+
ares_status_t status = ARES_SUCCESS;
|
|
622
|
+
|
|
623
|
+
if (requeue == NULL) {
|
|
624
|
+
return status;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
while (*requeue != NULL && ares_array_len(*requeue) > 0) {
|
|
628
|
+
ares_query_t *query;
|
|
629
|
+
ares_requeue_t entry;
|
|
630
|
+
ares_status_t internal_status;
|
|
631
|
+
|
|
632
|
+
internal_status = ares_array_claim_at(&entry, sizeof(entry), *requeue, 0);
|
|
633
|
+
if (internal_status != ARES_SUCCESS) {
|
|
634
|
+
break; /* LCOV_EXCL_LINE: DefensiveCoding */
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
query = ares_htable_szvp_get_direct(channel->queries_by_qid, entry.qid);
|
|
638
|
+
|
|
639
|
+
if (entry.type == REQUEUE_REQUEUE) {
|
|
640
|
+
/* Query disappeared (e.g. a prior callback in this drain cancelled it) */
|
|
641
|
+
if (query == NULL) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
/* Re-dispatch via the internal entrypoint so any further requeues are
|
|
645
|
+
* appended back onto this same list and drained by the loop above,
|
|
646
|
+
* rather than recursing. */
|
|
647
|
+
internal_status = ares_send_query_int(entry.server, query, now, requeue);
|
|
648
|
+
/* We only care about ARES_ENOMEM */
|
|
649
|
+
if (internal_status == ARES_ENOMEM) {
|
|
650
|
+
status = ARES_ENOMEM;
|
|
651
|
+
}
|
|
652
|
+
} else { /* REQUEUE_ENDQUERY */
|
|
653
|
+
if (query != NULL) {
|
|
654
|
+
/* Detach the query from all lookup lists BEFORE invoking the callback.
|
|
655
|
+
* Otherwise a reentrant ares_cancel() from within the callback would
|
|
656
|
+
* find this query still linked in all_queries/queries_by_qid, free it,
|
|
657
|
+
* and the ares_free_query() below would then double-free it. */
|
|
658
|
+
ares_detach_query(query);
|
|
659
|
+
query->callback(query->arg, entry.status, query->timeouts,
|
|
660
|
+
entry.dnsrec);
|
|
661
|
+
ares_free_query(query);
|
|
662
|
+
}
|
|
663
|
+
ares_dns_record_destroy(entry.dnsrec);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/* Don't forget to send notification if queue emptied */
|
|
668
|
+
if (*requeue != NULL) {
|
|
669
|
+
ares_queue_notify_empty(channel);
|
|
670
|
+
}
|
|
671
|
+
ares_array_destroy(*requeue);
|
|
672
|
+
*requeue = NULL;
|
|
673
|
+
|
|
674
|
+
return status;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
static ares_status_t read_answers(ares_conn_t *conn, const ares_timeval_t *now)
|
|
678
|
+
{
|
|
679
|
+
ares_status_t status;
|
|
680
|
+
ares_channel_t *channel = conn->server->channel;
|
|
681
|
+
ares_array_t *requeue = NULL;
|
|
682
|
+
|
|
683
|
+
/* Process all queued answers */
|
|
684
|
+
while (1) {
|
|
685
|
+
unsigned short dns_len = 0;
|
|
686
|
+
const unsigned char *data = NULL;
|
|
687
|
+
size_t data_len = 0;
|
|
688
|
+
|
|
689
|
+
/* Tag so we can roll back */
|
|
690
|
+
ares_buf_tag(conn->in_buf);
|
|
691
|
+
|
|
692
|
+
/* Read length indicator */
|
|
693
|
+
status = ares_buf_fetch_be16(conn->in_buf, &dns_len);
|
|
694
|
+
if (status != ARES_SUCCESS) {
|
|
695
|
+
ares_buf_tag_rollback(conn->in_buf);
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/* Not enough data for a full response yet */
|
|
700
|
+
status = ares_buf_consume(conn->in_buf, dns_len);
|
|
701
|
+
if (status != ARES_SUCCESS) {
|
|
702
|
+
ares_buf_tag_rollback(conn->in_buf);
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/* Can't fail except for misuse */
|
|
707
|
+
data = ares_buf_tag_fetch(conn->in_buf, &data_len);
|
|
708
|
+
if (data == NULL || data_len < 2) {
|
|
709
|
+
ares_buf_tag_clear(conn->in_buf);
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/* Strip off 2 bytes length */
|
|
714
|
+
data += 2;
|
|
715
|
+
data_len -= 2;
|
|
716
|
+
|
|
717
|
+
/* We finished reading this answer; process it */
|
|
718
|
+
status = process_answer(channel, data, data_len, conn, now, &requeue);
|
|
719
|
+
if (status != ARES_SUCCESS) {
|
|
720
|
+
handle_conn_error(conn, ARES_TRUE, status);
|
|
721
|
+
goto cleanup;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/* Since we processed the answer, clear the tag so space can be reclaimed */
|
|
725
|
+
ares_buf_tag_clear(conn->in_buf);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
cleanup:
|
|
729
|
+
/* Flush requeue - re-dispatch retries and invoke deferred callbacks
|
|
730
|
+
* iteratively and safely */
|
|
731
|
+
if (ares_flush_requeue(channel, now, &requeue) == ARES_ENOMEM) {
|
|
732
|
+
status = ARES_ENOMEM;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return status;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
static ares_status_t process_read(ares_channel_t *channel,
|
|
739
|
+
ares_socket_t read_fd,
|
|
740
|
+
const ares_timeval_t *now)
|
|
741
|
+
{
|
|
742
|
+
ares_conn_t *conn = ares_conn_from_fd(channel, read_fd);
|
|
743
|
+
ares_bool_t conn_error;
|
|
744
|
+
ares_status_t status;
|
|
745
|
+
|
|
746
|
+
if (conn == NULL) {
|
|
747
|
+
return ARES_SUCCESS;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
status = read_conn_packets(conn, &conn_error);
|
|
751
|
+
|
|
752
|
+
if (status != ARES_SUCCESS) {
|
|
753
|
+
return status;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
status = read_answers(conn, now);
|
|
757
|
+
if (status != ARES_SUCCESS) {
|
|
758
|
+
return status;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (conn_error) {
|
|
762
|
+
conn = ares_conn_from_fd(channel, read_fd);
|
|
763
|
+
if (conn != NULL) {
|
|
764
|
+
handle_conn_error(conn, ARES_TRUE, ARES_ECONNREFUSED);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return ARES_SUCCESS;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/* If any queries have timed out, note the timeout and move them on. */
|
|
772
|
+
static ares_status_t process_timeouts(ares_channel_t *channel,
|
|
773
|
+
const ares_timeval_t *now)
|
|
774
|
+
{
|
|
775
|
+
ares_slist_node_t *node;
|
|
776
|
+
ares_status_t status = ARES_SUCCESS;
|
|
777
|
+
ares_array_t *requeue = NULL;
|
|
778
|
+
|
|
779
|
+
/* Just keep popping off the first as this list will re-sort as things come
|
|
780
|
+
* and go. We don't want to try to rely on 'next' as some operation might
|
|
781
|
+
* cause a cleanup of that pointer and would become invalid */
|
|
782
|
+
while ((node = ares_slist_node_first(channel->queries_by_timeout)) != NULL) {
|
|
783
|
+
ares_query_t *query = ares_slist_node_val(node);
|
|
784
|
+
ares_conn_t *conn;
|
|
785
|
+
|
|
786
|
+
/* Since this is sorted, as soon as we hit a query that isn't timed out,
|
|
787
|
+
* break */
|
|
788
|
+
if (!ares_timedout(now, &query->timeout)) {
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
query->timeouts++;
|
|
793
|
+
|
|
794
|
+
conn = query->conn;
|
|
795
|
+
/* Retire this connection for NEW queries. A timeout suggests packets are
|
|
796
|
+
* being dropped on it, so route new queries to a fresh source port while
|
|
797
|
+
* the in-flight queries here drain (it is cleaned up once idle). This is
|
|
798
|
+
* per-connection so a transient failure doesn't stop reuse of healthy
|
|
799
|
+
* connections to the same server. */
|
|
800
|
+
conn->flags |= ARES_CONN_FLAG_NONEW;
|
|
801
|
+
server_increment_failures(conn->server, query->using_tcp);
|
|
802
|
+
status =
|
|
803
|
+
ares_requeue_query(query, now, ARES_ETIMEOUT, ARES_TRUE, NULL, &requeue);
|
|
804
|
+
if (status == ARES_ENOMEM) {
|
|
805
|
+
goto done;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
done:
|
|
809
|
+
/* Flush requeue - re-dispatch retries and invoke deferred callbacks
|
|
810
|
+
* iteratively and safely */
|
|
811
|
+
if (ares_flush_requeue(channel, now, &requeue) == ARES_ENOMEM) {
|
|
812
|
+
status = ARES_ENOMEM;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (status == ARES_ENOMEM) {
|
|
816
|
+
return ARES_ENOMEM;
|
|
817
|
+
}
|
|
818
|
+
return ARES_SUCCESS;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
static ares_status_t rewrite_without_edns(ares_query_t *query)
|
|
822
|
+
{
|
|
823
|
+
ares_status_t status = ARES_SUCCESS;
|
|
824
|
+
size_t i;
|
|
825
|
+
ares_bool_t found_opt_rr = ARES_FALSE;
|
|
826
|
+
|
|
827
|
+
/* Find and remove the OPT RR record */
|
|
828
|
+
for (i = 0; i < ares_dns_record_rr_cnt(query->query, ARES_SECTION_ADDITIONAL);
|
|
829
|
+
i++) {
|
|
830
|
+
const ares_dns_rr_t *rr;
|
|
831
|
+
rr = ares_dns_record_rr_get(query->query, ARES_SECTION_ADDITIONAL, i);
|
|
832
|
+
if (ares_dns_rr_get_type(rr) == ARES_REC_TYPE_OPT) {
|
|
833
|
+
ares_dns_record_rr_del(query->query, ARES_SECTION_ADDITIONAL, i);
|
|
834
|
+
found_opt_rr = ARES_TRUE;
|
|
835
|
+
break;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (!found_opt_rr) {
|
|
840
|
+
status = ARES_EFORMERR;
|
|
841
|
+
goto done;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
done:
|
|
845
|
+
return status;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
static ares_bool_t issue_might_be_edns(const ares_dns_record_t *req,
|
|
849
|
+
const ares_dns_record_t *rsp)
|
|
850
|
+
{
|
|
851
|
+
const ares_dns_rr_t *rr;
|
|
852
|
+
|
|
853
|
+
/* If we use EDNS and server answers with FORMERR without an OPT RR, the
|
|
854
|
+
* protocol extension is not understood by the responder. We must retry the
|
|
855
|
+
* query without EDNS enabled. */
|
|
856
|
+
if (ares_dns_record_get_rcode(rsp) != ARES_RCODE_FORMERR) {
|
|
857
|
+
return ARES_FALSE;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
rr = ares_dns_get_opt_rr_const(req);
|
|
861
|
+
if (rr == NULL) {
|
|
862
|
+
/* We didn't send EDNS */
|
|
863
|
+
return ARES_FALSE;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
if (ares_dns_get_opt_rr_const(rsp) == NULL) {
|
|
867
|
+
/* Spec says EDNS won't be echo'd back on non-supporting servers, so
|
|
868
|
+
* retry without EDNS */
|
|
869
|
+
return ARES_TRUE;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/* As per issue #911 some non-compliant servers that do indeed support EDNS
|
|
873
|
+
* but don't support unrecognized option codes exist. At this point we
|
|
874
|
+
* expect them to have also returned an EDNS opt record, but we may remove
|
|
875
|
+
* that check in the future. Lets detect this situation if we're sending
|
|
876
|
+
* option codes */
|
|
877
|
+
if (ares_dns_rr_get_opt_cnt(rr, ARES_RR_OPT_OPTIONS) == 0) {
|
|
878
|
+
/* We didn't send any option codes */
|
|
879
|
+
return ARES_FALSE;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (ares_dns_get_opt_rr_const(rsp) != NULL) {
|
|
883
|
+
/* At this time we're requiring the server to respond with EDNS opt
|
|
884
|
+
* records since that's what has been observed in the field. We might
|
|
885
|
+
* find in the future we have to remove this, who knows. Lets go
|
|
886
|
+
* ahead and force a retry without EDNS*/
|
|
887
|
+
return ARES_TRUE;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
return ARES_FALSE;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/* Handle an answer from a server. This must NEVER cleanup the
|
|
894
|
+
* server connection! Return something other than ARES_SUCCESS to cause
|
|
895
|
+
* the connection to be terminated after this call. */
|
|
896
|
+
static ares_status_t process_answer(ares_channel_t *channel,
|
|
897
|
+
const unsigned char *abuf, size_t alen,
|
|
898
|
+
ares_conn_t *conn,
|
|
899
|
+
const ares_timeval_t *now,
|
|
900
|
+
ares_array_t **requeue)
|
|
901
|
+
{
|
|
902
|
+
ares_query_t *query;
|
|
903
|
+
/* Cache these as once ares_send_query() gets called, it may end up
|
|
904
|
+
* invalidating the connection all-together */
|
|
905
|
+
ares_server_t *server = conn->server;
|
|
906
|
+
ares_dns_record_t *rdnsrec = NULL;
|
|
907
|
+
ares_status_t status;
|
|
908
|
+
ares_bool_t is_cached = ARES_FALSE;
|
|
909
|
+
|
|
910
|
+
/* UDP can have 0-byte messages, drop them to the ground */
|
|
911
|
+
if (alen == 0) {
|
|
912
|
+
return ARES_SUCCESS;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/* Parse the response */
|
|
916
|
+
status = ares_dns_parse(abuf, alen, 0, &rdnsrec);
|
|
917
|
+
if (status != ARES_SUCCESS) {
|
|
918
|
+
/* Malformations are never accepted */
|
|
919
|
+
status = ARES_EBADRESP;
|
|
920
|
+
goto cleanup;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/* Find the query corresponding to this packet. The queries are
|
|
924
|
+
* hashed/bucketed by query id, so this lookup should be quick.
|
|
925
|
+
*/
|
|
926
|
+
query = ares_htable_szvp_get_direct(channel->queries_by_qid,
|
|
927
|
+
ares_dns_record_get_id(rdnsrec));
|
|
928
|
+
if (!query) {
|
|
929
|
+
/* We may have stopped listening for this query, that's ok */
|
|
930
|
+
status = ARES_SUCCESS;
|
|
931
|
+
goto cleanup;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
/* Both the query id and the questions must be the same. We will drop any
|
|
935
|
+
* replies that aren't for the same query as this is considered invalid. */
|
|
936
|
+
if (!same_questions(query, rdnsrec)) {
|
|
937
|
+
/* Possible qid conflict due to delayed response, that's ok */
|
|
938
|
+
status = ARES_SUCCESS;
|
|
939
|
+
goto cleanup;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/* Validate DNS cookie in response. This function may need to requeue the
|
|
943
|
+
* query. */
|
|
944
|
+
if (ares_cookie_validate(query, rdnsrec, conn, now, requeue)
|
|
945
|
+
!= ARES_SUCCESS) {
|
|
946
|
+
/* Drop response and return */
|
|
947
|
+
status = ARES_SUCCESS;
|
|
948
|
+
goto cleanup;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/* At this point we know we've received an answer for this query, so we should
|
|
952
|
+
* remove it from the connection's queue so we can possibly invalidate the
|
|
953
|
+
* connection. Delay cleaning up the connection though as we may enqueue
|
|
954
|
+
* something new. */
|
|
955
|
+
ares_llist_node_destroy(query->node_queries_to_conn);
|
|
956
|
+
query->node_queries_to_conn = NULL;
|
|
957
|
+
|
|
958
|
+
/* There are old servers that don't understand EDNS at all, then some servers
|
|
959
|
+
* that have non-compliant implementations. Lets try to detect this sort
|
|
960
|
+
* of thing. */
|
|
961
|
+
if (issue_might_be_edns(query->query, rdnsrec)) {
|
|
962
|
+
status = rewrite_without_edns(query);
|
|
963
|
+
if (status != ARES_SUCCESS) {
|
|
964
|
+
end_query(channel, server, query, status, NULL, requeue);
|
|
965
|
+
goto cleanup;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/* Requeue to same server */
|
|
969
|
+
status = ares_append_requeue(requeue, query, server);
|
|
970
|
+
goto cleanup;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/* If we got a truncated UDP packet and are not ignoring truncation,
|
|
974
|
+
* don't accept the packet, and switch the query to TCP if we hadn't
|
|
975
|
+
* done so already.
|
|
976
|
+
*/
|
|
977
|
+
if (ares_dns_record_get_flags(rdnsrec) & ARES_FLAG_TC &&
|
|
978
|
+
!(conn->flags & ARES_CONN_FLAG_TCP) &&
|
|
979
|
+
!(channel->flags & ARES_FLAG_IGNTC)) {
|
|
980
|
+
query->using_tcp = ARES_TRUE;
|
|
981
|
+
status = ares_append_requeue(requeue, query, NULL);
|
|
982
|
+
/* Status will reflect success except on memory error, which is good since
|
|
983
|
+
* requeuing to TCP is ok */
|
|
984
|
+
goto cleanup;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/* If we aren't passing through all error packets, discard packets
|
|
988
|
+
* with SERVFAIL, NOTIMP, or REFUSED response codes.
|
|
989
|
+
*/
|
|
990
|
+
if (!(channel->flags & ARES_FLAG_NOCHECKRESP)) {
|
|
991
|
+
ares_dns_rcode_t rcode = ares_dns_record_get_rcode(rdnsrec);
|
|
992
|
+
if (rcode == ARES_RCODE_SERVFAIL || rcode == ARES_RCODE_NOTIMP ||
|
|
993
|
+
rcode == ARES_RCODE_REFUSED) {
|
|
994
|
+
switch (rcode) {
|
|
995
|
+
case ARES_RCODE_SERVFAIL:
|
|
996
|
+
status = ARES_ESERVFAIL;
|
|
997
|
+
break;
|
|
998
|
+
case ARES_RCODE_NOTIMP:
|
|
999
|
+
status = ARES_ENOTIMP;
|
|
1000
|
+
break;
|
|
1001
|
+
case ARES_RCODE_REFUSED:
|
|
1002
|
+
status = ARES_EREFUSED;
|
|
1003
|
+
break;
|
|
1004
|
+
default:
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
server_increment_failures(server, query->using_tcp);
|
|
1009
|
+
status = ares_requeue_query(query, now, status, ARES_TRUE, rdnsrec,
|
|
1010
|
+
requeue);
|
|
1011
|
+
rdnsrec = NULL; /* Free'd by ares_requeue_query() */
|
|
1012
|
+
|
|
1013
|
+
if (status != ARES_ENOMEM) {
|
|
1014
|
+
/* Should any of these cause a connection termination?
|
|
1015
|
+
* Maybe SERVER_FAILURE? */
|
|
1016
|
+
status = ARES_SUCCESS;
|
|
1017
|
+
}
|
|
1018
|
+
goto cleanup;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/* If cache insertion was successful, it took ownership. We ignore
|
|
1023
|
+
* other cache insertion failures. */
|
|
1024
|
+
ares_qcache_insert(channel, now, query, rdnsrec);
|
|
1025
|
+
|
|
1026
|
+
server_set_good(server, query->using_tcp);
|
|
1027
|
+
end_query(channel, server, query, ARES_SUCCESS, rdnsrec, requeue);
|
|
1028
|
+
rdnsrec = NULL; /* Free'd by the requeue */
|
|
1029
|
+
|
|
1030
|
+
status = ARES_SUCCESS;
|
|
1031
|
+
|
|
1032
|
+
cleanup:
|
|
1033
|
+
/* Don't cleanup the cached pointer to the dns response */
|
|
1034
|
+
if (!is_cached) {
|
|
1035
|
+
ares_dns_record_destroy(rdnsrec);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
return status;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
static void handle_conn_error(ares_conn_t *conn, ares_bool_t critical_failure,
|
|
1042
|
+
ares_status_t failure_status)
|
|
1043
|
+
{
|
|
1044
|
+
ares_server_t *server = conn->server;
|
|
1045
|
+
|
|
1046
|
+
/* Increment failures first before requeue so it is unlikely to requeue
|
|
1047
|
+
* to the same server */
|
|
1048
|
+
if (critical_failure) {
|
|
1049
|
+
server_increment_failures(
|
|
1050
|
+
server, (conn->flags & ARES_CONN_FLAG_TCP) ? ARES_TRUE : ARES_FALSE);
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/* This will requeue any connections automatically */
|
|
1054
|
+
ares_close_connection(conn, failure_status);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/* Requeue query will normally call ares_send_query() but in some circumstances
|
|
1058
|
+
* this needs to be delayed, so if requeue is not NULL, it will add the query
|
|
1059
|
+
* to the queue instead */
|
|
1060
|
+
ares_status_t ares_requeue_query(ares_query_t *query, const ares_timeval_t *now,
|
|
1061
|
+
ares_status_t status,
|
|
1062
|
+
ares_bool_t inc_try_count,
|
|
1063
|
+
ares_dns_record_t *dnsrec,
|
|
1064
|
+
ares_array_t **requeue)
|
|
1065
|
+
{
|
|
1066
|
+
ares_channel_t *channel = query->channel;
|
|
1067
|
+
size_t max_tries = ares_slist_len(channel->servers) * channel->tries;
|
|
1068
|
+
ares_server_t *server = NULL;
|
|
1069
|
+
|
|
1070
|
+
if (query->conn != NULL) {
|
|
1071
|
+
server = query->conn->server;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
ares_query_remove_from_conn(query);
|
|
1075
|
+
|
|
1076
|
+
if (status != ARES_SUCCESS) {
|
|
1077
|
+
query->error_status = status;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (inc_try_count) {
|
|
1081
|
+
query->try_count++;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
if (query->try_count < max_tries && !query->no_retries) {
|
|
1085
|
+
ares_dns_record_destroy(dnsrec);
|
|
1086
|
+
if (requeue != NULL) {
|
|
1087
|
+
return ares_append_requeue(requeue, query, NULL);
|
|
1088
|
+
}
|
|
1089
|
+
return ares_send_query(NULL, query, now);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/* If we are here, all attempts to perform query failed. */
|
|
1093
|
+
if (query->error_status == ARES_SUCCESS) {
|
|
1094
|
+
query->error_status = ARES_ETIMEOUT;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
end_query(channel, server, query, query->error_status, dnsrec, requeue);
|
|
1098
|
+
return ARES_ETIMEOUT;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/*! Count the number of servers that share the same highest priority (lowest
|
|
1102
|
+
* consecutive failures). Since they are sorted in priority order, we just
|
|
1103
|
+
* stop when the consecutive failure count changes. Used for random selection
|
|
1104
|
+
* of good servers. */
|
|
1105
|
+
static size_t count_highest_prio_servers(const ares_channel_t *channel)
|
|
1106
|
+
{
|
|
1107
|
+
ares_slist_node_t *node;
|
|
1108
|
+
size_t cnt = 0;
|
|
1109
|
+
size_t last_consec_failures = SIZE_MAX;
|
|
1110
|
+
|
|
1111
|
+
for (node = ares_slist_node_first(channel->servers); node != NULL;
|
|
1112
|
+
node = ares_slist_node_next(node)) {
|
|
1113
|
+
const ares_server_t *server = ares_slist_node_val(node);
|
|
1114
|
+
|
|
1115
|
+
if (last_consec_failures != SIZE_MAX &&
|
|
1116
|
+
last_consec_failures < server->consec_failures) {
|
|
1117
|
+
break;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
last_consec_failures = server->consec_failures;
|
|
1121
|
+
cnt++;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
return cnt;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/* Pick a random *best* server from the list, we first get a random number in
|
|
1128
|
+
* the range of the number of *best* servers, then scan until we find that
|
|
1129
|
+
* server in the list */
|
|
1130
|
+
static ares_server_t *ares_random_server(ares_channel_t *channel)
|
|
1131
|
+
{
|
|
1132
|
+
unsigned char c;
|
|
1133
|
+
size_t cnt;
|
|
1134
|
+
size_t idx;
|
|
1135
|
+
ares_slist_node_t *node;
|
|
1136
|
+
size_t num_servers = count_highest_prio_servers(channel);
|
|
1137
|
+
|
|
1138
|
+
/* Silence coverity, not possible */
|
|
1139
|
+
if (num_servers == 0) {
|
|
1140
|
+
return NULL;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
ares_rand_bytes(channel->rand_state, &c, 1);
|
|
1144
|
+
|
|
1145
|
+
cnt = c;
|
|
1146
|
+
idx = cnt % num_servers;
|
|
1147
|
+
|
|
1148
|
+
cnt = 0;
|
|
1149
|
+
for (node = ares_slist_node_first(channel->servers); node != NULL;
|
|
1150
|
+
node = ares_slist_node_next(node)) {
|
|
1151
|
+
if (cnt == idx) {
|
|
1152
|
+
return ares_slist_node_val(node);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
cnt++;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return NULL;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
static void server_probe_cb(void *arg, ares_status_t status, size_t timeouts,
|
|
1162
|
+
const ares_dns_record_t *dnsrec)
|
|
1163
|
+
{
|
|
1164
|
+
(void)arg;
|
|
1165
|
+
(void)status;
|
|
1166
|
+
(void)timeouts;
|
|
1167
|
+
(void)dnsrec;
|
|
1168
|
+
/* Nothing to do, the logic internally will handle success/fail of this */
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/* Determine if we should probe a downed server */
|
|
1172
|
+
static void ares_probe_failed_server(ares_channel_t *channel,
|
|
1173
|
+
const ares_server_t *server,
|
|
1174
|
+
const ares_query_t *query)
|
|
1175
|
+
{
|
|
1176
|
+
const ares_server_t *last_server = ares_slist_last_val(channel->servers);
|
|
1177
|
+
unsigned short r;
|
|
1178
|
+
ares_timeval_t now;
|
|
1179
|
+
ares_slist_node_t *node;
|
|
1180
|
+
ares_server_t *probe_server = NULL;
|
|
1181
|
+
|
|
1182
|
+
/* If no servers have failures, or we're not configured with a server retry
|
|
1183
|
+
* chance, then nothing to probe */
|
|
1184
|
+
if ((last_server != NULL && last_server->consec_failures == 0) ||
|
|
1185
|
+
channel->server_retry_chance == 0) {
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/* Generate a random value to decide whether to retry a failed server. The
|
|
1190
|
+
* probability to use is 1/channel->server_retry_chance, rounded up to a
|
|
1191
|
+
* precision of 1/2^B where B is the number of bits in the random value.
|
|
1192
|
+
* We use an unsigned short for the random value for increased precision.
|
|
1193
|
+
*/
|
|
1194
|
+
ares_rand_bytes(channel->rand_state, (unsigned char *)&r, sizeof(r));
|
|
1195
|
+
if (r % channel->server_retry_chance != 0) {
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/* Select the first server with failures to retry that has passed the retry
|
|
1200
|
+
* timeout and doesn't already have a pending probe. Skip the server the
|
|
1201
|
+
* triggering query was just sent to since that query is already exercising
|
|
1202
|
+
* it. */
|
|
1203
|
+
ares_tvnow(&now);
|
|
1204
|
+
for (node = ares_slist_node_first(channel->servers); node != NULL;
|
|
1205
|
+
node = ares_slist_node_next(node)) {
|
|
1206
|
+
ares_server_t *node_val = ares_slist_node_val(node);
|
|
1207
|
+
if (node_val != NULL && node_val != server &&
|
|
1208
|
+
node_val->consec_failures > 0 && !node_val->probe_pending &&
|
|
1209
|
+
ares_timedout(&now, &node_val->next_retry_time)) {
|
|
1210
|
+
probe_server = node_val;
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/* Nothing to probe */
|
|
1216
|
+
if (probe_server == NULL) {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/* Enqueue an identical query onto the specified server without honoring
|
|
1221
|
+
* the cache or allowing retries. We want to make sure it only attempts to
|
|
1222
|
+
* use the server in question */
|
|
1223
|
+
probe_server->probe_pending = ARES_TRUE;
|
|
1224
|
+
ares_send_nolock(channel, probe_server,
|
|
1225
|
+
ARES_SEND_FLAG_NOCACHE | ARES_SEND_FLAG_NORETRY,
|
|
1226
|
+
query->query, server_probe_cb, NULL, NULL);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
static size_t ares_calc_query_timeout(const ares_query_t *query,
|
|
1230
|
+
const ares_server_t *server,
|
|
1231
|
+
const ares_timeval_t *now)
|
|
1232
|
+
{
|
|
1233
|
+
const ares_channel_t *channel = query->channel;
|
|
1234
|
+
size_t timeout = ares_metrics_server_timeout(server, now);
|
|
1235
|
+
size_t timeplus = timeout;
|
|
1236
|
+
size_t rounds;
|
|
1237
|
+
size_t num_servers = ares_slist_len(channel->servers);
|
|
1238
|
+
|
|
1239
|
+
if (num_servers == 0) {
|
|
1240
|
+
return 0; /* LCOV_EXCL_LINE: DefensiveCoding */
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/* For each trip through the entire server list, we want to double the
|
|
1244
|
+
* retry from the last retry */
|
|
1245
|
+
rounds = (query->try_count / num_servers);
|
|
1246
|
+
if (rounds > 0) {
|
|
1247
|
+
if (rounds >= sizeof(timeplus) * CHAR_BIT ||
|
|
1248
|
+
timeplus > (SIZE_MAX >> rounds)) {
|
|
1249
|
+
timeplus = SIZE_MAX;
|
|
1250
|
+
} else {
|
|
1251
|
+
timeplus <<= rounds;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
if (channel->maxtimeout && timeplus > channel->maxtimeout) {
|
|
1256
|
+
timeplus = channel->maxtimeout;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/* Add some jitter to the retry timeout.
|
|
1260
|
+
*
|
|
1261
|
+
* Jitter is needed in situation when resolve requests are performed
|
|
1262
|
+
* simultaneously from multiple hosts and DNS server throttle these requests.
|
|
1263
|
+
* Adding randomness allows to avoid synchronisation of retries.
|
|
1264
|
+
*
|
|
1265
|
+
* Value of timeplus adjusted randomly to the range [0.5 * timeplus,
|
|
1266
|
+
* timeplus].
|
|
1267
|
+
*/
|
|
1268
|
+
if (rounds > 0) {
|
|
1269
|
+
unsigned short r;
|
|
1270
|
+
float delta_multiplier;
|
|
1271
|
+
|
|
1272
|
+
ares_rand_bytes(channel->rand_state, (unsigned char *)&r, sizeof(r));
|
|
1273
|
+
delta_multiplier = ((float)r / USHRT_MAX) * 0.5f;
|
|
1274
|
+
timeplus -= (size_t)((float)timeplus * delta_multiplier);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/* We want explicitly guarantee that timeplus is greater or equal to timeout
|
|
1278
|
+
* specified in channel options. */
|
|
1279
|
+
if (timeplus < timeout) {
|
|
1280
|
+
timeplus = timeout;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
return timeplus;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
static ares_conn_t *ares_fetch_connection(const ares_channel_t *channel,
|
|
1287
|
+
ares_server_t *server,
|
|
1288
|
+
const ares_query_t *query)
|
|
1289
|
+
{
|
|
1290
|
+
ares_llist_node_t *node;
|
|
1291
|
+
ares_conn_t *conn;
|
|
1292
|
+
|
|
1293
|
+
if (query->using_tcp) {
|
|
1294
|
+
return server->tcp_conn;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/* Fetch existing UDP connection */
|
|
1298
|
+
node = ares_llist_node_first(server->connections);
|
|
1299
|
+
if (node == NULL) {
|
|
1300
|
+
return NULL;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
conn = ares_llist_node_val(node);
|
|
1304
|
+
/* Not UDP, skip */
|
|
1305
|
+
if (conn->flags & ARES_CONN_FLAG_TCP) {
|
|
1306
|
+
return NULL;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/* Don't hand new queries to a connection that has been retired (e.g. it saw
|
|
1310
|
+
* a timeout). It keeps servicing its in-flight queries and is cleaned up
|
|
1311
|
+
* once idle. Note this is a per-connection check: a server-wide failure
|
|
1312
|
+
* counter must not be used here or a single transient failure would evict
|
|
1313
|
+
* every (including healthy) connection to the server and spawn a new socket
|
|
1314
|
+
* per query. */
|
|
1315
|
+
if (conn->flags & ARES_CONN_FLAG_NONEW) {
|
|
1316
|
+
return NULL;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/* Used too many times */
|
|
1320
|
+
if (channel->udp_max_queries > 0 &&
|
|
1321
|
+
conn->total_queries >= channel->udp_max_queries) {
|
|
1322
|
+
return NULL;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
return conn;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
static ares_status_t ares_conn_query_write(ares_conn_t *conn,
|
|
1329
|
+
ares_query_t *query,
|
|
1330
|
+
const ares_timeval_t *now)
|
|
1331
|
+
{
|
|
1332
|
+
ares_server_t *server = conn->server;
|
|
1333
|
+
ares_channel_t *channel = server->channel;
|
|
1334
|
+
ares_status_t status;
|
|
1335
|
+
|
|
1336
|
+
status = ares_cookie_apply(query->query, conn, now);
|
|
1337
|
+
if (status != ARES_SUCCESS) {
|
|
1338
|
+
return status;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/* We write using the TCP format even for UDP, we just strip the length
|
|
1342
|
+
* before putting on the wire */
|
|
1343
|
+
status = ares_dns_write_buf_tcp(query->query, conn->out_buf);
|
|
1344
|
+
if (status != ARES_SUCCESS) {
|
|
1345
|
+
return status;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/* Not pending a TFO write and not connected, so we can't even try to
|
|
1349
|
+
* write until we get a signal */
|
|
1350
|
+
if (conn->flags & ARES_CONN_FLAG_TCP &&
|
|
1351
|
+
!(conn->state_flags & ARES_CONN_STATE_CONNECTED) &&
|
|
1352
|
+
!(conn->flags & ARES_CONN_FLAG_TFO_INITIAL)) {
|
|
1353
|
+
return ARES_SUCCESS;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
/* Delay actual write if possible (TCP only, and only if callback
|
|
1357
|
+
* configured) */
|
|
1358
|
+
if (channel->notify_pending_write_cb && !channel->notify_pending_write &&
|
|
1359
|
+
conn->flags & ARES_CONN_FLAG_TCP) {
|
|
1360
|
+
channel->notify_pending_write = ARES_TRUE;
|
|
1361
|
+
channel->notify_pending_write_cb(channel->notify_pending_write_cb_data);
|
|
1362
|
+
return ARES_SUCCESS;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/* Unfortunately we need to write right away and can't aggregate multiple
|
|
1366
|
+
* queries into a single write. */
|
|
1367
|
+
return ares_conn_flush(conn);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/* Public entrypoint. Establishes a requeue list and drives
|
|
1371
|
+
* ares_send_query_int() plus any retries/deferred callbacks it produces
|
|
1372
|
+
* iteratively, so a chain of retryable failures can never recurse until the
|
|
1373
|
+
* stack is exhausted (#1043). */
|
|
1374
|
+
ares_status_t ares_send_query(ares_server_t *requested_server,
|
|
1375
|
+
ares_query_t *query, const ares_timeval_t *now)
|
|
1376
|
+
{
|
|
1377
|
+
ares_channel_t *channel = query->channel;
|
|
1378
|
+
ares_array_t *requeue = NULL;
|
|
1379
|
+
unsigned short qid = query->qid;
|
|
1380
|
+
ares_status_t status;
|
|
1381
|
+
|
|
1382
|
+
status = ares_send_query_int(requested_server, query, now, &requeue);
|
|
1383
|
+
|
|
1384
|
+
/* Drain any retries/deferred callbacks this send produced.
|
|
1385
|
+
* ares_flush_requeue() always fully processes and destroys the list (even on
|
|
1386
|
+
* ENOMEM), and sends the empty-queue notification if needed. */
|
|
1387
|
+
if (ares_flush_requeue(channel, now, &requeue) == ARES_ENOMEM) {
|
|
1388
|
+
status = ARES_ENOMEM;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
/* A retry may have been deferred (returning ARES_SUCCESS from the append)
|
|
1392
|
+
* and then terminally failed while draining, in which case the query has
|
|
1393
|
+
* been freed. Do not dereference 'query' here. If it is no longer tracked
|
|
1394
|
+
* it ended, so don't report success to the caller (which would, e.g., cause
|
|
1395
|
+
* ares_send_nolock() to write to a now-freed *qid). */
|
|
1396
|
+
if (status == ARES_SUCCESS &&
|
|
1397
|
+
ares_htable_szvp_get_direct(channel->queries_by_qid, qid) == NULL) {
|
|
1398
|
+
status = ARES_ETIMEOUT;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
return status;
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
static ares_status_t ares_send_query_int(ares_server_t *requested_server,
|
|
1405
|
+
ares_query_t *query,
|
|
1406
|
+
const ares_timeval_t *now,
|
|
1407
|
+
ares_array_t **requeue)
|
|
1408
|
+
{
|
|
1409
|
+
ares_channel_t *channel = query->channel;
|
|
1410
|
+
ares_server_t *server;
|
|
1411
|
+
ares_conn_t *conn;
|
|
1412
|
+
size_t timeplus;
|
|
1413
|
+
ares_status_t status;
|
|
1414
|
+
ares_bool_t probe_downed_server = ARES_TRUE;
|
|
1415
|
+
|
|
1416
|
+
/* Choose the server to send the query to */
|
|
1417
|
+
if (requested_server != NULL) {
|
|
1418
|
+
server = requested_server;
|
|
1419
|
+
} else {
|
|
1420
|
+
/* If rotate is turned on, do a random selection */
|
|
1421
|
+
if (channel->rotate) {
|
|
1422
|
+
server = ares_random_server(channel);
|
|
1423
|
+
} else {
|
|
1424
|
+
/* First server in list */
|
|
1425
|
+
server = ares_slist_first_val(channel->servers);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
if (server == NULL) {
|
|
1430
|
+
end_query(channel, server, query, ARES_ENOSERVER /* ? */, NULL, requeue);
|
|
1431
|
+
return ARES_ENOSERVER;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/* If a query is directed to a specific server, or the query is being
|
|
1435
|
+
* retried, don't probe for downed servers. Note that the chosen server
|
|
1436
|
+
* having failures itself does NOT disable probing: when every server has
|
|
1437
|
+
* failures (e.g. during an extended outage), probes are the only way a
|
|
1438
|
+
* recovered server that sorts behind the current best can be noticed. */
|
|
1439
|
+
if (requested_server != NULL || query->try_count != 0) {
|
|
1440
|
+
probe_downed_server = ARES_FALSE;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
conn = ares_fetch_connection(channel, server, query);
|
|
1444
|
+
if (conn == NULL) {
|
|
1445
|
+
status = ares_open_connection(&conn, channel, server, query->using_tcp);
|
|
1446
|
+
switch (status) {
|
|
1447
|
+
/* Good result, continue on */
|
|
1448
|
+
case ARES_SUCCESS:
|
|
1449
|
+
break;
|
|
1450
|
+
|
|
1451
|
+
/* These conditions are retryable as they are server-specific
|
|
1452
|
+
* error codes */
|
|
1453
|
+
case ARES_ECONNREFUSED:
|
|
1454
|
+
case ARES_EBADFAMILY:
|
|
1455
|
+
server_increment_failures(server, query->using_tcp);
|
|
1456
|
+
return ares_requeue_query(query, now, status, ARES_TRUE, NULL, requeue);
|
|
1457
|
+
|
|
1458
|
+
/* Anything else is not retryable, likely ENOMEM */
|
|
1459
|
+
default:
|
|
1460
|
+
end_query(channel, server, query, status, NULL, requeue);
|
|
1461
|
+
return status;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/* Write the query */
|
|
1466
|
+
status = ares_conn_query_write(conn, query, now);
|
|
1467
|
+
switch (status) {
|
|
1468
|
+
/* Good result, continue on */
|
|
1469
|
+
case ARES_SUCCESS:
|
|
1470
|
+
break;
|
|
1471
|
+
|
|
1472
|
+
case ARES_ENOMEM:
|
|
1473
|
+
/* Not retryable */
|
|
1474
|
+
end_query(channel, server, query, status, NULL, requeue);
|
|
1475
|
+
return status;
|
|
1476
|
+
|
|
1477
|
+
/* These conditions are retryable as they are server-specific
|
|
1478
|
+
* error codes */
|
|
1479
|
+
case ARES_ECONNREFUSED:
|
|
1480
|
+
case ARES_EBADFAMILY:
|
|
1481
|
+
handle_conn_error(conn, ARES_TRUE, status);
|
|
1482
|
+
status = ares_requeue_query(query, now, status, ARES_TRUE, NULL, requeue);
|
|
1483
|
+
if (status == ARES_ETIMEOUT) {
|
|
1484
|
+
status = ARES_ECONNREFUSED;
|
|
1485
|
+
}
|
|
1486
|
+
return status;
|
|
1487
|
+
|
|
1488
|
+
default:
|
|
1489
|
+
server_increment_failures(server, query->using_tcp);
|
|
1490
|
+
status = ares_requeue_query(query, now, status, ARES_TRUE, NULL, requeue);
|
|
1491
|
+
return status;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
timeplus = ares_calc_query_timeout(query, server, now);
|
|
1495
|
+
/* Keep track of queries bucketed by timeout, so we can process
|
|
1496
|
+
* timeout events quickly.
|
|
1497
|
+
*/
|
|
1498
|
+
ares_slist_node_destroy(query->node_queries_by_timeout);
|
|
1499
|
+
query->ts = *now;
|
|
1500
|
+
query->timeout = *now;
|
|
1501
|
+
ares_timeval_add(&query->timeout, timeplus);
|
|
1502
|
+
query->node_queries_by_timeout =
|
|
1503
|
+
ares_slist_insert(channel->queries_by_timeout, query);
|
|
1504
|
+
if (!query->node_queries_by_timeout) {
|
|
1505
|
+
/* LCOV_EXCL_START: OutOfMemory */
|
|
1506
|
+
end_query(channel, server, query, ARES_ENOMEM, NULL, requeue);
|
|
1507
|
+
return ARES_ENOMEM;
|
|
1508
|
+
/* LCOV_EXCL_STOP */
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/* Keep track of queries bucketed by connection, so we can process errors
|
|
1512
|
+
* quickly. */
|
|
1513
|
+
ares_llist_node_destroy(query->node_queries_to_conn);
|
|
1514
|
+
query->node_queries_to_conn =
|
|
1515
|
+
ares_llist_insert_last(conn->queries_to_conn, query);
|
|
1516
|
+
|
|
1517
|
+
if (query->node_queries_to_conn == NULL) {
|
|
1518
|
+
/* LCOV_EXCL_START: OutOfMemory */
|
|
1519
|
+
end_query(channel, server, query, ARES_ENOMEM, NULL, requeue);
|
|
1520
|
+
return ARES_ENOMEM;
|
|
1521
|
+
/* LCOV_EXCL_STOP */
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
query->conn = conn;
|
|
1525
|
+
conn->total_queries++;
|
|
1526
|
+
|
|
1527
|
+
/* We just successfully enqueud a query, see if we should probe downed
|
|
1528
|
+
* servers. */
|
|
1529
|
+
if (probe_downed_server) {
|
|
1530
|
+
ares_probe_failed_server(channel, server, query);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (channel->query_enqueue_cb) {
|
|
1534
|
+
channel->query_enqueue_cb(channel->query_enqueue_cb_data);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
return ARES_SUCCESS;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
static ares_bool_t same_questions(const ares_query_t *query,
|
|
1541
|
+
const ares_dns_record_t *arec)
|
|
1542
|
+
{
|
|
1543
|
+
size_t i;
|
|
1544
|
+
ares_bool_t rv = ARES_FALSE;
|
|
1545
|
+
const ares_dns_record_t *qrec = query->query;
|
|
1546
|
+
const ares_channel_t *channel = query->channel;
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
if (ares_dns_record_query_cnt(qrec) != ares_dns_record_query_cnt(arec)) {
|
|
1550
|
+
goto done;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
for (i = 0; i < ares_dns_record_query_cnt(qrec); i++) {
|
|
1554
|
+
const char *qname = NULL;
|
|
1555
|
+
const char *aname = NULL;
|
|
1556
|
+
ares_dns_rec_type_t qtype;
|
|
1557
|
+
ares_dns_rec_type_t atype;
|
|
1558
|
+
ares_dns_class_t qclass;
|
|
1559
|
+
ares_dns_class_t aclass;
|
|
1560
|
+
|
|
1561
|
+
if (ares_dns_record_query_get(qrec, i, &qname, &qtype, &qclass) !=
|
|
1562
|
+
ARES_SUCCESS ||
|
|
1563
|
+
qname == NULL) {
|
|
1564
|
+
goto done;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (ares_dns_record_query_get(arec, i, &aname, &atype, &aclass) !=
|
|
1568
|
+
ARES_SUCCESS ||
|
|
1569
|
+
aname == NULL) {
|
|
1570
|
+
goto done;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
if (qtype != atype || qclass != aclass) {
|
|
1574
|
+
goto done;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if (channel->flags & ARES_FLAG_DNS0x20 && !query->using_tcp) {
|
|
1578
|
+
/* NOTE: for DNS 0x20, part of the protection is to use a case-sensitive
|
|
1579
|
+
* comparison of the DNS query name. This expects the upstream DNS
|
|
1580
|
+
* server to preserve the case of the name in the response packet.
|
|
1581
|
+
* https://datatracker.ietf.org/doc/html/draft-vixie-dnsext-dns0x20-00
|
|
1582
|
+
*/
|
|
1583
|
+
if (!ares_streq(qname, aname)) {
|
|
1584
|
+
goto done;
|
|
1585
|
+
}
|
|
1586
|
+
} else {
|
|
1587
|
+
/* without DNS0x20 use case-insensitive matching */
|
|
1588
|
+
if (!ares_strcaseeq(qname, aname)) {
|
|
1589
|
+
goto done;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
rv = ARES_TRUE;
|
|
1595
|
+
|
|
1596
|
+
done:
|
|
1597
|
+
return rv;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
static void ares_detach_query(ares_query_t *query)
|
|
1601
|
+
{
|
|
1602
|
+
/* Remove the query from all the lists in which it is linked */
|
|
1603
|
+
ares_query_remove_from_conn(query);
|
|
1604
|
+
ares_htable_szvp_remove(query->channel->queries_by_qid, query->qid);
|
|
1605
|
+
ares_llist_node_destroy(query->node_all_queries);
|
|
1606
|
+
query->node_all_queries = NULL;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
static void end_query(ares_channel_t *channel, ares_server_t *server,
|
|
1610
|
+
ares_query_t *query, ares_status_t status,
|
|
1611
|
+
ares_dns_record_t *dnsrec, ares_array_t **requeue)
|
|
1612
|
+
{
|
|
1613
|
+
/* If we were probing for the server to come back online, lets mark it as
|
|
1614
|
+
* no longer being probed */
|
|
1615
|
+
if (server != NULL) {
|
|
1616
|
+
server->probe_pending = ARES_FALSE;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
ares_metrics_record(query, server, status, dnsrec);
|
|
1620
|
+
|
|
1621
|
+
/* Delay calling the query callback */
|
|
1622
|
+
if (requeue != NULL) {
|
|
1623
|
+
ares_append_endqueue(requeue, query, status, dnsrec);
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/* Invoke the callback. */
|
|
1628
|
+
query->callback(query->arg, status, query->timeouts, dnsrec);
|
|
1629
|
+
ares_free_query(query);
|
|
1630
|
+
|
|
1631
|
+
/* Check and notify if no other queries are enqueued on the channel. This
|
|
1632
|
+
* must come after the callback and freeing the query for 2 reasons.
|
|
1633
|
+
* 1) The callback itself may enqueue a new query
|
|
1634
|
+
* 2) Technically the current query isn't detached until it is free()'d.
|
|
1635
|
+
*/
|
|
1636
|
+
ares_queue_notify_empty(channel);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
void ares_free_query(ares_query_t *query)
|
|
1640
|
+
{
|
|
1641
|
+
ares_detach_query(query);
|
|
1642
|
+
/* Zero out some important stuff, to help catch bugs */
|
|
1643
|
+
query->callback = NULL;
|
|
1644
|
+
query->arg = NULL;
|
|
1645
|
+
/* Deallocate the memory associated with the query */
|
|
1646
|
+
ares_dns_record_destroy(query->query);
|
|
1647
|
+
|
|
1648
|
+
ares_free(query);
|
|
1649
|
+
}
|