Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/lib/asyn-ares.c
2065 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 "sendf.h"
52
#include "hostip.h"
53
#include "hash.h"
54
#include "share.h"
55
#include "url.h"
56
#include "multiif.h"
57
#include "curlx/inet_pton.h"
58
#include "connect.h"
59
#include "select.h"
60
#include "progress.h"
61
#include "curlx/timediff.h"
62
#include "httpsrr.h"
63
#include "strdup.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
#endif
88
89
#ifdef USE_HTTPSRR
90
#if ARES_VERSION < 0x011c00
91
#error "requires c-ares 1.28.0 or newer for HTTPSRR"
92
#endif
93
#define HTTPSRR_WORKS
94
#endif
95
96
/* The last 3 #include files should be in this order */
97
#include "curl_printf.h"
98
#include "curl_memory.h"
99
#include "memdebug.h"
100
101
/* How long we are willing to wait for additional parallel responses after
102
obtaining a "definitive" one. For old c-ares without getaddrinfo.
103
104
This is intended to equal the c-ares default timeout. cURL always uses that
105
default value. Unfortunately, c-ares does not expose its default timeout in
106
its API, but it is officially documented as 5 seconds.
107
108
See query_completed_cb() for an explanation of how this is used.
109
*/
110
#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
111
112
#define CARES_TIMEOUT_PER_ATTEMPT 2000
113
114
static int ares_ver = 0;
115
116
static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
117
bool reset_on_null);
118
119
/*
120
* Curl_async_global_init() - the generic low-level asynchronous name
121
* resolve API. Called from curl_global_init() to initialize global resolver
122
* environment. Initializes ares library.
123
*/
124
int Curl_async_global_init(void)
125
{
126
#ifdef CARES_HAVE_ARES_LIBRARY_INIT
127
if(ares_library_init(ARES_LIB_INIT_ALL)) {
128
return CURLE_FAILED_INIT;
129
}
130
#endif
131
ares_version(&ares_ver);
132
return CURLE_OK;
133
}
134
135
/*
136
* Curl_async_global_cleanup()
137
*
138
* Called from curl_global_cleanup() to destroy global resolver environment.
139
* Deinitializes ares library.
140
*/
141
void Curl_async_global_cleanup(void)
142
{
143
#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
144
ares_library_cleanup();
145
#endif
146
}
147
148
149
static void sock_state_cb(void *data, ares_socket_t socket_fd,
150
int readable, int writable)
151
{
152
struct Curl_easy *easy = data;
153
if(!readable && !writable) {
154
DEBUGASSERT(easy);
155
Curl_multi_will_close(easy, socket_fd);
156
}
157
}
158
159
static CURLcode async_ares_init(struct Curl_easy *data)
160
{
161
struct async_ares_ctx *ares = &data->state.async.ares;
162
int status;
163
struct ares_options options;
164
int optmask = ARES_OPT_SOCK_STATE_CB;
165
CURLcode rc = CURLE_OK;
166
167
options.sock_state_cb = sock_state_cb;
168
options.sock_state_cb_data = data;
169
170
DEBUGASSERT(!ares->channel);
171
/*
172
if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
173
174
if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
175
to set the timeout value;
176
177
if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
178
overwrite c-ares' timeout.
179
*/
180
DEBUGASSERT(ares_ver);
181
if(ares_ver < 0x011400) {
182
options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
183
optmask |= ARES_OPT_TIMEOUTMS;
184
}
185
186
status = ares_init_options(&ares->channel, &options, optmask);
187
if(status != ARES_SUCCESS) {
188
ares->channel = NULL;
189
rc = (status == ARES_ENOMEM) ?
190
CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
191
goto out;
192
}
193
194
rc = async_ares_set_dns_servers(data, FALSE);
195
if(rc && rc != CURLE_NOT_BUILT_IN)
196
goto out;
197
198
rc = Curl_async_ares_set_dns_interface(data);
199
if(rc && rc != CURLE_NOT_BUILT_IN)
200
goto out;
201
202
rc = Curl_async_ares_set_dns_local_ip4(data);
203
if(rc && rc != CURLE_NOT_BUILT_IN)
204
goto out;
205
206
rc = Curl_async_ares_set_dns_local_ip6(data);
207
if(rc && rc != CURLE_NOT_BUILT_IN)
208
goto out;
209
210
rc = CURLE_OK;
211
212
out:
213
if(rc && ares->channel) {
214
ares_destroy(ares->channel);
215
ares->channel = NULL;
216
}
217
return rc;
218
}
219
220
static CURLcode async_ares_init_lazy(struct Curl_easy *data)
221
{
222
struct async_ares_ctx *ares = &data->state.async.ares;
223
if(!ares->channel)
224
return async_ares_init(data);
225
return CURLE_OK;
226
}
227
228
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
229
{
230
struct async_ares_ctx *ares = &data->state.async.ares;
231
CURLcode result = CURLE_OK;
232
if(!ares->channel) {
233
result = async_ares_init(data);
234
}
235
*impl = ares->channel;
236
return result;
237
}
238
239
static void async_ares_cleanup(struct Curl_easy *data);
240
241
void Curl_async_ares_shutdown(struct Curl_easy *data)
242
{
243
struct async_ares_ctx *ares = &data->state.async.ares;
244
if(ares->channel)
245
ares_cancel(ares->channel);
246
async_ares_cleanup(data);
247
}
248
249
void Curl_async_ares_destroy(struct Curl_easy *data)
250
{
251
struct async_ares_ctx *ares = &data->state.async.ares;
252
Curl_async_ares_shutdown(data);
253
if(ares->channel) {
254
ares_destroy(ares->channel);
255
ares->channel = NULL;
256
}
257
}
258
259
/*
260
* async_ares_cleanup() cleans up async resolver data.
261
*/
262
static void async_ares_cleanup(struct Curl_easy *data)
263
{
264
struct async_ares_ctx *ares = &data->state.async.ares;
265
if(ares->temp_ai) {
266
Curl_freeaddrinfo(ares->temp_ai);
267
ares->temp_ai = NULL;
268
}
269
#ifdef USE_HTTPSRR
270
Curl_httpsrr_cleanup(&ares->hinfo);
271
#endif
272
}
273
274
/*
275
* Curl_async_getsock() is called when someone from the outside world
276
* (using curl_multi_fdset()) wants to get our fd_set setup.
277
*/
278
279
int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
280
{
281
struct async_ares_ctx *ares = &data->state.async.ares;
282
DEBUGASSERT(ares->channel);
283
return Curl_ares_getsock(data, ares->channel, socks);
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 CURLE_OK;
305
}
306
307
if(Curl_ares_perform(ares->channel, 0) < 0)
308
return CURLE_UNRECOVERABLE_POLL;
309
310
#ifndef HAVE_CARES_GETADDRINFO
311
/* Now that we have checked for any last minute results above, see if there
312
are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
313
expires. */
314
if(ares->num_pending
315
/* This is only set to non-zero if the timer was started. */
316
&& (ares->happy_eyeballs_dns_time.tv_sec
317
|| ares->happy_eyeballs_dns_time.tv_usec)
318
&& (curlx_timediff(curlx_now(), ares->happy_eyeballs_dns_time)
319
>= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
320
/* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
321
running. */
322
memset(&ares->happy_eyeballs_dns_time, 0,
323
sizeof(ares->happy_eyeballs_dns_time));
324
325
/* Cancel the raw c-ares request, which will fire query_completed_cb() with
326
ARES_ECANCELLED synchronously for all pending responses. This will
327
leave us with res->num_pending == 0, which is perfect for the next
328
block. */
329
ares_cancel(ares->channel);
330
DEBUGASSERT(ares->num_pending == 0);
331
}
332
#endif
333
334
if(!ares->num_pending) {
335
/* all c-ares operations done, what is the result to report? */
336
Curl_resolv_unlink(data, &data->state.async.dns);
337
data->state.async.done = TRUE;
338
result = ares->result;
339
if(ares->last_status == CURL_ASYNC_SUCCESS && !result) {
340
data->state.async.dns =
341
Curl_dnscache_mk_entry(data, ares->temp_ai,
342
data->state.async.hostname, 0,
343
data->state.async.port, FALSE);
344
ares->temp_ai = NULL; /* temp_ai now owned by entry */
345
#ifdef HTTPSRR_WORKS
346
if(data->state.async.dns) {
347
struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
348
if(!lhrr)
349
result = CURLE_OUT_OF_MEMORY;
350
else
351
data->state.async.dns->hinfo = lhrr;
352
}
353
#endif
354
if(!result && data->state.async.dns)
355
result = Curl_dnscache_add(data, data->state.async.dns);
356
}
357
/* if we have not found anything, report the proper
358
* CURLE_COULDNT_RESOLVE_* code */
359
if(!result && !data->state.async.dns)
360
result = Curl_resolver_error(data);
361
if(result)
362
Curl_resolv_unlink(data, &data->state.async.dns);
363
*dns = data->state.async.dns;
364
CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
365
result, *dns ? "" : "not ");
366
async_ares_cleanup(data);
367
}
368
return result;
369
}
370
371
/*
372
* Curl_async_await()
373
*
374
* Waits for a resolve to finish. This function should be avoided since using
375
* this risk getting the multi interface to "hang".
376
*
377
* 'entry' MUST be non-NULL.
378
*
379
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
380
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
381
*/
382
CURLcode Curl_async_await(struct Curl_easy *data,
383
struct Curl_dns_entry **entry)
384
{
385
struct async_ares_ctx *ares = &data->state.async.ares;
386
CURLcode result = CURLE_OK;
387
timediff_t timeout;
388
struct curltime now = curlx_now();
389
390
DEBUGASSERT(entry);
391
*entry = NULL; /* clear on entry */
392
393
timeout = Curl_timeleft(data, &now, TRUE);
394
if(timeout < 0) {
395
/* already expired! */
396
connclose(data->conn, "Timed out before name resolve started");
397
return CURLE_OPERATION_TIMEDOUT;
398
}
399
if(!timeout)
400
timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
401
402
/* Wait for the name resolve query to complete. */
403
while(!result) {
404
struct timeval *tvp, tv, store;
405
int itimeout;
406
timediff_t timeout_ms;
407
408
#if TIMEDIFF_T_MAX > INT_MAX
409
itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
410
#else
411
itimeout = (int)timeout;
412
#endif
413
414
store.tv_sec = itimeout/1000;
415
store.tv_usec = (itimeout%1000)*1000;
416
417
tvp = ares_timeout(ares->channel, &store, &tv);
418
419
/* use the timeout period ares returned to us above if less than one
420
second is left, otherwise just use 1000ms to make sure the progress
421
callback gets called frequent enough */
422
if(!tvp->tv_sec)
423
timeout_ms = (timediff_t)(tvp->tv_usec/1000);
424
else
425
timeout_ms = 1000;
426
427
if(Curl_ares_perform(ares->channel, timeout_ms) < 0)
428
return CURLE_UNRECOVERABLE_POLL;
429
430
result = Curl_async_is_resolved(data, entry);
431
if(result || data->state.async.done)
432
break;
433
434
if(Curl_pgrsUpdate(data))
435
result = CURLE_ABORTED_BY_CALLBACK;
436
else {
437
struct curltime now2 = curlx_now();
438
timediff_t timediff = curlx_timediff(now2, now); /* spent time */
439
if(timediff <= 0)
440
timeout -= 1; /* always deduct at least 1 */
441
else if(timediff > timeout)
442
timeout = -1;
443
else
444
timeout -= timediff;
445
now = now2; /* for next loop */
446
}
447
if(timeout < 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
if(entry)
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 = (data->conn->transport == TRNSPRT_TCP) ?
762
SOCK_STREAM : SOCK_DGRAM;
763
/* Since the service is a numerical one, set the hint flags
764
* accordingly to save a call to getservbyname in inside C-Ares
765
*/
766
hints.ai_flags = ARES_AI_NUMERICSERV;
767
msnprintf(service, sizeof(service), "%d", port);
768
ares->num_pending = 1;
769
ares_getaddrinfo(ares->channel, data->state.async.hostname,
770
service, &hints, async_ares_addrinfo_cb, data);
771
}
772
#else
773
774
#ifdef HAVE_CARES_IPV6
775
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
776
/* The stack seems to be IPv6-enabled */
777
/* areschannel is already setup in the Curl_open() function */
778
CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
779
ares_gethostbyname(ares->channel, hostname, PF_INET,
780
async_ares_hostbyname_cb, data);
781
CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
782
ares->num_pending = 2;
783
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6,
784
async_ares_hostbyname_cb, data);
785
}
786
else
787
#endif
788
{
789
/* areschannel is already setup in the Curl_open() function */
790
CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
791
ares->num_pending = 1;
792
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
793
async_ares_hostbyname_cb, data);
794
}
795
#endif
796
#ifdef USE_HTTPSRR
797
{
798
CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR");
799
memset(&ares->hinfo, 0, sizeof(ares->hinfo));
800
ares->hinfo.port = -1;
801
ares->num_pending++; /* one more */
802
ares_query_dnsrec(ares->channel, data->state.async.hostname,
803
ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
804
async_ares_rr_done, data, NULL);
805
}
806
#endif
807
*waitp = 1; /* expect asynchronous response */
808
809
return NULL; /* no struct yet */
810
}
811
812
/* Set what DNS server are is to use. This is called in 2 situations:
813
* 1. when the application does 'CURLOPT_DNS_SERVERS' and passing NULL
814
* means any previous set value should be unset. Which means
815
* we need to destroy and create the are channel anew, if there is one.
816
* 2. When we lazy init the ares channel and NULL means that there
817
* are no preferences and we do not reset any existing channel. */
818
static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
819
bool reset_on_null)
820
{
821
struct async_ares_ctx *ares = &data->state.async.ares;
822
CURLcode result = CURLE_NOT_BUILT_IN;
823
const char *servers = data->set.str[STRING_DNS_SERVERS];
824
int ares_result = ARES_SUCCESS;
825
826
#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV)
827
if(getenv("CURL_DNS_SERVER"))
828
servers = getenv("CURL_DNS_SERVER");
829
#endif
830
831
if(!servers) {
832
if(reset_on_null) {
833
Curl_async_destroy(data);
834
}
835
return CURLE_OK;
836
}
837
838
#ifdef HAVE_CARES_SERVERS_CSV
839
/* if channel is not there, this is just a parameter check */
840
if(ares->channel)
841
#ifdef HAVE_CARES_PORTS_CSV
842
ares_result = ares_set_servers_ports_csv(ares->channel, servers);
843
#else
844
ares_result = ares_set_servers_csv(ares->channel, servers);
845
#endif
846
switch(ares_result) {
847
case ARES_SUCCESS:
848
result = CURLE_OK;
849
break;
850
case ARES_ENOMEM:
851
result = CURLE_OUT_OF_MEMORY;
852
break;
853
case ARES_ENOTINITIALIZED:
854
case ARES_ENODATA:
855
case ARES_EBADSTR:
856
default:
857
DEBUGF(infof(data, "bad servers set"));
858
result = CURLE_BAD_FUNCTION_ARGUMENT;
859
break;
860
}
861
#else /* too old c-ares version! */
862
(void)data;
863
(void)(ares_result);
864
#endif
865
return result;
866
}
867
868
CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
869
{
870
return async_ares_set_dns_servers(data, TRUE);
871
}
872
873
CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
874
{
875
#ifdef HAVE_CARES_LOCAL_DEV
876
struct async_ares_ctx *ares = &data->state.async.ares;
877
const char *interf = data->set.str[STRING_DNS_INTERFACE];
878
879
if(!interf)
880
interf = "";
881
882
/* if channel is not there, this is just a parameter check */
883
if(ares->channel)
884
ares_set_local_dev(ares->channel, interf);
885
886
return CURLE_OK;
887
#else /* c-ares version too old! */
888
(void)data;
889
(void)interf;
890
return CURLE_NOT_BUILT_IN;
891
#endif
892
}
893
894
CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
895
{
896
#ifdef HAVE_CARES_SET_LOCAL
897
struct async_ares_ctx *ares = &data->state.async.ares;
898
struct in_addr a4;
899
const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
900
901
if((!local_ip4) || (local_ip4[0] == 0)) {
902
a4.s_addr = 0; /* disabled: do not bind to a specific address */
903
}
904
else {
905
if(curlx_inet_pton(AF_INET, local_ip4, &a4) != 1) {
906
DEBUGF(infof(data, "bad DNS IPv4 address"));
907
return CURLE_BAD_FUNCTION_ARGUMENT;
908
}
909
}
910
911
/* if channel is not there yet, this is just a parameter check */
912
if(ares->channel)
913
ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
914
915
return CURLE_OK;
916
#else /* c-ares version too old! */
917
(void)data;
918
(void)local_ip4;
919
return CURLE_NOT_BUILT_IN;
920
#endif
921
}
922
923
CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
924
{
925
#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
926
struct async_ares_ctx *ares = &data->state.async.ares;
927
unsigned char a6[INET6_ADDRSTRLEN];
928
const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
929
930
if((!local_ip6) || (local_ip6[0] == 0)) {
931
/* disabled: do not bind to a specific address */
932
memset(a6, 0, sizeof(a6));
933
}
934
else {
935
if(curlx_inet_pton(AF_INET6, local_ip6, a6) != 1) {
936
DEBUGF(infof(data, "bad DNS IPv6 address"));
937
return CURLE_BAD_FUNCTION_ARGUMENT;
938
}
939
}
940
941
/* if channel is not there, this is just a parameter check */
942
if(ares->channel)
943
ares_set_local_ip6(ares->channel, a6);
944
945
return CURLE_OK;
946
#else /* c-ares version too old! */
947
(void)data;
948
return CURLE_NOT_BUILT_IN;
949
#endif
950
}
951
952
#endif /* CURLRES_ARES */
953
954