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