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