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