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