1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, et al.
|
---|
9 | *
|
---|
10 | * This software is licensed as described in the file COPYING, which
|
---|
11 | * you should have received as part of this distribution. The terms
|
---|
12 | * are also available at https://curl.se/docs/copyright.html.
|
---|
13 | *
|
---|
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
---|
15 | * copies of the Software, and permit persons to whom the Software is
|
---|
16 | * furnished to do so, under the terms of the COPYING file.
|
---|
17 | *
|
---|
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
19 | * KIND, either express or implied.
|
---|
20 | *
|
---|
21 | * SPDX-License-Identifier: curl
|
---|
22 | *
|
---|
23 | ***************************************************************************/
|
---|
24 |
|
---|
25 | #include "curl_setup.h"
|
---|
26 |
|
---|
27 | /***********************************************************************
|
---|
28 | * Only for ares-enabled builds
|
---|
29 | * And only for functions that fulfill the asynch resolver backend API
|
---|
30 | * as defined in asyn.h, nothing else belongs in this file!
|
---|
31 | **********************************************************************/
|
---|
32 |
|
---|
33 | #ifdef CURLRES_ARES
|
---|
34 |
|
---|
35 | #include <limits.h>
|
---|
36 | #ifdef HAVE_NETINET_IN_H
|
---|
37 | #include <netinet/in.h>
|
---|
38 | #endif
|
---|
39 | #ifdef HAVE_NETDB_H
|
---|
40 | #include <netdb.h>
|
---|
41 | #endif
|
---|
42 | #ifdef HAVE_ARPA_INET_H
|
---|
43 | #include <arpa/inet.h>
|
---|
44 | #endif
|
---|
45 | #ifdef __VMS
|
---|
46 | #include <in.h>
|
---|
47 | #include <inet.h>
|
---|
48 | #endif
|
---|
49 |
|
---|
50 | #include "urldata.h"
|
---|
51 | #include "sendf.h"
|
---|
52 | #include "hostip.h"
|
---|
53 | #include "hash.h"
|
---|
54 | #include "share.h"
|
---|
55 | #include "url.h"
|
---|
56 | #include "multiif.h"
|
---|
57 | #include "inet_pton.h"
|
---|
58 | #include "connect.h"
|
---|
59 | #include "select.h"
|
---|
60 | #include "progress.h"
|
---|
61 | #include "timediff.h"
|
---|
62 |
|
---|
63 | # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
|
---|
64 | defined(WIN32)
|
---|
65 | # define CARES_STATICLIB
|
---|
66 | # endif
|
---|
67 | # include <ares.h>
|
---|
68 | # include <ares_version.h> /* really old c-ares didn't include this by
|
---|
69 | itself */
|
---|
70 |
|
---|
71 | #if ARES_VERSION >= 0x010500
|
---|
72 | /* c-ares 1.5.0 or later, the callback proto is modified */
|
---|
73 | #define HAVE_CARES_CALLBACK_TIMEOUTS 1
|
---|
74 | #endif
|
---|
75 |
|
---|
76 | #if ARES_VERSION >= 0x010601
|
---|
77 | /* IPv6 supported since 1.6.1 */
|
---|
78 | #define HAVE_CARES_IPV6 1
|
---|
79 | #endif
|
---|
80 |
|
---|
81 | #if ARES_VERSION >= 0x010704
|
---|
82 | #define HAVE_CARES_SERVERS_CSV 1
|
---|
83 | #define HAVE_CARES_LOCAL_DEV 1
|
---|
84 | #define HAVE_CARES_SET_LOCAL 1
|
---|
85 | #endif
|
---|
86 |
|
---|
87 | #if ARES_VERSION >= 0x010b00
|
---|
88 | #define HAVE_CARES_PORTS_CSV 1
|
---|
89 | #endif
|
---|
90 |
|
---|
91 | #if ARES_VERSION >= 0x011000
|
---|
92 | /* 1.16.0 or later has ares_getaddrinfo */
|
---|
93 | #define HAVE_CARES_GETADDRINFO 1
|
---|
94 | #endif
|
---|
95 |
|
---|
96 | /* The last 3 #include files should be in this order */
|
---|
97 | #include "curl_printf.h"
|
---|
98 | #include "curl_memory.h"
|
---|
99 | #include "memdebug.h"
|
---|
100 |
|
---|
101 | struct thread_data {
|
---|
102 | int num_pending; /* number of outstanding c-ares requests */
|
---|
103 | struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
|
---|
104 | parts */
|
---|
105 | int last_status;
|
---|
106 | #ifndef HAVE_CARES_GETADDRINFO
|
---|
107 | struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
|
---|
108 | #endif
|
---|
109 | char hostname[1];
|
---|
110 | };
|
---|
111 |
|
---|
112 | /* How long we are willing to wait for additional parallel responses after
|
---|
113 | obtaining a "definitive" one.
|
---|
114 |
|
---|
115 | This is intended to equal the c-ares default timeout. cURL always uses that
|
---|
116 | default value. Unfortunately, c-ares doesn't expose its default timeout in
|
---|
117 | its API, but it is officially documented as 5 seconds.
|
---|
118 |
|
---|
119 | See query_completed_cb() for an explanation of how this is used.
|
---|
120 | */
|
---|
121 | #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
|
---|
122 |
|
---|
123 | /*
|
---|
124 | * Curl_resolver_global_init() - the generic low-level asynchronous name
|
---|
125 | * resolve API. Called from curl_global_init() to initialize global resolver
|
---|
126 | * environment. Initializes ares library.
|
---|
127 | */
|
---|
128 | int Curl_resolver_global_init(void)
|
---|
129 | {
|
---|
130 | #ifdef CARES_HAVE_ARES_LIBRARY_INIT
|
---|
131 | if(ares_library_init(ARES_LIB_INIT_ALL)) {
|
---|
132 | return CURLE_FAILED_INIT;
|
---|
133 | }
|
---|
134 | #endif
|
---|
135 | return CURLE_OK;
|
---|
136 | }
|
---|
137 |
|
---|
138 | /*
|
---|
139 | * Curl_resolver_global_cleanup()
|
---|
140 | *
|
---|
141 | * Called from curl_global_cleanup() to destroy global resolver environment.
|
---|
142 | * Deinitializes ares library.
|
---|
143 | */
|
---|
144 | void Curl_resolver_global_cleanup(void)
|
---|
145 | {
|
---|
146 | #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
|
---|
147 | ares_library_cleanup();
|
---|
148 | #endif
|
---|
149 | }
|
---|
150 |
|
---|
151 |
|
---|
152 | static void sock_state_cb(void *data, ares_socket_t socket_fd,
|
---|
153 | int readable, int writable)
|
---|
154 | {
|
---|
155 | struct Curl_easy *easy = data;
|
---|
156 | if(!readable && !writable) {
|
---|
157 | DEBUGASSERT(easy);
|
---|
158 | Curl_multi_closed(easy, socket_fd);
|
---|
159 | }
|
---|
160 | }
|
---|
161 |
|
---|
162 | /*
|
---|
163 | * Curl_resolver_init()
|
---|
164 | *
|
---|
165 | * Called from curl_easy_init() -> Curl_open() to initialize resolver
|
---|
166 | * URL-state specific environment ('resolver' member of the UrlState
|
---|
167 | * structure). Fills the passed pointer by the initialized ares_channel.
|
---|
168 | */
|
---|
169 | CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
|
---|
170 | {
|
---|
171 | int status;
|
---|
172 | struct ares_options options;
|
---|
173 | int optmask = ARES_OPT_SOCK_STATE_CB;
|
---|
174 | options.sock_state_cb = sock_state_cb;
|
---|
175 | options.sock_state_cb_data = easy;
|
---|
176 | status = ares_init_options((ares_channel*)resolver, &options, optmask);
|
---|
177 | if(status != ARES_SUCCESS) {
|
---|
178 | if(status == ARES_ENOMEM)
|
---|
179 | return CURLE_OUT_OF_MEMORY;
|
---|
180 | else
|
---|
181 | return CURLE_FAILED_INIT;
|
---|
182 | }
|
---|
183 | return CURLE_OK;
|
---|
184 | /* make sure that all other returns from this function should destroy the
|
---|
185 | ares channel before returning error! */
|
---|
186 | }
|
---|
187 |
|
---|
188 | /*
|
---|
189 | * Curl_resolver_cleanup()
|
---|
190 | *
|
---|
191 | * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
|
---|
192 | * URL-state specific environment ('resolver' member of the UrlState
|
---|
193 | * structure). Destroys the ares channel.
|
---|
194 | */
|
---|
195 | void Curl_resolver_cleanup(void *resolver)
|
---|
196 | {
|
---|
197 | ares_destroy((ares_channel)resolver);
|
---|
198 | }
|
---|
199 |
|
---|
200 | /*
|
---|
201 | * Curl_resolver_duphandle()
|
---|
202 | *
|
---|
203 | * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
|
---|
204 | * environment ('resolver' member of the UrlState structure). Duplicates the
|
---|
205 | * 'from' ares channel and passes the resulting channel to the 'to' pointer.
|
---|
206 | */
|
---|
207 | CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
|
---|
208 | {
|
---|
209 | (void)from;
|
---|
210 | /*
|
---|
211 | * it would be better to call ares_dup instead, but right now
|
---|
212 | * it is not possible to set 'sock_state_cb_data' outside of
|
---|
213 | * ares_init_options
|
---|
214 | */
|
---|
215 | return Curl_resolver_init(easy, to);
|
---|
216 | }
|
---|
217 |
|
---|
218 | static void destroy_async_data(struct Curl_async *async);
|
---|
219 |
|
---|
220 | /*
|
---|
221 | * Cancel all possibly still on-going resolves for this connection.
|
---|
222 | */
|
---|
223 | void Curl_resolver_cancel(struct Curl_easy *data)
|
---|
224 | {
|
---|
225 | DEBUGASSERT(data);
|
---|
226 | if(data->state.async.resolver)
|
---|
227 | ares_cancel((ares_channel)data->state.async.resolver);
|
---|
228 | destroy_async_data(&data->state.async);
|
---|
229 | }
|
---|
230 |
|
---|
231 | /*
|
---|
232 | * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
|
---|
233 | * never block.
|
---|
234 | */
|
---|
235 | void Curl_resolver_kill(struct Curl_easy *data)
|
---|
236 | {
|
---|
237 | /* We don't need to check the resolver state because we can be called safely
|
---|
238 | at any time and we always do the same thing. */
|
---|
239 | Curl_resolver_cancel(data);
|
---|
240 | }
|
---|
241 |
|
---|
242 | /*
|
---|
243 | * destroy_async_data() cleans up async resolver data.
|
---|
244 | */
|
---|
245 | static void destroy_async_data(struct Curl_async *async)
|
---|
246 | {
|
---|
247 | if(async->tdata) {
|
---|
248 | struct thread_data *res = async->tdata;
|
---|
249 | if(res) {
|
---|
250 | if(res->temp_ai) {
|
---|
251 | Curl_freeaddrinfo(res->temp_ai);
|
---|
252 | res->temp_ai = NULL;
|
---|
253 | }
|
---|
254 | free(res);
|
---|
255 | }
|
---|
256 | async->tdata = NULL;
|
---|
257 | }
|
---|
258 | }
|
---|
259 |
|
---|
260 | /*
|
---|
261 | * Curl_resolver_getsock() is called when someone from the outside world
|
---|
262 | * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
|
---|
263 | * with ares. The caller must make sure that this function is only called when
|
---|
264 | * we have a working ares channel.
|
---|
265 | *
|
---|
266 | * Returns: sockets-in-use-bitmap
|
---|
267 | */
|
---|
268 |
|
---|
269 | int Curl_resolver_getsock(struct Curl_easy *data,
|
---|
270 | curl_socket_t *socks)
|
---|
271 | {
|
---|
272 | struct timeval maxtime;
|
---|
273 | struct timeval timebuf;
|
---|
274 | struct timeval *timeout;
|
---|
275 | long milli;
|
---|
276 | int max = ares_getsock((ares_channel)data->state.async.resolver,
|
---|
277 | (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
|
---|
278 |
|
---|
279 | maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
|
---|
280 | maxtime.tv_usec = 0;
|
---|
281 |
|
---|
282 | timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
|
---|
283 | &timebuf);
|
---|
284 | milli = (long)curlx_tvtoms(timeout);
|
---|
285 | if(milli == 0)
|
---|
286 | milli += 10;
|
---|
287 | Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
|
---|
288 |
|
---|
289 | return max;
|
---|
290 | }
|
---|
291 |
|
---|
292 | /*
|
---|
293 | * waitperform()
|
---|
294 | *
|
---|
295 | * 1) Ask ares what sockets it currently plays with, then
|
---|
296 | * 2) wait for the timeout period to check for action on ares' sockets.
|
---|
297 | * 3) tell ares to act on all the sockets marked as "with action"
|
---|
298 | *
|
---|
299 | * return number of sockets it worked on, or -1 on error
|
---|
300 | */
|
---|
301 |
|
---|
302 | static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
|
---|
303 | {
|
---|
304 | int nfds;
|
---|
305 | int bitmask;
|
---|
306 | ares_socket_t socks[ARES_GETSOCK_MAXNUM];
|
---|
307 | struct pollfd pfd[ARES_GETSOCK_MAXNUM];
|
---|
308 | int i;
|
---|
309 | int num = 0;
|
---|
310 |
|
---|
311 | bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
|
---|
312 | ARES_GETSOCK_MAXNUM);
|
---|
313 |
|
---|
314 | for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
|
---|
315 | pfd[i].events = 0;
|
---|
316 | pfd[i].revents = 0;
|
---|
317 | if(ARES_GETSOCK_READABLE(bitmask, i)) {
|
---|
318 | pfd[i].fd = socks[i];
|
---|
319 | pfd[i].events |= POLLRDNORM|POLLIN;
|
---|
320 | }
|
---|
321 | if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
|
---|
322 | pfd[i].fd = socks[i];
|
---|
323 | pfd[i].events |= POLLWRNORM|POLLOUT;
|
---|
324 | }
|
---|
325 | if(pfd[i].events)
|
---|
326 | num++;
|
---|
327 | else
|
---|
328 | break;
|
---|
329 | }
|
---|
330 |
|
---|
331 | if(num) {
|
---|
332 | nfds = Curl_poll(pfd, num, timeout_ms);
|
---|
333 | if(nfds < 0)
|
---|
334 | return -1;
|
---|
335 | }
|
---|
336 | else
|
---|
337 | nfds = 0;
|
---|
338 |
|
---|
339 | if(!nfds)
|
---|
340 | /* Call ares_process() unconditionally here, even if we simply timed out
|
---|
341 | above, as otherwise the ares name resolve won't timeout! */
|
---|
342 | ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
|
---|
343 | ARES_SOCKET_BAD);
|
---|
344 | else {
|
---|
345 | /* move through the descriptors and ask for processing on them */
|
---|
346 | for(i = 0; i < num; i++)
|
---|
347 | ares_process_fd((ares_channel)data->state.async.resolver,
|
---|
348 | (pfd[i].revents & (POLLRDNORM|POLLIN))?
|
---|
349 | pfd[i].fd:ARES_SOCKET_BAD,
|
---|
350 | (pfd[i].revents & (POLLWRNORM|POLLOUT))?
|
---|
351 | pfd[i].fd:ARES_SOCKET_BAD);
|
---|
352 | }
|
---|
353 | return nfds;
|
---|
354 | }
|
---|
355 |
|
---|
356 | /*
|
---|
357 | * Curl_resolver_is_resolved() is called repeatedly to check if a previous
|
---|
358 | * name resolve request has completed. It should also make sure to time-out if
|
---|
359 | * the operation seems to take too long.
|
---|
360 | *
|
---|
361 | * Returns normal CURLcode errors.
|
---|
362 | */
|
---|
363 | CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
---|
364 | struct Curl_dns_entry **dns)
|
---|
365 | {
|
---|
366 | struct thread_data *res = data->state.async.tdata;
|
---|
367 | CURLcode result = CURLE_OK;
|
---|
368 |
|
---|
369 | DEBUGASSERT(dns);
|
---|
370 | *dns = NULL;
|
---|
371 |
|
---|
372 | if(waitperform(data, 0) < 0)
|
---|
373 | return CURLE_UNRECOVERABLE_POLL;
|
---|
374 |
|
---|
375 | #ifndef HAVE_CARES_GETADDRINFO
|
---|
376 | /* Now that we've checked for any last minute results above, see if there are
|
---|
377 | any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
|
---|
378 | expires. */
|
---|
379 | if(res
|
---|
380 | && res->num_pending
|
---|
381 | /* This is only set to non-zero if the timer was started. */
|
---|
382 | && (res->happy_eyeballs_dns_time.tv_sec
|
---|
383 | || res->happy_eyeballs_dns_time.tv_usec)
|
---|
384 | && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
|
---|
385 | >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
|
---|
386 | /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
|
---|
387 | running. */
|
---|
388 | memset(
|
---|
389 | &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
|
---|
390 |
|
---|
391 | /* Cancel the raw c-ares request, which will fire query_completed_cb() with
|
---|
392 | ARES_ECANCELLED synchronously for all pending responses. This will
|
---|
393 | leave us with res->num_pending == 0, which is perfect for the next
|
---|
394 | block. */
|
---|
395 | ares_cancel((ares_channel)data->state.async.resolver);
|
---|
396 | DEBUGASSERT(res->num_pending == 0);
|
---|
397 | }
|
---|
398 | #endif
|
---|
399 |
|
---|
400 | if(res && !res->num_pending) {
|
---|
401 | (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
|
---|
402 | /* temp_ai ownership is moved to the connection, so we need not free-up
|
---|
403 | them */
|
---|
404 | res->temp_ai = NULL;
|
---|
405 |
|
---|
406 | if(!data->state.async.dns)
|
---|
407 | result = Curl_resolver_error(data);
|
---|
408 | else
|
---|
409 | *dns = data->state.async.dns;
|
---|
410 |
|
---|
411 | destroy_async_data(&data->state.async);
|
---|
412 | }
|
---|
413 |
|
---|
414 | return result;
|
---|
415 | }
|
---|
416 |
|
---|
417 | /*
|
---|
418 | * Curl_resolver_wait_resolv()
|
---|
419 | *
|
---|
420 | * Waits for a resolve to finish. This function should be avoided since using
|
---|
421 | * this risk getting the multi interface to "hang".
|
---|
422 | *
|
---|
423 | * 'entry' MUST be non-NULL.
|
---|
424 | *
|
---|
425 | * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
|
---|
426 | * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
|
---|
427 | */
|
---|
428 | CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
|
---|
429 | struct Curl_dns_entry **entry)
|
---|
430 | {
|
---|
431 | CURLcode result = CURLE_OK;
|
---|
432 | timediff_t timeout;
|
---|
433 | struct curltime now = Curl_now();
|
---|
434 |
|
---|
435 | DEBUGASSERT(entry);
|
---|
436 | *entry = NULL; /* clear on entry */
|
---|
437 |
|
---|
438 | timeout = Curl_timeleft(data, &now, TRUE);
|
---|
439 | if(timeout < 0) {
|
---|
440 | /* already expired! */
|
---|
441 | connclose(data->conn, "Timed out before name resolve started");
|
---|
442 | return CURLE_OPERATION_TIMEDOUT;
|
---|
443 | }
|
---|
444 | if(!timeout)
|
---|
445 | timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
|
---|
446 |
|
---|
447 | /* Wait for the name resolve query to complete. */
|
---|
448 | while(!result) {
|
---|
449 | struct timeval *tvp, tv, store;
|
---|
450 | int itimeout;
|
---|
451 | timediff_t timeout_ms;
|
---|
452 |
|
---|
453 | #if TIMEDIFF_T_MAX > INT_MAX
|
---|
454 | itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
|
---|
455 | #else
|
---|
456 | itimeout = (int)timeout;
|
---|
457 | #endif
|
---|
458 |
|
---|
459 | store.tv_sec = itimeout/1000;
|
---|
460 | store.tv_usec = (itimeout%1000)*1000;
|
---|
461 |
|
---|
462 | tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
|
---|
463 |
|
---|
464 | /* use the timeout period ares returned to us above if less than one
|
---|
465 | second is left, otherwise just use 1000ms to make sure the progress
|
---|
466 | callback gets called frequent enough */
|
---|
467 | if(!tvp->tv_sec)
|
---|
468 | timeout_ms = (timediff_t)(tvp->tv_usec/1000);
|
---|
469 | else
|
---|
470 | timeout_ms = 1000;
|
---|
471 |
|
---|
472 | if(waitperform(data, timeout_ms) < 0)
|
---|
473 | return CURLE_UNRECOVERABLE_POLL;
|
---|
474 | result = Curl_resolver_is_resolved(data, entry);
|
---|
475 |
|
---|
476 | if(result || data->state.async.done)
|
---|
477 | break;
|
---|
478 |
|
---|
479 | if(Curl_pgrsUpdate(data))
|
---|
480 | result = CURLE_ABORTED_BY_CALLBACK;
|
---|
481 | else {
|
---|
482 | struct curltime now2 = Curl_now();
|
---|
483 | timediff_t timediff = Curl_timediff(now2, now); /* spent time */
|
---|
484 | if(timediff <= 0)
|
---|
485 | timeout -= 1; /* always deduct at least 1 */
|
---|
486 | else if(timediff > timeout)
|
---|
487 | timeout = -1;
|
---|
488 | else
|
---|
489 | timeout -= timediff;
|
---|
490 | now = now2; /* for next loop */
|
---|
491 | }
|
---|
492 | if(timeout < 0)
|
---|
493 | result = CURLE_OPERATION_TIMEDOUT;
|
---|
494 | }
|
---|
495 | if(result)
|
---|
496 | /* failure, so we cancel the ares operation */
|
---|
497 | ares_cancel((ares_channel)data->state.async.resolver);
|
---|
498 |
|
---|
499 | /* Operation complete, if the lookup was successful we now have the entry
|
---|
500 | in the cache. */
|
---|
501 | if(entry)
|
---|
502 | *entry = data->state.async.dns;
|
---|
503 |
|
---|
504 | if(result)
|
---|
505 | /* close the connection, since we can't return failure here without
|
---|
506 | cleaning up this connection properly. */
|
---|
507 | connclose(data->conn, "c-ares resolve failed");
|
---|
508 |
|
---|
509 | return result;
|
---|
510 | }
|
---|
511 |
|
---|
512 | #ifndef HAVE_CARES_GETADDRINFO
|
---|
513 |
|
---|
514 | /* Connects results to the list */
|
---|
515 | static void compound_results(struct thread_data *res,
|
---|
516 | struct Curl_addrinfo *ai)
|
---|
517 | {
|
---|
518 | if(!ai)
|
---|
519 | return;
|
---|
520 |
|
---|
521 | #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
|
---|
522 | if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
|
---|
523 | /* We have results already, put the new IPv6 entries at the head of the
|
---|
524 | list. */
|
---|
525 | struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
|
---|
526 |
|
---|
527 | while(temp_ai_tail->ai_next)
|
---|
528 | temp_ai_tail = temp_ai_tail->ai_next;
|
---|
529 |
|
---|
530 | temp_ai_tail->ai_next = ai;
|
---|
531 | }
|
---|
532 | else
|
---|
533 | #endif /* CURLRES_IPV6 */
|
---|
534 | {
|
---|
535 | /* Add the new results to the list of old results. */
|
---|
536 | struct Curl_addrinfo *ai_tail = ai;
|
---|
537 | while(ai_tail->ai_next)
|
---|
538 | ai_tail = ai_tail->ai_next;
|
---|
539 |
|
---|
540 | ai_tail->ai_next = res->temp_ai;
|
---|
541 | res->temp_ai = ai;
|
---|
542 | }
|
---|
543 | }
|
---|
544 |
|
---|
545 | /*
|
---|
546 | * ares_query_completed_cb() is the callback that ares will call when
|
---|
547 | * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
|
---|
548 | * when using ares, is completed either successfully or with failure.
|
---|
549 | */
|
---|
550 | static void query_completed_cb(void *arg, /* (struct connectdata *) */
|
---|
551 | int status,
|
---|
552 | #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
|
---|
553 | int timeouts,
|
---|
554 | #endif
|
---|
555 | struct hostent *hostent)
|
---|
556 | {
|
---|
557 | struct Curl_easy *data = (struct Curl_easy *)arg;
|
---|
558 | struct thread_data *res;
|
---|
559 |
|
---|
560 | #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
|
---|
561 | (void)timeouts; /* ignored */
|
---|
562 | #endif
|
---|
563 |
|
---|
564 | if(ARES_EDESTRUCTION == status)
|
---|
565 | /* when this ares handle is getting destroyed, the 'arg' pointer may not
|
---|
566 | be valid so only defer it when we know the 'status' says its fine! */
|
---|
567 | return;
|
---|
568 |
|
---|
569 | res = data->state.async.tdata;
|
---|
570 | if(res) {
|
---|
571 | res->num_pending--;
|
---|
572 |
|
---|
573 | if(CURL_ASYNC_SUCCESS == status) {
|
---|
574 | struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
|
---|
575 | if(ai) {
|
---|
576 | compound_results(res, ai);
|
---|
577 | }
|
---|
578 | }
|
---|
579 | /* A successful result overwrites any previous error */
|
---|
580 | if(res->last_status != ARES_SUCCESS)
|
---|
581 | res->last_status = status;
|
---|
582 |
|
---|
583 | /* If there are responses still pending, we presume they must be the
|
---|
584 | complementary IPv4 or IPv6 lookups that we started in parallel in
|
---|
585 | Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a
|
---|
586 | "definitive" response from one of a set of parallel queries, we need to
|
---|
587 | think about how long we're willing to wait for more responses. */
|
---|
588 | if(res->num_pending
|
---|
589 | /* Only these c-ares status values count as "definitive" for these
|
---|
590 | purposes. For example, ARES_ENODATA is what we expect when there is
|
---|
591 | no IPv6 entry for a domain name, and that's not a reason to get more
|
---|
592 | aggressive in our timeouts for the other response. Other errors are
|
---|
593 | either a result of bad input (which should affect all parallel
|
---|
594 | requests), local or network conditions, non-definitive server
|
---|
595 | responses, or us cancelling the request. */
|
---|
596 | && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
|
---|
597 | /* Right now, there can only be up to two parallel queries, so don't
|
---|
598 | bother handling any other cases. */
|
---|
599 | DEBUGASSERT(res->num_pending == 1);
|
---|
600 |
|
---|
601 | /* It's possible that one of these parallel queries could succeed
|
---|
602 | quickly, but the other could always fail or timeout (when we're
|
---|
603 | talking to a pool of DNS servers that can only successfully resolve
|
---|
604 | IPv4 address, for example).
|
---|
605 |
|
---|
606 | It's also possible that the other request could always just take
|
---|
607 | longer because it needs more time or only the second DNS server can
|
---|
608 | fulfill it successfully. But, to align with the philosophy of Happy
|
---|
609 | Eyeballs, we don't want to wait _too_ long or users will think
|
---|
610 | requests are slow when IPv6 lookups don't actually work (but IPv4 ones
|
---|
611 | do).
|
---|
612 |
|
---|
613 | So, now that we have a usable answer (some IPv4 addresses, some IPv6
|
---|
614 | addresses, or "no such domain"), we start a timeout for the remaining
|
---|
615 | pending responses. Even though it is typical that this resolved
|
---|
616 | request came back quickly, that needn't be the case. It might be that
|
---|
617 | this completing request didn't get a result from the first DNS server
|
---|
618 | or even the first round of the whole DNS server pool. So it could
|
---|
619 | already be quite some time after we issued the DNS queries in the
|
---|
620 | first place. Without modifying c-ares, we can't know exactly where in
|
---|
621 | its retry cycle we are. We could guess based on how much time has
|
---|
622 | gone by, but it doesn't really matter. Happy Eyeballs tells us that,
|
---|
623 | given usable information in hand, we simply don't want to wait "too
|
---|
624 | much longer" after we get a result.
|
---|
625 |
|
---|
626 | We simply wait an additional amount of time equal to the default
|
---|
627 | c-ares query timeout. That is enough time for a typical parallel
|
---|
628 | response to arrive without being "too long". Even on a network
|
---|
629 | where one of the two types of queries is failing or timing out
|
---|
630 | constantly, this will usually mean we wait a total of the default
|
---|
631 | c-ares timeout (5 seconds) plus the round trip time for the successful
|
---|
632 | request, which seems bearable. The downside is that c-ares might race
|
---|
633 | with us to issue one more retry just before we give up, but it seems
|
---|
634 | better to "waste" that request instead of trying to guess the perfect
|
---|
635 | timeout to prevent it. After all, we don't even know where in the
|
---|
636 | c-ares retry cycle each request is.
|
---|
637 | */
|
---|
638 | res->happy_eyeballs_dns_time = Curl_now();
|
---|
639 | Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
|
---|
640 | EXPIRE_HAPPY_EYEBALLS_DNS);
|
---|
641 | }
|
---|
642 | }
|
---|
643 | }
|
---|
644 | #else
|
---|
645 | /* c-ares 1.16.0 or later */
|
---|
646 |
|
---|
647 | /*
|
---|
648 | * ares2addr() converts an address list provided by c-ares to an internal
|
---|
649 | * libcurl compatible list
|
---|
650 | */
|
---|
651 | static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
|
---|
652 | {
|
---|
653 | /* traverse the ares_addrinfo_node list */
|
---|
654 | struct ares_addrinfo_node *ai;
|
---|
655 | struct Curl_addrinfo *cafirst = NULL;
|
---|
656 | struct Curl_addrinfo *calast = NULL;
|
---|
657 | int error = 0;
|
---|
658 |
|
---|
659 | for(ai = node; ai != NULL; ai = ai->ai_next) {
|
---|
660 | size_t ss_size;
|
---|
661 | struct Curl_addrinfo *ca;
|
---|
662 | /* ignore elements with unsupported address family, */
|
---|
663 | /* settle family-specific sockaddr structure size. */
|
---|
664 | if(ai->ai_family == AF_INET)
|
---|
665 | ss_size = sizeof(struct sockaddr_in);
|
---|
666 | #ifdef ENABLE_IPV6
|
---|
667 | else if(ai->ai_family == AF_INET6)
|
---|
668 | ss_size = sizeof(struct sockaddr_in6);
|
---|
669 | #endif
|
---|
670 | else
|
---|
671 | continue;
|
---|
672 |
|
---|
673 | /* ignore elements without required address info */
|
---|
674 | if(!ai->ai_addr || !(ai->ai_addrlen > 0))
|
---|
675 | continue;
|
---|
676 |
|
---|
677 | /* ignore elements with bogus address size */
|
---|
678 | if((size_t)ai->ai_addrlen < ss_size)
|
---|
679 | continue;
|
---|
680 |
|
---|
681 | ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
|
---|
682 | if(!ca) {
|
---|
683 | error = EAI_MEMORY;
|
---|
684 | break;
|
---|
685 | }
|
---|
686 |
|
---|
687 | /* copy each structure member individually, member ordering, */
|
---|
688 | /* size, or padding might be different for each platform. */
|
---|
689 |
|
---|
690 | ca->ai_flags = ai->ai_flags;
|
---|
691 | ca->ai_family = ai->ai_family;
|
---|
692 | ca->ai_socktype = ai->ai_socktype;
|
---|
693 | ca->ai_protocol = ai->ai_protocol;
|
---|
694 | ca->ai_addrlen = (curl_socklen_t)ss_size;
|
---|
695 | ca->ai_addr = NULL;
|
---|
696 | ca->ai_canonname = NULL;
|
---|
697 | ca->ai_next = NULL;
|
---|
698 |
|
---|
699 | ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
|
---|
700 | memcpy(ca->ai_addr, ai->ai_addr, ss_size);
|
---|
701 |
|
---|
702 | /* if the return list is empty, this becomes the first element */
|
---|
703 | if(!cafirst)
|
---|
704 | cafirst = ca;
|
---|
705 |
|
---|
706 | /* add this element last in the return list */
|
---|
707 | if(calast)
|
---|
708 | calast->ai_next = ca;
|
---|
709 | calast = ca;
|
---|
710 | }
|
---|
711 |
|
---|
712 | /* if we failed, destroy the Curl_addrinfo list */
|
---|
713 | if(error) {
|
---|
714 | Curl_freeaddrinfo(cafirst);
|
---|
715 | cafirst = NULL;
|
---|
716 | }
|
---|
717 |
|
---|
718 | return cafirst;
|
---|
719 | }
|
---|
720 |
|
---|
721 | static void addrinfo_cb(void *arg, int status, int timeouts,
|
---|
722 | struct ares_addrinfo *result)
|
---|
723 | {
|
---|
724 | struct Curl_easy *data = (struct Curl_easy *)arg;
|
---|
725 | struct thread_data *res = data->state.async.tdata;
|
---|
726 | (void)timeouts;
|
---|
727 | if(ARES_SUCCESS == status) {
|
---|
728 | res->temp_ai = ares2addr(result->nodes);
|
---|
729 | res->last_status = CURL_ASYNC_SUCCESS;
|
---|
730 | ares_freeaddrinfo(result);
|
---|
731 | }
|
---|
732 | res->num_pending--;
|
---|
733 | }
|
---|
734 |
|
---|
735 | #endif
|
---|
736 | /*
|
---|
737 | * Curl_resolver_getaddrinfo() - when using ares
|
---|
738 | *
|
---|
739 | * Returns name information about the given hostname and port number. If
|
---|
740 | * successful, the 'hostent' is returned and the fourth argument will point to
|
---|
741 | * memory we need to free after use. That memory *MUST* be freed with
|
---|
742 | * Curl_freeaddrinfo(), nothing else.
|
---|
743 | */
|
---|
744 | struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
---|
745 | const char *hostname,
|
---|
746 | int port,
|
---|
747 | int *waitp)
|
---|
748 | {
|
---|
749 | struct thread_data *res = NULL;
|
---|
750 | size_t namelen = strlen(hostname);
|
---|
751 | *waitp = 0; /* default to synchronous response */
|
---|
752 |
|
---|
753 | res = calloc(sizeof(struct thread_data) + namelen, 1);
|
---|
754 | if(res) {
|
---|
755 | strcpy(res->hostname, hostname);
|
---|
756 | data->state.async.hostname = res->hostname;
|
---|
757 | data->state.async.port = port;
|
---|
758 | data->state.async.done = FALSE; /* not done */
|
---|
759 | data->state.async.status = 0; /* clear */
|
---|
760 | data->state.async.dns = NULL; /* clear */
|
---|
761 | data->state.async.tdata = res;
|
---|
762 |
|
---|
763 | /* initial status - failed */
|
---|
764 | res->last_status = ARES_ENOTFOUND;
|
---|
765 |
|
---|
766 | #ifdef HAVE_CARES_GETADDRINFO
|
---|
767 | {
|
---|
768 | struct ares_addrinfo_hints hints;
|
---|
769 | char service[12];
|
---|
770 | int pf = PF_INET;
|
---|
771 | memset(&hints, 0, sizeof(hints));
|
---|
772 | #ifdef CURLRES_IPV6
|
---|
773 | if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
|
---|
774 | /* The stack seems to be IPv6-enabled */
|
---|
775 | pf = PF_UNSPEC;
|
---|
776 | #endif /* CURLRES_IPV6 */
|
---|
777 | hints.ai_family = pf;
|
---|
778 | hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
|
---|
779 | SOCK_STREAM : SOCK_DGRAM;
|
---|
780 | /* Since the service is a numerical one, set the hint flags
|
---|
781 | * accordingly to save a call to getservbyname in inside C-Ares
|
---|
782 | */
|
---|
783 | hints.ai_flags = ARES_AI_NUMERICSERV;
|
---|
784 | msnprintf(service, sizeof(service), "%d", port);
|
---|
785 | res->num_pending = 1;
|
---|
786 | ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
|
---|
787 | service, &hints, addrinfo_cb, data);
|
---|
788 | }
|
---|
789 | #else
|
---|
790 |
|
---|
791 | #ifdef HAVE_CARES_IPV6
|
---|
792 | if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
|
---|
793 | /* The stack seems to be IPv6-enabled */
|
---|
794 | res->num_pending = 2;
|
---|
795 |
|
---|
796 | /* areschannel is already setup in the Curl_open() function */
|
---|
797 | ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
|
---|
798 | PF_INET, query_completed_cb, data);
|
---|
799 | ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
|
---|
800 | PF_INET6, query_completed_cb, data);
|
---|
801 | }
|
---|
802 | else
|
---|
803 | #endif
|
---|
804 | {
|
---|
805 | res->num_pending = 1;
|
---|
806 |
|
---|
807 | /* areschannel is already setup in the Curl_open() function */
|
---|
808 | ares_gethostbyname((ares_channel)data->state.async.resolver,
|
---|
809 | hostname, PF_INET,
|
---|
810 | query_completed_cb, data);
|
---|
811 | }
|
---|
812 | #endif
|
---|
813 | *waitp = 1; /* expect asynchronous response */
|
---|
814 | }
|
---|
815 | return NULL; /* no struct yet */
|
---|
816 | }
|
---|
817 |
|
---|
818 | CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
---|
819 | char *servers)
|
---|
820 | {
|
---|
821 | CURLcode result = CURLE_NOT_BUILT_IN;
|
---|
822 | int ares_result;
|
---|
823 |
|
---|
824 | /* If server is NULL or empty, this would purge all DNS servers
|
---|
825 | * from ares library, which will cause any and all queries to fail.
|
---|
826 | * So, just return OK if none are configured and don't actually make
|
---|
827 | * any changes to c-ares. This lets c-ares use it's defaults, which
|
---|
828 | * it gets from the OS (for instance from /etc/resolv.conf on Linux).
|
---|
829 | */
|
---|
830 | if(!(servers && servers[0]))
|
---|
831 | return CURLE_OK;
|
---|
832 |
|
---|
833 | #ifdef HAVE_CARES_SERVERS_CSV
|
---|
834 | #ifdef HAVE_CARES_PORTS_CSV
|
---|
835 | ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
|
---|
836 | servers);
|
---|
837 | #else
|
---|
838 | ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
|
---|
839 | #endif
|
---|
840 | switch(ares_result) {
|
---|
841 | case ARES_SUCCESS:
|
---|
842 | result = CURLE_OK;
|
---|
843 | break;
|
---|
844 | case ARES_ENOMEM:
|
---|
845 | result = CURLE_OUT_OF_MEMORY;
|
---|
846 | break;
|
---|
847 | case ARES_ENOTINITIALIZED:
|
---|
848 | case ARES_ENODATA:
|
---|
849 | case ARES_EBADSTR:
|
---|
850 | default:
|
---|
851 | result = CURLE_BAD_FUNCTION_ARGUMENT;
|
---|
852 | break;
|
---|
853 | }
|
---|
854 | #else /* too old c-ares version! */
|
---|
855 | (void)data;
|
---|
856 | (void)(ares_result);
|
---|
857 | #endif
|
---|
858 | return result;
|
---|
859 | }
|
---|
860 |
|
---|
861 | CURLcode Curl_set_dns_interface(struct Curl_easy *data,
|
---|
862 | const char *interf)
|
---|
863 | {
|
---|
864 | #ifdef HAVE_CARES_LOCAL_DEV
|
---|
865 | if(!interf)
|
---|
866 | interf = "";
|
---|
867 |
|
---|
868 | ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
|
---|
869 |
|
---|
870 | return CURLE_OK;
|
---|
871 | #else /* c-ares version too old! */
|
---|
872 | (void)data;
|
---|
873 | (void)interf;
|
---|
874 | return CURLE_NOT_BUILT_IN;
|
---|
875 | #endif
|
---|
876 | }
|
---|
877 |
|
---|
878 | CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
|
---|
879 | const char *local_ip4)
|
---|
880 | {
|
---|
881 | #ifdef HAVE_CARES_SET_LOCAL
|
---|
882 | struct in_addr a4;
|
---|
883 |
|
---|
884 | if((!local_ip4) || (local_ip4[0] == 0)) {
|
---|
885 | a4.s_addr = 0; /* disabled: do not bind to a specific address */
|
---|
886 | }
|
---|
887 | else {
|
---|
888 | if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
|
---|
889 | return CURLE_BAD_FUNCTION_ARGUMENT;
|
---|
890 | }
|
---|
891 | }
|
---|
892 |
|
---|
893 | ares_set_local_ip4((ares_channel)data->state.async.resolver,
|
---|
894 | ntohl(a4.s_addr));
|
---|
895 |
|
---|
896 | return CURLE_OK;
|
---|
897 | #else /* c-ares version too old! */
|
---|
898 | (void)data;
|
---|
899 | (void)local_ip4;
|
---|
900 | return CURLE_NOT_BUILT_IN;
|
---|
901 | #endif
|
---|
902 | }
|
---|
903 |
|
---|
904 | CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
|
---|
905 | const char *local_ip6)
|
---|
906 | {
|
---|
907 | #if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
|
---|
908 | unsigned char a6[INET6_ADDRSTRLEN];
|
---|
909 |
|
---|
910 | if((!local_ip6) || (local_ip6[0] == 0)) {
|
---|
911 | /* disabled: do not bind to a specific address */
|
---|
912 | memset(a6, 0, sizeof(a6));
|
---|
913 | }
|
---|
914 | else {
|
---|
915 | if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
|
---|
916 | return CURLE_BAD_FUNCTION_ARGUMENT;
|
---|
917 | }
|
---|
918 | }
|
---|
919 |
|
---|
920 | ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
|
---|
921 |
|
---|
922 | return CURLE_OK;
|
---|
923 | #else /* c-ares version too old! */
|
---|
924 | (void)data;
|
---|
925 | (void)local_ip6;
|
---|
926 | return CURLE_NOT_BUILT_IN;
|
---|
927 | #endif
|
---|
928 | }
|
---|
929 | #endif /* CURLRES_ARES */
|
---|