source: asp3_tinet_ecnl_rx/trunk/curl-7.57.0/lib/asyn-ares.c@ 337

Last change on this file since 337 was 337, checked in by coas-nagasima, 6 years ago

ASP3版ECNLを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 19.7 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2017, 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#ifdef HAVE_LIMITS_H
34#include <limits.h>
35#endif
36#ifdef HAVE_NETINET_IN_H
37#include <netinet/in.h>
38#endif
39#ifdef HAVE_NETDB_H
40#include <netdb.h>
41#endif
42#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
45#ifdef __VMS
46#include <in.h>
47#include <inet.h>
48#endif
49
50#ifdef HAVE_PROCESS_H
51#include <process.h>
52#endif
53
54#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
55#undef in_addr_t
56#define in_addr_t unsigned long
57#endif
58
59#include "urldata.h"
60#include "sendf.h"
61#include "hostip.h"
62#include "hash.h"
63#include "share.h"
64#include "strerror.h"
65#include "url.h"
66#include "multiif.h"
67#include "inet_pton.h"
68#include "connect.h"
69#include "select.h"
70#include "progress.h"
71
72# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
73 (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
74# define CARES_STATICLIB
75# endif
76# include <ares.h>
77# include <ares_version.h> /* really old c-ares didn't include this by
78 itself */
79
80#if ARES_VERSION >= 0x010500
81/* c-ares 1.5.0 or later, the callback proto is modified */
82#define HAVE_CARES_CALLBACK_TIMEOUTS 1
83#endif
84
85/* The last 3 #include files should be in this order */
86#include "curl_printf.h"
87#include "curl_memory.h"
88#include "memdebug.h"
89
90struct ResolverResults {
91 int num_pending; /* number of ares_gethostbyname() requests */
92 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
93 int last_status;
94};
95
96/*
97 * Curl_resolver_global_init() - the generic low-level asynchronous name
98 * resolve API. Called from curl_global_init() to initialize global resolver
99 * environment. Initializes ares library.
100 */
101int Curl_resolver_global_init(void)
102{
103#ifdef CARES_HAVE_ARES_LIBRARY_INIT
104 if(ares_library_init(ARES_LIB_INIT_ALL)) {
105 return CURLE_FAILED_INIT;
106 }
107#endif
108 return CURLE_OK;
109}
110
111/*
112 * Curl_resolver_global_cleanup()
113 *
114 * Called from curl_global_cleanup() to destroy global resolver environment.
115 * Deinitializes ares library.
116 */
117void Curl_resolver_global_cleanup(void)
118{
119#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
120 ares_library_cleanup();
121#endif
122}
123
124/*
125 * Curl_resolver_init()
126 *
127 * Called from curl_easy_init() -> Curl_open() to initialize resolver
128 * URL-state specific environment ('resolver' member of the UrlState
129 * structure). Fills the passed pointer by the initialized ares_channel.
130 */
131CURLcode Curl_resolver_init(void **resolver)
132{
133 int status = ares_init((ares_channel*)resolver);
134 if(status != ARES_SUCCESS) {
135 if(status == ARES_ENOMEM)
136 return CURLE_OUT_OF_MEMORY;
137 else
138 return CURLE_FAILED_INIT;
139 }
140 return CURLE_OK;
141 /* make sure that all other returns from this function should destroy the
142 ares channel before returning error! */
143}
144
145/*
146 * Curl_resolver_cleanup()
147 *
148 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
149 * URL-state specific environment ('resolver' member of the UrlState
150 * structure). Destroys the ares channel.
151 */
152void Curl_resolver_cleanup(void *resolver)
153{
154 ares_destroy((ares_channel)resolver);
155}
156
157/*
158 * Curl_resolver_duphandle()
159 *
160 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
161 * environment ('resolver' member of the UrlState structure). Duplicates the
162 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
163 */
164int Curl_resolver_duphandle(void **to, void *from)
165{
166 /* Clone the ares channel for the new handle */
167 if(ARES_SUCCESS != ares_dup((ares_channel*)to, (ares_channel)from))
168 return CURLE_FAILED_INIT;
169 return CURLE_OK;
170}
171
172static void destroy_async_data(struct Curl_async *async);
173
174/*
175 * Cancel all possibly still on-going resolves for this connection.
176 */
177void Curl_resolver_cancel(struct connectdata *conn)
178{
179 if(conn->data && conn->data->state.resolver)
180 ares_cancel((ares_channel)conn->data->state.resolver);
181 destroy_async_data(&conn->async);
182}
183
184/*
185 * destroy_async_data() cleans up async resolver data.
186 */
187static void destroy_async_data(struct Curl_async *async)
188{
189 free(async->hostname);
190
191 if(async->os_specific) {
192 struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
193 if(res) {
194 if(res->temp_ai) {
195 Curl_freeaddrinfo(res->temp_ai);
196 res->temp_ai = NULL;
197 }
198 free(res);
199 }
200 async->os_specific = NULL;
201 }
202
203 async->hostname = NULL;
204}
205
206/*
207 * Curl_resolver_getsock() is called when someone from the outside world
208 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
209 * with ares. The caller must make sure that this function is only called when
210 * we have a working ares channel.
211 *
212 * Returns: sockets-in-use-bitmap
213 */
214
215int Curl_resolver_getsock(struct connectdata *conn,
216 curl_socket_t *socks,
217 int numsocks)
218
219{
220 struct timeval maxtime;
221 struct timeval timebuf;
222 struct timeval *timeout;
223 long milli;
224 int max = ares_getsock((ares_channel)conn->data->state.resolver,
225 (ares_socket_t *)socks, numsocks);
226
227 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
228 maxtime.tv_usec = 0;
229
230 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
231 &timebuf);
232 milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
233 if(milli == 0)
234 milli += 10;
235 Curl_expire(conn->data, milli, EXPIRE_ASYNC_NAME);
236
237 return max;
238}
239
240/*
241 * waitperform()
242 *
243 * 1) Ask ares what sockets it currently plays with, then
244 * 2) wait for the timeout period to check for action on ares' sockets.
245 * 3) tell ares to act on all the sockets marked as "with action"
246 *
247 * return number of sockets it worked on
248 */
249
250static int waitperform(struct connectdata *conn, int timeout_ms)
251{
252 struct Curl_easy *data = conn->data;
253 int nfds;
254 int bitmask;
255 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
256 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
257 int i;
258 int num = 0;
259
260 bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
261 ARES_GETSOCK_MAXNUM);
262
263 for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
264 pfd[i].events = 0;
265 pfd[i].revents = 0;
266 if(ARES_GETSOCK_READABLE(bitmask, i)) {
267 pfd[i].fd = socks[i];
268 pfd[i].events |= POLLRDNORM|POLLIN;
269 }
270 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
271 pfd[i].fd = socks[i];
272 pfd[i].events |= POLLWRNORM|POLLOUT;
273 }
274 if(pfd[i].events != 0)
275 num++;
276 else
277 break;
278 }
279
280 if(num)
281 nfds = Curl_poll(pfd, num, timeout_ms);
282 else
283 nfds = 0;
284
285 if(!nfds)
286 /* Call ares_process() unconditonally here, even if we simply timed out
287 above, as otherwise the ares name resolve won't timeout! */
288 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
289 ARES_SOCKET_BAD);
290 else {
291 /* move through the descriptors and ask for processing on them */
292 for(i = 0; i < num; i++)
293 ares_process_fd((ares_channel)data->state.resolver,
294 pfd[i].revents & (POLLRDNORM|POLLIN)?
295 pfd[i].fd:ARES_SOCKET_BAD,
296 pfd[i].revents & (POLLWRNORM|POLLOUT)?
297 pfd[i].fd:ARES_SOCKET_BAD);
298 }
299 return nfds;
300}
301
302/*
303 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
304 * name resolve request has completed. It should also make sure to time-out if
305 * the operation seems to take too long.
306 *
307 * Returns normal CURLcode errors.
308 */
309CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
310 struct Curl_dns_entry **dns)
311{
312 struct Curl_easy *data = conn->data;
313 struct ResolverResults *res = (struct ResolverResults *)
314 conn->async.os_specific;
315 CURLcode result = CURLE_OK;
316
317 *dns = NULL;
318
319 waitperform(conn, 0);
320
321 if(res && !res->num_pending) {
322 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
323 /* temp_ai ownership is moved to the connection, so we need not free-up
324 them */
325 res->temp_ai = NULL;
326 if(!conn->async.dns) {
327 failf(data, "Could not resolve: %s (%s)",
328 conn->async.hostname, ares_strerror(conn->async.status));
329 result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
330 CURLE_COULDNT_RESOLVE_HOST;
331 }
332 else
333 *dns = conn->async.dns;
334
335 destroy_async_data(&conn->async);
336 }
337
338 return result;
339}
340
341/*
342 * Curl_resolver_wait_resolv()
343 *
344 * waits for a resolve to finish. This function should be avoided since using
345 * this risk getting the multi interface to "hang".
346 *
347 * If 'entry' is non-NULL, make it point to the resolved dns entry
348 *
349 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
350 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
351 */
352CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
353 struct Curl_dns_entry **entry)
354{
355 CURLcode result = CURLE_OK;
356 struct Curl_easy *data = conn->data;
357 timediff_t timeout;
358 struct curltime now = Curl_now();
359 struct Curl_dns_entry *temp_entry;
360
361 if(entry)
362 *entry = NULL; /* clear on entry */
363
364 timeout = Curl_timeleft(data, &now, TRUE);
365 if(timeout < 0) {
366 /* already expired! */
367 connclose(conn, "Timed out before name resolve started");
368 return CURLE_OPERATION_TIMEDOUT;
369 }
370 if(!timeout)
371 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
372
373 /* Wait for the name resolve query to complete. */
374 while(!result) {
375 struct timeval *tvp, tv, store;
376 int itimeout;
377 int timeout_ms;
378
379 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
380
381 store.tv_sec = itimeout/1000;
382 store.tv_usec = (itimeout%1000)*1000;
383
384 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
385
386 /* use the timeout period ares returned to us above if less than one
387 second is left, otherwise just use 1000ms to make sure the progress
388 callback gets called frequent enough */
389 if(!tvp->tv_sec)
390 timeout_ms = (int)(tvp->tv_usec/1000);
391 else
392 timeout_ms = 1000;
393
394 waitperform(conn, timeout_ms);
395 result = Curl_resolver_is_resolved(conn, &temp_entry);
396
397 if(result || conn->async.done)
398 break;
399
400 if(Curl_pgrsUpdate(conn))
401 result = CURLE_ABORTED_BY_CALLBACK;
402 else {
403 struct curltime now2 = Curl_now();
404 timediff_t timediff = Curl_timediff(now2, now); /* spent time */
405 if(timediff <= 0)
406 timeout -= 1; /* always deduct at least 1 */
407 else if(timediff > timeout)
408 timeout = -1;
409 else
410 timeout -= (long)timediff;
411 now = now2; /* for next loop */
412 }
413 if(timeout < 0)
414 result = CURLE_OPERATION_TIMEDOUT;
415 }
416 if(result)
417 /* failure, so we cancel the ares operation */
418 ares_cancel((ares_channel)data->state.resolver);
419
420 /* Operation complete, if the lookup was successful we now have the entry
421 in the cache. */
422 if(entry)
423 *entry = conn->async.dns;
424
425 if(result)
426 /* close the connection, since we can't return failure here without
427 cleaning up this connection properly.
428 TODO: remove this action from here, it is not a name resolver decision.
429 */
430 connclose(conn, "c-ares resolve failed");
431
432 return result;
433}
434
435/* Connects results to the list */
436static void compound_results(struct ResolverResults *res,
437 Curl_addrinfo *ai)
438{
439 Curl_addrinfo *ai_tail;
440 if(!ai)
441 return;
442 ai_tail = ai;
443
444 while(ai_tail->ai_next)
445 ai_tail = ai_tail->ai_next;
446
447 /* Add the new results to the list of old results. */
448 ai_tail->ai_next = res->temp_ai;
449 res->temp_ai = ai;
450}
451
452/*
453 * ares_query_completed_cb() is the callback that ares will call when
454 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
455 * when using ares, is completed either successfully or with failure.
456 */
457static void query_completed_cb(void *arg, /* (struct connectdata *) */
458 int status,
459#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
460 int timeouts,
461#endif
462 struct hostent *hostent)
463{
464 struct connectdata *conn = (struct connectdata *)arg;
465 struct ResolverResults *res;
466
467#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
468 (void)timeouts; /* ignored */
469#endif
470
471 if(ARES_EDESTRUCTION == status)
472 /* when this ares handle is getting destroyed, the 'arg' pointer may not
473 be valid so only defer it when we know the 'status' says its fine! */
474 return;
475
476 res = (struct ResolverResults *)conn->async.os_specific;
477 res->num_pending--;
478
479 if(CURL_ASYNC_SUCCESS == status) {
480 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
481 if(ai) {
482 compound_results(res, ai);
483 }
484 }
485 /* A successful result overwrites any previous error */
486 if(res->last_status != ARES_SUCCESS)
487 res->last_status = status;
488}
489
490/*
491 * Curl_resolver_getaddrinfo() - when using ares
492 *
493 * Returns name information about the given hostname and port number. If
494 * successful, the 'hostent' is returned and the forth argument will point to
495 * memory we need to free after use. That memory *MUST* be freed with
496 * Curl_freeaddrinfo(), nothing else.
497 */
498Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
499 const char *hostname,
500 int port,
501 int *waitp)
502{
503 char *bufp;
504 struct Curl_easy *data = conn->data;
505 struct in_addr in;
506 int family = PF_INET;
507#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
508 struct in6_addr in6;
509#endif /* CURLRES_IPV6 */
510
511 *waitp = 0; /* default to synchronous response */
512
513 /* First check if this is an IPv4 address string */
514 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
515 /* This is a dotted IP address 123.123.123.123-style */
516 return Curl_ip2addr(AF_INET, &in, hostname, port);
517 }
518
519#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
520 /* Otherwise, check if this is an IPv6 address string */
521 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
522 /* This must be an IPv6 address literal. */
523 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
524
525 switch(conn->ip_version) {
526 default:
527#if ARES_VERSION >= 0x010601
528 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
529 c-ares versions this just falls through and defaults
530 to PF_INET */
531 break;
532#endif
533 case CURL_IPRESOLVE_V4:
534 family = PF_INET;
535 break;
536 case CURL_IPRESOLVE_V6:
537 family = PF_INET6;
538 break;
539 }
540#endif /* CURLRES_IPV6 */
541
542 bufp = strdup(hostname);
543 if(bufp) {
544 struct ResolverResults *res = NULL;
545 free(conn->async.hostname);
546 conn->async.hostname = bufp;
547 conn->async.port = port;
548 conn->async.done = FALSE; /* not done */
549 conn->async.status = 0; /* clear */
550 conn->async.dns = NULL; /* clear */
551 res = calloc(sizeof(struct ResolverResults), 1);
552 if(!res) {
553 free(conn->async.hostname);
554 conn->async.hostname = NULL;
555 return NULL;
556 }
557 conn->async.os_specific = res;
558
559 /* initial status - failed */
560 res->last_status = ARES_ENOTFOUND;
561#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
562 if(family == PF_UNSPEC) {
563 if(Curl_ipv6works()) {
564 res->num_pending = 2;
565
566 /* areschannel is already setup in the Curl_open() function */
567 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
568 PF_INET, query_completed_cb, conn);
569 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
570 PF_INET6, query_completed_cb, conn);
571 }
572 else {
573 res->num_pending = 1;
574
575 /* areschannel is already setup in the Curl_open() function */
576 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
577 PF_INET, query_completed_cb, conn);
578 }
579 }
580 else
581#endif /* CURLRES_IPV6 */
582 {
583 res->num_pending = 1;
584
585 /* areschannel is already setup in the Curl_open() function */
586 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
587 query_completed_cb, conn);
588 }
589
590 *waitp = 1; /* expect asynchronous response */
591 }
592 return NULL; /* no struct yet */
593}
594
595CURLcode Curl_set_dns_servers(struct Curl_easy *data,
596 char *servers)
597{
598 CURLcode result = CURLE_NOT_BUILT_IN;
599 int ares_result;
600
601 /* If server is NULL or empty, this would purge all DNS servers
602 * from ares library, which will cause any and all queries to fail.
603 * So, just return OK if none are configured and don't actually make
604 * any changes to c-ares. This lets c-ares use it's defaults, which
605 * it gets from the OS (for instance from /etc/resolv.conf on Linux).
606 */
607 if(!(servers && servers[0]))
608 return CURLE_OK;
609
610#if (ARES_VERSION >= 0x010704)
611 ares_result = ares_set_servers_csv(data->state.resolver, servers);
612 switch(ares_result) {
613 case ARES_SUCCESS:
614 result = CURLE_OK;
615 break;
616 case ARES_ENOMEM:
617 result = CURLE_OUT_OF_MEMORY;
618 break;
619 case ARES_ENOTINITIALIZED:
620 case ARES_ENODATA:
621 case ARES_EBADSTR:
622 default:
623 result = CURLE_BAD_FUNCTION_ARGUMENT;
624 break;
625 }
626#else /* too old c-ares version! */
627 (void)data;
628 (void)(ares_result);
629#endif
630 return result;
631}
632
633CURLcode Curl_set_dns_interface(struct Curl_easy *data,
634 const char *interf)
635{
636#if (ARES_VERSION >= 0x010704)
637 if(!interf)
638 interf = "";
639
640 ares_set_local_dev((ares_channel)data->state.resolver, interf);
641
642 return CURLE_OK;
643#else /* c-ares version too old! */
644 (void)data;
645 (void)interf;
646 return CURLE_NOT_BUILT_IN;
647#endif
648}
649
650CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
651 const char *local_ip4)
652{
653#if (ARES_VERSION >= 0x010704)
654 struct in_addr a4;
655
656 if((!local_ip4) || (local_ip4[0] == 0)) {
657 a4.s_addr = 0; /* disabled: do not bind to a specific address */
658 }
659 else {
660 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
661 return CURLE_BAD_FUNCTION_ARGUMENT;
662 }
663 }
664
665 ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
666
667 return CURLE_OK;
668#else /* c-ares version too old! */
669 (void)data;
670 (void)local_ip4;
671 return CURLE_NOT_BUILT_IN;
672#endif
673}
674
675CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
676 const char *local_ip6)
677{
678#if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
679 unsigned char a6[INET6_ADDRSTRLEN];
680
681 if((!local_ip6) || (local_ip6[0] == 0)) {
682 /* disabled: do not bind to a specific address */
683 memset(a6, 0, sizeof(a6));
684 }
685 else {
686 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
687 return CURLE_BAD_FUNCTION_ARGUMENT;
688 }
689 }
690
691 ares_set_local_ip6((ares_channel)data->state.resolver, a6);
692
693 return CURLE_OK;
694#else /* c-ares version too old! */
695 (void)data;
696 (void)local_ip6;
697 return CURLE_NOT_BUILT_IN;
698#endif
699}
700#endif /* CURLRES_ARES */
Note: See TracBrowser for help on using the repository browser.