VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/asyn-ares.c@ 97138

最後變更 在這個檔案從97138是 95312,由 vboxsync 提交於 3 年 前

libs/{curl,libxml2}: OSE export fixes, bugref:8515

  • 屬性 svn:eol-style 設為 native
檔案大小: 28.1 KB
 
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
108struct 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 */
134int 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 */
150void Curl_resolver_global_cleanup(void)
151{
152#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
153 ares_library_cleanup();
154#endif
155}
156
157
158static 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 */
175CURLcode 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 */
201void 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 */
213CURLcode 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
224static void destroy_async_data(struct Curl_async *async);
225
226/*
227 * Cancel all possibly still on-going resolves for this connection.
228 */
229void 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 */
241void 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 */
251static 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
279int 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
312static 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 */
370CURLcode 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 */
434CURLcode 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 */
520static 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 */
555static 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 */
656static 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
726static 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 */
749struct 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
826CURLcode 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
869CURLcode 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
886CURLcode 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
912CURLcode 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 */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette