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.
Files changed (232) hide show
  1. package/README.md +12 -4
  2. package/THIRD_PARTY_LICENSES +31 -1
  3. package/cli.js +3745 -890
  4. package/node/include/js_node.h +12 -1
  5. package/node/prelude/_http_common.js +29 -0
  6. package/node/prelude/async_hooks.js +50 -11
  7. package/node/prelude/child_process.js +31 -4
  8. package/node/prelude/console.js +311 -143
  9. package/node/prelude/crypto.js +6 -4
  10. package/node/prelude/dgram.js +1169 -0
  11. package/node/prelude/diagnostics_channel.js +3 -1
  12. package/node/prelude/dns.js +250 -45
  13. package/node/prelude/dns_promises.js +8 -2
  14. package/node/prelude/fs_promises.js +9 -1
  15. package/node/prelude/http.js +405 -94
  16. package/node/prelude/https.js +13 -4
  17. package/node/prelude/internal/console_class.js +7 -0
  18. package/node/prelude/internal/console_global.js +25 -0
  19. package/node/prelude/internal/crypto/key_encryption.js +217 -0
  20. package/node/prelude/internal/crypto/keys.js +161 -26
  21. package/node/prelude/internal/crypto/random.js +24 -0
  22. package/node/prelude/internal/crypto/util.js +2 -1
  23. package/node/prelude/internal/crypto_x509.js +161 -0
  24. package/node/prelude/internal/dns_classes.js +6 -0
  25. package/node/prelude/internal/dns_resolver.js +371 -0
  26. package/node/prelude/internal/errors.js +9 -0
  27. package/node/prelude/internal/events_on.js +82 -37
  28. package/node/prelude/internal/features.js +8 -4
  29. package/node/prelude/internal/fs_filehandle.js +10 -4
  30. package/node/prelude/internal/http_chars.ts +22 -0
  31. package/node/prelude/internal/perf_histogram.js +1 -1
  32. package/node/prelude/internal/process_more.js +32 -17
  33. package/node/prelude/internal/process_signals.js +8 -2
  34. package/node/prelude/internal/punycode.ts +163 -0
  35. package/node/prelude/internal/readline.js +648 -274
  36. package/node/prelude/internal/readline_editor.js +1240 -0
  37. package/node/prelude/internal/string_width.js +43 -0
  38. package/node/prelude/internal/tls/common.js +145 -33
  39. package/node/prelude/internal/tls/wrap.js +182 -49
  40. package/node/prelude/internal/tls/x509.js +4 -1
  41. package/node/prelude/internal/udp.js +177 -0
  42. package/node/prelude/net.js +30 -14
  43. package/node/prelude/os.js +24 -6
  44. package/node/prelude/perf_hooks.js +37 -2
  45. package/node/prelude/punycode.js +2 -130
  46. package/node/prelude/readline.js +113 -4
  47. package/node/prelude/readline_promises.js +56 -32
  48. package/node/prelude/tls.js +21 -4
  49. package/node/prelude/util.js +3 -16
  50. package/node/src/host_dns.c +623 -0
  51. package/node/src/host_http.c +12 -0
  52. package/node/src/host_net.c +46 -0
  53. package/node/src/host_os.c +1 -6
  54. package/node/src/host_sys.c +79 -0
  55. package/node/src/host_udp.c +418 -0
  56. package/node/third_party/c-ares/LICENSE.md +24 -0
  57. package/node/third_party/c-ares/config/darwin/ares_config.h +573 -0
  58. package/node/third_party/c-ares/config/freebsd/ares_config.h +573 -0
  59. package/node/third_party/c-ares/config/linux/ares_config.h +573 -0
  60. package/node/third_party/c-ares/config/openbsd/ares_config.h +583 -0
  61. package/node/third_party/c-ares/include/ares.h +1237 -0
  62. package/node/third_party/c-ares/include/ares_build.h +202 -0
  63. package/node/third_party/c-ares/include/ares_dns.h +127 -0
  64. package/node/third_party/c-ares/include/ares_dns_record.h +1119 -0
  65. package/node/third_party/c-ares/include/ares_nameser.h +510 -0
  66. package/node/third_party/c-ares/include/ares_version.h +47 -0
  67. package/node/third_party/c-ares/src/lib/ares_addrinfo2hostent.c +305 -0
  68. package/node/third_party/c-ares/src/lib/ares_addrinfo_localhost.c +245 -0
  69. package/node/third_party/c-ares/src/lib/ares_android.c +496 -0
  70. package/node/third_party/c-ares/src/lib/ares_android.h +38 -0
  71. package/node/third_party/c-ares/src/lib/ares_cancel.c +87 -0
  72. package/node/third_party/c-ares/src/lib/ares_close_sockets.c +137 -0
  73. package/node/third_party/c-ares/src/lib/ares_conn.c +511 -0
  74. package/node/third_party/c-ares/src/lib/ares_conn.h +202 -0
  75. package/node/third_party/c-ares/src/lib/ares_cookie.c +461 -0
  76. package/node/third_party/c-ares/src/lib/ares_data.c +168 -0
  77. package/node/third_party/c-ares/src/lib/ares_data.h +92 -0
  78. package/node/third_party/c-ares/src/lib/ares_destroy.c +159 -0
  79. package/node/third_party/c-ares/src/lib/ares_free_hostent.c +54 -0
  80. package/node/third_party/c-ares/src/lib/ares_free_string.c +33 -0
  81. package/node/third_party/c-ares/src/lib/ares_freeaddrinfo.c +67 -0
  82. package/node/third_party/c-ares/src/lib/ares_getaddrinfo.c +806 -0
  83. package/node/third_party/c-ares/src/lib/ares_getenv.c +38 -0
  84. package/node/third_party/c-ares/src/lib/ares_getenv.h +34 -0
  85. package/node/third_party/c-ares/src/lib/ares_gethostbyaddr.c +229 -0
  86. package/node/third_party/c-ares/src/lib/ares_gethostbyname.c +341 -0
  87. package/node/third_party/c-ares/src/lib/ares_getnameinfo.c +437 -0
  88. package/node/third_party/c-ares/src/lib/ares_hosts_file.c +960 -0
  89. package/node/third_party/c-ares/src/lib/ares_inet_net_pton.h +31 -0
  90. package/node/third_party/c-ares/src/lib/ares_init.c +631 -0
  91. package/node/third_party/c-ares/src/lib/ares_ipv6.h +115 -0
  92. package/node/third_party/c-ares/src/lib/ares_library_init.c +190 -0
  93. package/node/third_party/c-ares/src/lib/ares_metrics.c +261 -0
  94. package/node/third_party/c-ares/src/lib/ares_options.c +492 -0
  95. package/node/third_party/c-ares/src/lib/ares_parse_into_addrinfo.c +179 -0
  96. package/node/third_party/c-ares/src/lib/ares_private.h +648 -0
  97. package/node/third_party/c-ares/src/lib/ares_process.c +1649 -0
  98. package/node/third_party/c-ares/src/lib/ares_qcache.c +442 -0
  99. package/node/third_party/c-ares/src/lib/ares_query.c +151 -0
  100. package/node/third_party/c-ares/src/lib/ares_search.c +623 -0
  101. package/node/third_party/c-ares/src/lib/ares_send.c +300 -0
  102. package/node/third_party/c-ares/src/lib/ares_set_socket_functions.c +590 -0
  103. package/node/third_party/c-ares/src/lib/ares_setup.h +224 -0
  104. package/node/third_party/c-ares/src/lib/ares_socket.c +427 -0
  105. package/node/third_party/c-ares/src/lib/ares_socket.h +163 -0
  106. package/node/third_party/c-ares/src/lib/ares_sortaddrinfo.c +447 -0
  107. package/node/third_party/c-ares/src/lib/ares_strerror.c +91 -0
  108. package/node/third_party/c-ares/src/lib/ares_sysconfig.c +642 -0
  109. package/node/third_party/c-ares/src/lib/ares_sysconfig_files.c +851 -0
  110. package/node/third_party/c-ares/src/lib/ares_sysconfig_mac.c +373 -0
  111. package/node/third_party/c-ares/src/lib/ares_sysconfig_win.c +673 -0
  112. package/node/third_party/c-ares/src/lib/ares_timeout.c +162 -0
  113. package/node/third_party/c-ares/src/lib/ares_update_servers.c +1376 -0
  114. package/node/third_party/c-ares/src/lib/ares_version.c +36 -0
  115. package/node/third_party/c-ares/src/lib/config-dos.h +114 -0
  116. package/node/third_party/c-ares/src/lib/config-win32.h +396 -0
  117. package/node/third_party/c-ares/src/lib/dsa/ares_array.c +409 -0
  118. package/node/third_party/c-ares/src/lib/dsa/ares_htable.c +451 -0
  119. package/node/third_party/c-ares/src/lib/dsa/ares_htable.h +174 -0
  120. package/node/third_party/c-ares/src/lib/dsa/ares_htable_asvp.c +224 -0
  121. package/node/third_party/c-ares/src/lib/dsa/ares_htable_dict.c +230 -0
  122. package/node/third_party/c-ares/src/lib/dsa/ares_htable_strvp.c +210 -0
  123. package/node/third_party/c-ares/src/lib/dsa/ares_htable_szvp.c +188 -0
  124. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpstr.c +186 -0
  125. package/node/third_party/c-ares/src/lib/dsa/ares_htable_vpvp.c +194 -0
  126. package/node/third_party/c-ares/src/lib/dsa/ares_llist.c +385 -0
  127. package/node/third_party/c-ares/src/lib/dsa/ares_slist.c +479 -0
  128. package/node/third_party/c-ares/src/lib/dsa/ares_slist.h +207 -0
  129. package/node/third_party/c-ares/src/lib/event/ares_event.h +191 -0
  130. package/node/third_party/c-ares/src/lib/event/ares_event_configchg.c +746 -0
  131. package/node/third_party/c-ares/src/lib/event/ares_event_epoll.c +192 -0
  132. package/node/third_party/c-ares/src/lib/event/ares_event_kqueue.c +248 -0
  133. package/node/third_party/c-ares/src/lib/event/ares_event_poll.c +140 -0
  134. package/node/third_party/c-ares/src/lib/event/ares_event_select.c +159 -0
  135. package/node/third_party/c-ares/src/lib/event/ares_event_thread.c +575 -0
  136. package/node/third_party/c-ares/src/lib/event/ares_event_wake_pipe.c +172 -0
  137. package/node/third_party/c-ares/src/lib/event/ares_event_win32.c +981 -0
  138. package/node/third_party/c-ares/src/lib/event/ares_event_win32.h +161 -0
  139. package/node/third_party/c-ares/src/lib/include/ares_array.h +276 -0
  140. package/node/third_party/c-ares/src/lib/include/ares_buf.h +732 -0
  141. package/node/third_party/c-ares/src/lib/include/ares_htable_asvp.h +130 -0
  142. package/node/third_party/c-ares/src/lib/include/ares_htable_dict.h +123 -0
  143. package/node/third_party/c-ares/src/lib/include/ares_htable_strvp.h +130 -0
  144. package/node/third_party/c-ares/src/lib/include/ares_htable_szvp.h +118 -0
  145. package/node/third_party/c-ares/src/lib/include/ares_htable_vpstr.h +111 -0
  146. package/node/third_party/c-ares/src/lib/include/ares_htable_vpvp.h +128 -0
  147. package/node/third_party/c-ares/src/lib/include/ares_llist.h +239 -0
  148. package/node/third_party/c-ares/src/lib/include/ares_mem.h +41 -0
  149. package/node/third_party/c-ares/src/lib/include/ares_str.h +261 -0
  150. package/node/third_party/c-ares/src/lib/inet_net_pton.c +451 -0
  151. package/node/third_party/c-ares/src/lib/inet_ntop.c +219 -0
  152. package/node/third_party/c-ares/src/lib/legacy/ares_create_query.c +78 -0
  153. package/node/third_party/c-ares/src/lib/legacy/ares_expand_name.c +99 -0
  154. package/node/third_party/c-ares/src/lib/legacy/ares_expand_string.c +107 -0
  155. package/node/third_party/c-ares/src/lib/legacy/ares_fds.c +80 -0
  156. package/node/third_party/c-ares/src/lib/legacy/ares_getsock.c +85 -0
  157. package/node/third_party/c-ares/src/lib/legacy/ares_parse_a_reply.c +107 -0
  158. package/node/third_party/c-ares/src/lib/legacy/ares_parse_aaaa_reply.c +109 -0
  159. package/node/third_party/c-ares/src/lib/legacy/ares_parse_caa_reply.c +137 -0
  160. package/node/third_party/c-ares/src/lib/legacy/ares_parse_mx_reply.c +110 -0
  161. package/node/third_party/c-ares/src/lib/legacy/ares_parse_naptr_reply.c +132 -0
  162. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ns_reply.c +154 -0
  163. package/node/third_party/c-ares/src/lib/legacy/ares_parse_ptr_reply.c +213 -0
  164. package/node/third_party/c-ares/src/lib/legacy/ares_parse_soa_reply.c +115 -0
  165. package/node/third_party/c-ares/src/lib/legacy/ares_parse_srv_reply.c +114 -0
  166. package/node/third_party/c-ares/src/lib/legacy/ares_parse_txt_reply.c +144 -0
  167. package/node/third_party/c-ares/src/lib/legacy/ares_parse_uri_reply.c +113 -0
  168. package/node/third_party/c-ares/src/lib/record/ares_dns_mapping.c +982 -0
  169. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.c +309 -0
  170. package/node/third_party/c-ares/src/lib/record/ares_dns_multistring.h +72 -0
  171. package/node/third_party/c-ares/src/lib/record/ares_dns_name.c +736 -0
  172. package/node/third_party/c-ares/src/lib/record/ares_dns_parse.c +1360 -0
  173. package/node/third_party/c-ares/src/lib/record/ares_dns_private.h +273 -0
  174. package/node/third_party/c-ares/src/lib/record/ares_dns_record.c +1701 -0
  175. package/node/third_party/c-ares/src/lib/record/ares_dns_write.c +1229 -0
  176. package/node/third_party/c-ares/src/lib/str/ares_buf.c +1510 -0
  177. package/node/third_party/c-ares/src/lib/str/ares_str.c +562 -0
  178. package/node/third_party/c-ares/src/lib/str/ares_strsplit.c +90 -0
  179. package/node/third_party/c-ares/src/lib/str/ares_strsplit.h +51 -0
  180. package/node/third_party/c-ares/src/lib/thirdparty/apple/dnsinfo.h +122 -0
  181. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.c +640 -0
  182. package/node/third_party/c-ares/src/lib/util/ares_iface_ips.h +140 -0
  183. package/node/third_party/c-ares/src/lib/util/ares_math.c +167 -0
  184. package/node/third_party/c-ares/src/lib/util/ares_math.h +58 -0
  185. package/node/third_party/c-ares/src/lib/util/ares_rand.c +389 -0
  186. package/node/third_party/c-ares/src/lib/util/ares_rand.h +36 -0
  187. package/node/third_party/c-ares/src/lib/util/ares_threads.c +613 -0
  188. package/node/third_party/c-ares/src/lib/util/ares_threads.h +60 -0
  189. package/node/third_party/c-ares/src/lib/util/ares_time.h +49 -0
  190. package/node/third_party/c-ares/src/lib/util/ares_timeval.c +95 -0
  191. package/node/third_party/c-ares/src/lib/util/ares_uri.c +1627 -0
  192. package/node/third_party/c-ares/src/lib/util/ares_uri.h +252 -0
  193. package/node/third_party/c-ares/src/lib/windows_port.c +29 -0
  194. package/package.json +1 -1
  195. package/prebuilt/darwin-arm64/cares-a6286c65ca9374a3.a +0 -0
  196. package/prebuilt/darwin-x64/cares-a6286c65ca9374a3.a +0 -0
  197. package/prebuilt/linux-arm64/cares-4ba05ac8e948af8e.a +0 -0
  198. package/prebuilt/linux-arm64-musl/cares-4ba05ac8e948af8e.a +0 -0
  199. package/prebuilt/linux-x64/cares-4ba05ac8e948af8e.a +0 -0
  200. package/prebuilt/linux-x64-musl/cares-4ba05ac8e948af8e.a +0 -0
  201. package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
  202. package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
  203. package/prebuilt/win32-x64/cares-49c9ec95162b912d.a +0 -0
  204. package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
  205. package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  206. package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
  207. package/runtime/include/js_buffer.h +185 -0
  208. package/runtime/include/js_builtins.h +58 -0
  209. package/runtime/include/js_crypto.h +11 -0
  210. package/runtime/include/js_exception.h +3 -0
  211. package/runtime/include/js_function.h +1 -1
  212. package/runtime/include/js_gc.h +13 -10
  213. package/runtime/include/js_jobs.h +2 -0
  214. package/runtime/include/js_object.h +79 -3
  215. package/runtime/include/js_ops.h +27 -0
  216. package/runtime/include/js_runtime.h +1 -0
  217. package/runtime/include/js_web.h +0 -3
  218. package/runtime/include/jscontext.h +8 -0
  219. package/runtime/src/builtins.c +17 -4
  220. package/runtime/src/crypto_internal.h +1 -0
  221. package/runtime/src/crypto_subtle.c +66 -7
  222. package/runtime/src/crypto_tls.c +265 -19
  223. package/runtime/src/function.c +37 -10
  224. package/runtime/src/gc.c +76 -93
  225. package/runtime/src/inspect.c +18 -1
  226. package/runtime/src/iterator.c +5 -1
  227. package/runtime/src/jobs.c +37 -22
  228. package/runtime/src/object.c +91 -8
  229. package/runtime/src/os.c +16 -6
  230. package/runtime/src/os.h +2 -0
  231. package/runtime/src/process.c +97 -10
  232. 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
+ }