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