1 /* MIT License
2 *
3 * Copyright (c) 1998 Massachusetts Institute of Technology
4 * Copyright (c) 2008 Daniel Stenberg
5 * Copyright (c) 2023 Brad House
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 *
26 * SPDX-License-Identifier: MIT
27 */
28 #include "ares_private.h"
29
30 #ifdef HAVE_ARPA_INET_H
31 # include <arpa/inet.h>
32 #endif
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36 #ifdef HAVE_SYS_SOCKET_H
37 # include <sys/socket.h>
38 #endif
39 #ifdef HAVE_NET_IF_H
40 # include <net/if.h>
41 #endif
42 #ifdef HAVE_STDINT_H
43 # include <stdint.h>
44 #endif
45
46 #if defined(USE_WINSOCK)
47 # if defined(HAVE_IPHLPAPI_H)
48 # include <iphlpapi.h>
49 # endif
50 # if defined(HAVE_NETIOAPI_H)
51 # include <netioapi.h>
52 # endif
53 #endif
54
55 #include "ares_data.h"
56 #include "ares_inet_net_pton.h"
57
58 typedef struct {
59 struct ares_addr addr;
60 unsigned short tcp_port;
61 unsigned short udp_port;
62
63 char ll_iface[IF_NAMESIZE];
64 unsigned int ll_scope;
65 } ares_sconfig_t;
66
ares_addr_match(const struct ares_addr * addr1,const struct ares_addr * addr2)67 static ares_bool_t ares_addr_match(const struct ares_addr *addr1,
68 const struct ares_addr *addr2)
69 {
70 if (addr1 == NULL && addr2 == NULL) {
71 return ARES_TRUE; /* LCOV_EXCL_LINE: DefensiveCoding */
72 }
73
74 if (addr1 == NULL || addr2 == NULL) {
75 return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
76 }
77
78 if (addr1->family != addr2->family) {
79 return ARES_FALSE;
80 }
81
82 if (addr1->family == AF_INET && memcmp(&addr1->addr.addr4, &addr2->addr.addr4,
83 sizeof(addr1->addr.addr4)) == 0) {
84 return ARES_TRUE;
85 }
86
87 if (addr1->family == AF_INET6 &&
88 memcmp(&addr1->addr.addr6._S6_un._S6_u8, &addr2->addr.addr6._S6_un._S6_u8,
89 sizeof(addr1->addr.addr6._S6_un._S6_u8)) == 0) {
90 return ARES_TRUE;
91 }
92
93 return ARES_FALSE;
94 }
95
ares_subnet_match(const struct ares_addr * addr,const struct ares_addr * subnet,unsigned char netmask)96 ares_bool_t ares_subnet_match(const struct ares_addr *addr,
97 const struct ares_addr *subnet,
98 unsigned char netmask)
99 {
100 const unsigned char *addr_ptr;
101 const unsigned char *subnet_ptr;
102 size_t len;
103 size_t i;
104
105 if (addr == NULL || subnet == NULL) {
106 return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
107 }
108
109 if (addr->family != subnet->family) {
110 return ARES_FALSE;
111 }
112
113 if (addr->family == AF_INET) {
114 addr_ptr = (const unsigned char *)&addr->addr.addr4;
115 subnet_ptr = (const unsigned char *)&subnet->addr.addr4;
116 len = 4;
117
118 if (netmask > 32) {
119 return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
120 }
121 } else if (addr->family == AF_INET6) {
122 addr_ptr = (const unsigned char *)&addr->addr.addr6;
123 subnet_ptr = (const unsigned char *)&subnet->addr.addr6;
124 len = 16;
125
126 if (netmask > 128) {
127 return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
128 }
129 } else {
130 return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
131 }
132
133 for (i = 0; i < len && netmask > 0; i++) {
134 unsigned char mask = 0xff;
135 if (netmask < 8) {
136 mask <<= (8 - netmask);
137 netmask = 0;
138 } else {
139 netmask -= 8;
140 }
141
142 if ((addr_ptr[i] & mask) != (subnet_ptr[i] & mask)) {
143 return ARES_FALSE;
144 }
145 }
146
147 return ARES_TRUE;
148 }
149
ares_addr_is_linklocal(const struct ares_addr * addr)150 ares_bool_t ares_addr_is_linklocal(const struct ares_addr *addr)
151 {
152 struct ares_addr subnet;
153 const unsigned char subnetaddr[16] = { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00 };
156
157 /* fe80::/10 */
158 subnet.family = AF_INET6;
159 memcpy(&subnet.addr.addr6, subnetaddr, 16);
160
161 return ares_subnet_match(addr, &subnet, 10);
162 }
163
ares_server_blacklisted(const struct ares_addr * addr)164 static ares_bool_t ares_server_blacklisted(const struct ares_addr *addr)
165 {
166 /* A list of blacklisted IPv6 subnets. */
167 const struct {
168 const unsigned char netbase[16];
169 unsigned char netmask;
170 } blacklist_v6[] = {
171 /* fec0::/10 was deprecated by [RFC3879] in September 2004. Formerly a
172 * Site-Local scoped address prefix. These are never valid DNS servers,
173 * but are known to be returned at least sometimes on Windows and Android.
174 */
175 { { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00 },
177 10 }
178 };
179
180 size_t i;
181
182 if (addr->family != AF_INET6) {
183 return ARES_FALSE;
184 }
185
186 /* See if ipaddr matches any of the entries in the blacklist. */
187 for (i = 0; i < sizeof(blacklist_v6) / sizeof(*blacklist_v6); i++) {
188 struct ares_addr subnet;
189 subnet.family = AF_INET6;
190 memcpy(&subnet.addr.addr6, blacklist_v6[i].netbase, 16);
191 if (ares_subnet_match(addr, &subnet, blacklist_v6[i].netmask)) {
192 return ARES_TRUE;
193 }
194 }
195 return ARES_FALSE;
196 }
197
parse_nameserver_uri(ares_buf_t * buf,ares_sconfig_t * sconfig)198 static ares_status_t parse_nameserver_uri(ares_buf_t *buf,
199 ares_sconfig_t *sconfig)
200 {
201 ares_uri_t *uri = NULL;
202 ares_status_t status = ARES_SUCCESS;
203 const char *port;
204 char *ll_scope;
205 char hoststr[256];
206 size_t addrlen;
207
208 status = ares_uri_parse_buf(&uri, buf);
209 if (status != ARES_SUCCESS) {
210 return status;
211 }
212
213 if (!ares_streq("dns", ares_uri_get_scheme(uri))) {
214 status = ARES_EBADSTR;
215 goto done;
216 }
217
218 ares_strcpy(hoststr, ares_uri_get_host(uri), sizeof(hoststr));
219 ll_scope = strchr(hoststr, '%');
220 if (ll_scope != NULL) {
221 *ll_scope = 0;
222 ll_scope++;
223 ares_strcpy(sconfig->ll_iface, ll_scope, sizeof(sconfig->ll_iface));
224 }
225
226 /* Convert ip address from string to network byte order */
227 sconfig->addr.family = AF_UNSPEC;
228 if (ares_dns_pton(hoststr, &sconfig->addr, &addrlen) == NULL) {
229 status = ARES_EBADSTR;
230 goto done;
231 }
232
233 sconfig->udp_port = ares_uri_get_port(uri);
234 sconfig->tcp_port = sconfig->udp_port;
235 port = ares_uri_get_query_key(uri, "tcpport");
236 if (port != NULL) {
237 sconfig->tcp_port = (unsigned short)atoi(port);
238 }
239
240 done:
241 ares_uri_destroy(uri);
242 return status;
243 }
244
245 /* Parse address and port in these formats, either ipv4 or ipv6 addresses
246 * are allowed:
247 * ipaddr
248 * ipv4addr:port
249 * [ipaddr]
250 * [ipaddr]:port
251 *
252 * Modifiers: %iface
253 *
254 * TODO: #domain modifier
255 *
256 * If a port is not specified, will set port to 0.
257 *
258 * Will fail if an IPv6 nameserver as detected by
259 * ares_ipv6_server_blacklisted()
260 *
261 * Returns an error code on failure, else ARES_SUCCESS
262 */
263
parse_nameserver(ares_buf_t * buf,ares_sconfig_t * sconfig)264 static ares_status_t parse_nameserver(ares_buf_t *buf, ares_sconfig_t *sconfig)
265 {
266 ares_status_t status;
267 char ipaddr[INET6_ADDRSTRLEN] = "";
268 size_t addrlen;
269
270 memset(sconfig, 0, sizeof(*sconfig));
271
272 /* Consume any leading whitespace */
273 ares_buf_consume_whitespace(buf, ARES_TRUE);
274
275 /* pop off IP address. If it is in [ ] then it can be ipv4 or ipv6. If
276 * not, ipv4 only */
277 if (ares_buf_begins_with(buf, (const unsigned char *)"[", 1)) {
278 /* Consume [ */
279 ares_buf_consume(buf, 1);
280
281 ares_buf_tag(buf);
282
283 /* Consume until ] */
284 if (ares_buf_consume_until_charset(buf, (const unsigned char *)"]", 1,
285 ARES_TRUE) == SIZE_MAX) {
286 return ARES_EBADSTR;
287 }
288
289 status = ares_buf_tag_fetch_string(buf, ipaddr, sizeof(ipaddr));
290 if (status != ARES_SUCCESS) {
291 return status;
292 }
293
294 /* Skip over ] */
295 ares_buf_consume(buf, 1);
296 } else {
297 size_t offset;
298
299 /* Not in [ ], see if '.' is in first 4 characters, if it is, then its ipv4,
300 * otherwise treat as ipv6 */
301 ares_buf_tag(buf);
302
303 offset = ares_buf_consume_until_charset(buf, (const unsigned char *)".", 1,
304 ARES_TRUE);
305 ares_buf_tag_rollback(buf);
306 ares_buf_tag(buf);
307
308 if (offset > 0 && offset < 4) {
309 /* IPv4 */
310 if (ares_buf_consume_charset(buf, (const unsigned char *)"0123456789.",
311 11) == 0) {
312 return ARES_EBADSTR;
313 }
314 } else {
315 /* IPv6 */
316 const unsigned char ipv6_charset[] = "ABCDEFabcdef0123456789.:";
317 if (ares_buf_consume_charset(buf, ipv6_charset,
318 sizeof(ipv6_charset) - 1) == 0) {
319 return ARES_EBADSTR;
320 }
321 }
322
323 status = ares_buf_tag_fetch_string(buf, ipaddr, sizeof(ipaddr));
324 if (status != ARES_SUCCESS) {
325 return status;
326 }
327 }
328
329 /* Convert ip address from string to network byte order */
330 sconfig->addr.family = AF_UNSPEC;
331 if (ares_dns_pton(ipaddr, &sconfig->addr, &addrlen) == NULL) {
332 return ARES_EBADSTR;
333 }
334
335 /* Pull off port */
336 if (ares_buf_begins_with(buf, (const unsigned char *)":", 1)) {
337 char portstr[6];
338
339 /* Consume : */
340 ares_buf_consume(buf, 1);
341
342 ares_buf_tag(buf);
343
344 /* Read numbers */
345 if (ares_buf_consume_charset(buf, (const unsigned char *)"0123456789",
346 10) == 0) {
347 return ARES_EBADSTR;
348 }
349
350 status = ares_buf_tag_fetch_string(buf, portstr, sizeof(portstr));
351 if (status != ARES_SUCCESS) {
352 return status;
353 }
354
355 sconfig->udp_port = (unsigned short)atoi(portstr);
356 sconfig->tcp_port = sconfig->udp_port;
357 }
358
359 /* Pull off interface modifier */
360 if (ares_buf_begins_with(buf, (const unsigned char *)"%", 1)) {
361 const unsigned char iface_charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
362 "abcdefghijklmnopqrstuvwxyz"
363 "0123456789.-_\\:{}";
364 /* Consume % */
365 ares_buf_consume(buf, 1);
366
367 ares_buf_tag(buf);
368
369 if (ares_buf_consume_charset(buf, iface_charset,
370 sizeof(iface_charset) - 1) == 0) {
371 return ARES_EBADSTR;
372 }
373
374 status = ares_buf_tag_fetch_string(buf, sconfig->ll_iface,
375 sizeof(sconfig->ll_iface));
376 if (status != ARES_SUCCESS) {
377 return status;
378 }
379 }
380
381 /* Consume any trailing whitespace so we can bail out if there is something
382 * after we didn't read */
383 ares_buf_consume_whitespace(buf, ARES_TRUE);
384
385 if (ares_buf_len(buf) != 0) {
386 return ARES_EBADSTR;
387 }
388
389 return ARES_SUCCESS;
390 }
391
ares_sconfig_linklocal(const ares_channel_t * channel,ares_sconfig_t * s,const char * ll_iface)392 static ares_status_t ares_sconfig_linklocal(const ares_channel_t *channel,
393 ares_sconfig_t *s,
394 const char *ll_iface)
395 {
396 unsigned int ll_scope = 0;
397
398
399 if (ares_str_isnum(ll_iface)) {
400 char ifname[IF_NAMESIZE] = "";
401 ll_scope = (unsigned int)atoi(ll_iface);
402 if (channel->sock_funcs.aif_indextoname == NULL ||
403 channel->sock_funcs.aif_indextoname(ll_scope, ifname, sizeof(ifname),
404 channel->sock_func_cb_data) ==
405 NULL) {
406 DEBUGF(fprintf(stderr, "Interface %s for ipv6 Link Local not found\n",
407 ll_iface));
408 return ARES_ENOTFOUND;
409 }
410 ares_strcpy(s->ll_iface, ifname, sizeof(s->ll_iface));
411 s->ll_scope = ll_scope;
412 return ARES_SUCCESS;
413 }
414
415 if (channel->sock_funcs.aif_nametoindex != NULL) {
416 ll_scope =
417 channel->sock_funcs.aif_nametoindex(ll_iface, channel->sock_func_cb_data);
418 }
419 if (ll_scope == 0) {
420 DEBUGF(fprintf(stderr, "Interface %s for ipv6 Link Local not found\n",
421 ll_iface));
422 return ARES_ENOTFOUND;
423 }
424 ares_strcpy(s->ll_iface, ll_iface, sizeof(s->ll_iface));
425 s->ll_scope = ll_scope;
426 return ARES_SUCCESS;
427 }
428
ares_sconfig_append(const ares_channel_t * channel,ares_llist_t ** sconfig,const struct ares_addr * addr,unsigned short udp_port,unsigned short tcp_port,const char * ll_iface)429 ares_status_t ares_sconfig_append(const ares_channel_t *channel,
430 ares_llist_t **sconfig,
431 const struct ares_addr *addr,
432 unsigned short udp_port,
433 unsigned short tcp_port, const char *ll_iface)
434 {
435 ares_sconfig_t *s;
436 ares_status_t status;
437
438 if (sconfig == NULL || addr == NULL) {
439 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
440 }
441
442 /* Silently skip blacklisted IPv6 servers. */
443 if (ares_server_blacklisted(addr)) {
444 return ARES_SUCCESS;
445 }
446
447 s = ares_malloc_zero(sizeof(*s));
448 if (s == NULL) {
449 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
450 }
451
452 if (*sconfig == NULL) {
453 *sconfig = ares_llist_create(ares_free);
454 if (*sconfig == NULL) {
455 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
456 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
457 }
458 }
459
460 memcpy(&s->addr, addr, sizeof(s->addr));
461 s->udp_port = udp_port;
462 s->tcp_port = tcp_port;
463
464 /* Handle link-local enumeration. If an interface is specified on a
465 * non-link-local address, we'll simply end up ignoring that */
466 if (ares_addr_is_linklocal(&s->addr)) {
467 if (ares_strlen(ll_iface) == 0) {
468 /* Silently ignore this entry, we require an interface */
469 status = ARES_SUCCESS;
470 goto fail;
471 }
472 status = ares_sconfig_linklocal(channel, s, ll_iface);
473 /* Silently ignore this entry, we can't validate the interface */
474 if (status != ARES_SUCCESS) {
475 status = ARES_SUCCESS;
476 goto fail;
477 }
478 }
479
480 if (ares_llist_insert_last(*sconfig, s) == NULL) {
481 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
482 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
483 }
484
485 return ARES_SUCCESS;
486
487 fail:
488 ares_free(s);
489
490 return status;
491 }
492
493 /* Add the IPv4 or IPv6 nameservers in str (separated by commas or spaces) to
494 * the servers list, updating servers and nservers as required.
495 *
496 * If a nameserver is encapsulated in [ ] it may optionally include a port
497 * suffix, e.g.:
498 * [127.0.0.1]:59591
499 *
500 * The extended format is required to support OpenBSD's resolv.conf format:
501 * https://man.openbsd.org/OpenBSD-5.1/resolv.conf.5
502 * As well as MacOS libresolv that may include a non-default port number.
503 *
504 * This will silently ignore blacklisted IPv6 nameservers as detected by
505 * ares_ipv6_server_blacklisted().
506 *
507 * Returns an error code on failure, else ARES_SUCCESS.
508 */
ares_sconfig_append_fromstr(const ares_channel_t * channel,ares_llist_t ** sconfig,const char * str,ares_bool_t ignore_invalid)509 ares_status_t ares_sconfig_append_fromstr(const ares_channel_t *channel,
510 ares_llist_t **sconfig,
511 const char *str,
512 ares_bool_t ignore_invalid)
513 {
514 ares_status_t status = ARES_SUCCESS;
515 ares_buf_t *buf = NULL;
516 ares_array_t *list = NULL;
517 size_t num;
518 size_t i;
519
520 /* On Windows, there may be more than one nameserver specified in the same
521 * registry key, so we parse input as a space or comma separated list.
522 */
523 buf = ares_buf_create_const((const unsigned char *)str, ares_strlen(str));
524 if (buf == NULL) {
525 status = ARES_ENOMEM;
526 goto done;
527 }
528
529 status = ares_buf_split(buf, (const unsigned char *)" ,", 2,
530 ARES_BUF_SPLIT_NONE, 0, &list);
531 if (status != ARES_SUCCESS) {
532 goto done;
533 }
534
535 num = ares_array_len(list);
536 for (i = 0; i < num; i++) {
537 ares_buf_t **bufptr = ares_array_at(list, i);
538 ares_buf_t *entry = *bufptr;
539 ares_sconfig_t s;
540
541 status = parse_nameserver_uri(entry, &s);
542 if (status != ARES_SUCCESS) {
543 status = parse_nameserver(entry, &s);
544 }
545
546 if (status != ARES_SUCCESS) {
547 if (ignore_invalid) {
548 continue;
549 } else {
550 goto done;
551 }
552 }
553
554 status = ares_sconfig_append(channel, sconfig, &s.addr, s.udp_port,
555 s.tcp_port, s.ll_iface);
556 if (status != ARES_SUCCESS) {
557 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
558 }
559 }
560
561 status = ARES_SUCCESS;
562
563 done:
564 ares_array_destroy(list);
565 ares_buf_destroy(buf);
566 return status;
567 }
568
ares_sconfig_get_port(const ares_channel_t * channel,const ares_sconfig_t * s,ares_bool_t is_tcp)569 static unsigned short ares_sconfig_get_port(const ares_channel_t *channel,
570 const ares_sconfig_t *s,
571 ares_bool_t is_tcp)
572 {
573 unsigned short port = is_tcp ? s->tcp_port : s->udp_port;
574
575 if (port == 0) {
576 port = is_tcp ? channel->tcp_port : channel->udp_port;
577 }
578
579 if (port == 0) {
580 port = 53;
581 }
582
583 return port;
584 }
585
ares_server_find(const ares_channel_t * channel,const ares_sconfig_t * s)586 static ares_slist_node_t *ares_server_find(const ares_channel_t *channel,
587 const ares_sconfig_t *s)
588 {
589 ares_slist_node_t *node;
590
591 for (node = ares_slist_node_first(channel->servers); node != NULL;
592 node = ares_slist_node_next(node)) {
593 const ares_server_t *server = ares_slist_node_val(node);
594
595 if (!ares_addr_match(&server->addr, &s->addr)) {
596 continue;
597 }
598
599 if (server->tcp_port != ares_sconfig_get_port(channel, s, ARES_TRUE)) {
600 continue;
601 }
602
603 if (server->udp_port != ares_sconfig_get_port(channel, s, ARES_FALSE)) {
604 continue;
605 }
606
607 return node;
608 }
609 return NULL;
610 }
611
ares_server_isdup(const ares_channel_t * channel,ares_llist_node_t * s)612 static ares_bool_t ares_server_isdup(const ares_channel_t *channel,
613 ares_llist_node_t *s)
614 {
615 /* Scan backwards to see if this is a duplicate */
616 ares_llist_node_t *prev;
617 const ares_sconfig_t *server = ares_llist_node_val(s);
618
619 for (prev = ares_llist_node_prev(s); prev != NULL;
620 prev = ares_llist_node_prev(prev)) {
621 const ares_sconfig_t *p = ares_llist_node_val(prev);
622
623 if (!ares_addr_match(&server->addr, &p->addr)) {
624 continue;
625 }
626
627 if (ares_sconfig_get_port(channel, server, ARES_TRUE) !=
628 ares_sconfig_get_port(channel, p, ARES_TRUE)) {
629 continue;
630 }
631
632 if (ares_sconfig_get_port(channel, server, ARES_FALSE) !=
633 ares_sconfig_get_port(channel, p, ARES_FALSE)) {
634 continue;
635 }
636
637 return ARES_TRUE;
638 }
639
640 return ARES_FALSE;
641 }
642
ares_server_create(ares_channel_t * channel,const ares_sconfig_t * sconfig,size_t idx)643 static ares_status_t ares_server_create(ares_channel_t *channel,
644 const ares_sconfig_t *sconfig,
645 size_t idx)
646 {
647 ares_status_t status;
648 ares_server_t *server = ares_malloc_zero(sizeof(*server));
649
650 if (server == NULL) {
651 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
652 }
653
654 server->idx = idx;
655 server->channel = channel;
656 server->udp_port = ares_sconfig_get_port(channel, sconfig, ARES_FALSE);
657 server->tcp_port = ares_sconfig_get_port(channel, sconfig, ARES_TRUE);
658 server->addr.family = sconfig->addr.family;
659 server->next_retry_time.sec = 0;
660 server->next_retry_time.usec = 0;
661
662 if (sconfig->addr.family == AF_INET) {
663 memcpy(&server->addr.addr.addr4, &sconfig->addr.addr.addr4,
664 sizeof(server->addr.addr.addr4));
665 } else if (sconfig->addr.family == AF_INET6) {
666 memcpy(&server->addr.addr.addr6, &sconfig->addr.addr.addr6,
667 sizeof(server->addr.addr.addr6));
668 }
669
670 /* Copy over link-local settings */
671 if (ares_strlen(sconfig->ll_iface)) {
672 ares_strcpy(server->ll_iface, sconfig->ll_iface, sizeof(server->ll_iface));
673 server->ll_scope = sconfig->ll_scope;
674 }
675
676 server->connections = ares_llist_create(NULL);
677 if (server->connections == NULL) {
678 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
679 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
680 }
681
682 if (ares_slist_insert(channel->servers, server) == NULL) {
683 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
684 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
685 }
686
687 status = ARES_SUCCESS;
688
689 done:
690 if (status != ARES_SUCCESS) {
691 ares_destroy_server(server); /* LCOV_EXCL_LINE: OutOfMemory */
692 }
693
694 return status;
695 }
696
ares_server_in_newconfig(const ares_server_t * server,ares_llist_t * srvlist)697 static ares_bool_t ares_server_in_newconfig(const ares_server_t *server,
698 ares_llist_t *srvlist)
699 {
700 ares_llist_node_t *node;
701 const ares_channel_t *channel = server->channel;
702
703 for (node = ares_llist_node_first(srvlist); node != NULL;
704 node = ares_llist_node_next(node)) {
705 const ares_sconfig_t *s = ares_llist_node_val(node);
706
707 if (!ares_addr_match(&server->addr, &s->addr)) {
708 continue;
709 }
710
711 if (server->tcp_port != ares_sconfig_get_port(channel, s, ARES_TRUE)) {
712 continue;
713 }
714
715 if (server->udp_port != ares_sconfig_get_port(channel, s, ARES_FALSE)) {
716 continue;
717 }
718
719 return ARES_TRUE;
720 }
721
722 return ARES_FALSE;
723 }
724
ares_servers_remove_stale(ares_channel_t * channel,ares_llist_t * srvlist)725 static ares_bool_t ares_servers_remove_stale(ares_channel_t *channel,
726 ares_llist_t *srvlist)
727 {
728 ares_bool_t stale_removed = ARES_FALSE;
729 ares_slist_node_t *snode = ares_slist_node_first(channel->servers);
730
731 while (snode != NULL) {
732 ares_slist_node_t *snext = ares_slist_node_next(snode);
733 const ares_server_t *server = ares_slist_node_val(snode);
734 if (!ares_server_in_newconfig(server, srvlist)) {
735 /* This will clean up all server state via the destruction callback and
736 * move any queries to new servers */
737 ares_slist_node_destroy(snode);
738 stale_removed = ARES_TRUE;
739 }
740 snode = snext;
741 }
742 return stale_removed;
743 }
744
ares_servers_trim_single(ares_channel_t * channel)745 static void ares_servers_trim_single(ares_channel_t *channel)
746 {
747 while (ares_slist_len(channel->servers) > 1) {
748 ares_slist_node_destroy(ares_slist_node_last(channel->servers));
749 }
750 }
751
ares_servers_update(ares_channel_t * channel,ares_llist_t * server_list,ares_bool_t user_specified)752 ares_status_t ares_servers_update(ares_channel_t *channel,
753 ares_llist_t *server_list,
754 ares_bool_t user_specified)
755 {
756 ares_llist_node_t *node;
757 size_t idx = 0;
758 ares_status_t status;
759 ares_bool_t list_changed = ARES_FALSE;
760
761 if (channel == NULL) {
762 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
763 }
764
765 /* NOTE: a NULL or zero entry server list is considered valid due to
766 * real-world people needing support for this for their test harnesses
767 */
768
769 /* Add new entries */
770 for (node = ares_llist_node_first(server_list); node != NULL;
771 node = ares_llist_node_next(node)) {
772 const ares_sconfig_t *sconfig = ares_llist_node_val(node);
773 ares_slist_node_t *snode;
774
775 /* If a server has already appeared in the list of new servers, skip it. */
776 if (ares_server_isdup(channel, node)) {
777 continue;
778 }
779
780 snode = ares_server_find(channel, sconfig);
781 if (snode != NULL) {
782 ares_server_t *server = ares_slist_node_val(snode);
783
784 /* Copy over link-local settings. Its possible some of this data has
785 * changed, maybe ... */
786 if (ares_strlen(sconfig->ll_iface)) {
787 ares_strcpy(server->ll_iface, sconfig->ll_iface,
788 sizeof(server->ll_iface));
789 server->ll_scope = sconfig->ll_scope;
790 }
791
792 if (server->idx != idx) {
793 server->idx = idx;
794 /* Index changed, reinsert node, doesn't require any memory
795 * allocations so can't fail. */
796 ares_slist_node_reinsert(snode);
797 }
798 } else {
799 status = ares_server_create(channel, sconfig, idx);
800 if (status != ARES_SUCCESS) {
801 goto done;
802 }
803
804 list_changed = ARES_TRUE;
805 }
806
807 idx++;
808 }
809
810 /* Remove any servers that don't exist in the current configuration */
811 if (ares_servers_remove_stale(channel, server_list)) {
812 list_changed = ARES_TRUE;
813 }
814
815 /* Trim to one server if ARES_FLAG_PRIMARY is set. */
816 if (channel->flags & ARES_FLAG_PRIMARY) {
817 ares_servers_trim_single(channel);
818 }
819
820 if (user_specified) {
821 /* Save servers as if they were passed in as an option */
822 channel->optmask |= ARES_OPT_SERVERS;
823 }
824
825 /* Clear any cached query results only if the server list changed */
826 if (list_changed) {
827 ares_qcache_flush(channel->qcache);
828 }
829
830 status = ARES_SUCCESS;
831
832 done:
833 return status;
834 }
835
836 static ares_status_t
ares_addr_node_to_sconfig_llist(const struct ares_addr_node * servers,ares_llist_t ** llist)837 ares_addr_node_to_sconfig_llist(const struct ares_addr_node *servers,
838 ares_llist_t **llist)
839 {
840 const struct ares_addr_node *node;
841 ares_llist_t *s;
842
843 *llist = NULL;
844
845 s = ares_llist_create(ares_free);
846 if (s == NULL) {
847 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
848 }
849
850 for (node = servers; node != NULL; node = node->next) {
851 ares_sconfig_t *sconfig;
852
853 /* Invalid entry */
854 if (node->family != AF_INET && node->family != AF_INET6) {
855 continue;
856 }
857
858 sconfig = ares_malloc_zero(sizeof(*sconfig));
859 if (sconfig == NULL) {
860 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
861 }
862
863 sconfig->addr.family = node->family;
864 if (node->family == AF_INET) {
865 memcpy(&sconfig->addr.addr.addr4, &node->addr.addr4,
866 sizeof(sconfig->addr.addr.addr4));
867 } else if (sconfig->addr.family == AF_INET6) {
868 memcpy(&sconfig->addr.addr.addr6, &node->addr.addr6,
869 sizeof(sconfig->addr.addr.addr6));
870 }
871
872 if (ares_llist_insert_last(s, sconfig) == NULL) {
873 ares_free(sconfig); /* LCOV_EXCL_LINE: OutOfMemory */
874 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
875 }
876 }
877
878 *llist = s;
879 return ARES_SUCCESS;
880
881 /* LCOV_EXCL_START: OutOfMemory */
882 fail:
883 ares_llist_destroy(s);
884 return ARES_ENOMEM;
885 /* LCOV_EXCL_STOP */
886 }
887
888 static ares_status_t
ares_addrpnode_to_sconfig_llist(const struct ares_addr_port_node * servers,ares_llist_t ** llist)889 ares_addrpnode_to_sconfig_llist(const struct ares_addr_port_node *servers,
890 ares_llist_t **llist)
891 {
892 const struct ares_addr_port_node *node;
893 ares_llist_t *s;
894
895 *llist = NULL;
896
897 s = ares_llist_create(ares_free);
898 if (s == NULL) {
899 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
900 }
901
902 for (node = servers; node != NULL; node = node->next) {
903 ares_sconfig_t *sconfig;
904
905 /* Invalid entry */
906 if (node->family != AF_INET && node->family != AF_INET6) {
907 continue;
908 }
909
910 sconfig = ares_malloc_zero(sizeof(*sconfig));
911 if (sconfig == NULL) {
912 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
913 }
914
915 sconfig->addr.family = node->family;
916 if (node->family == AF_INET) {
917 memcpy(&sconfig->addr.addr.addr4, &node->addr.addr4,
918 sizeof(sconfig->addr.addr.addr4));
919 } else if (sconfig->addr.family == AF_INET6) {
920 memcpy(&sconfig->addr.addr.addr6, &node->addr.addr6,
921 sizeof(sconfig->addr.addr.addr6));
922 }
923
924 sconfig->tcp_port = (unsigned short)node->tcp_port;
925 sconfig->udp_port = (unsigned short)node->udp_port;
926
927 if (ares_llist_insert_last(s, sconfig) == NULL) {
928 ares_free(sconfig); /* LCOV_EXCL_LINE: OutOfMemory */
929 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
930 }
931 }
932
933 *llist = s;
934 return ARES_SUCCESS;
935
936 /* LCOV_EXCL_START: OutOfMemory */
937 fail:
938 ares_llist_destroy(s);
939 return ARES_ENOMEM;
940 /* LCOV_EXCL_STOP */
941 }
942
ares_in_addr_to_sconfig_llist(const struct in_addr * servers,size_t nservers,ares_llist_t ** llist)943 ares_status_t ares_in_addr_to_sconfig_llist(const struct in_addr *servers,
944 size_t nservers,
945 ares_llist_t **llist)
946 {
947 size_t i;
948 ares_llist_t *s;
949
950 *llist = NULL;
951
952 s = ares_llist_create(ares_free);
953 if (s == NULL) {
954 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
955 }
956
957 for (i = 0; servers != NULL && i < nservers; i++) {
958 ares_sconfig_t *sconfig;
959
960 sconfig = ares_malloc_zero(sizeof(*sconfig));
961 if (sconfig == NULL) {
962 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
963 }
964
965 sconfig->addr.family = AF_INET;
966 memcpy(&sconfig->addr.addr.addr4, &servers[i],
967 sizeof(sconfig->addr.addr.addr4));
968
969 if (ares_llist_insert_last(s, sconfig) == NULL) {
970 goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
971 }
972 }
973
974 *llist = s;
975 return ARES_SUCCESS;
976
977 /* LCOV_EXCL_START: OutOfMemory */
978 fail:
979 ares_llist_destroy(s);
980 return ARES_ENOMEM;
981 /* LCOV_EXCL_STOP */
982 }
983
ares_server_use_uri(const ares_server_t * server)984 static ares_bool_t ares_server_use_uri(const ares_server_t *server)
985 {
986 /* Currently only reason to use new format is if the ports for udp and tcp
987 * are different */
988 if (server->tcp_port != server->udp_port) {
989 return ARES_TRUE;
990 }
991 return ARES_FALSE;
992 }
993
ares_get_server_addr_uri(const ares_server_t * server,ares_buf_t * buf)994 static ares_status_t ares_get_server_addr_uri(const ares_server_t *server,
995 ares_buf_t *buf)
996 {
997 ares_uri_t *uri = NULL;
998 ares_status_t status;
999 char addr[INET6_ADDRSTRLEN];
1000
1001 uri = ares_uri_create();
1002 if (uri == NULL) {
1003 return ARES_ENOMEM;
1004 }
1005
1006 status = ares_uri_set_scheme(uri, "dns");
1007 if (status != ARES_SUCCESS) {
1008 goto done;
1009 }
1010
1011 ares_inet_ntop(server->addr.family, &server->addr.addr, addr, sizeof(addr));
1012
1013 if (ares_strlen(server->ll_iface)) {
1014 char addr_iface[256];
1015
1016 snprintf(addr_iface, sizeof(addr_iface), "%s%%%s", addr, server->ll_iface);
1017 status = ares_uri_set_host(uri, addr_iface);
1018 } else {
1019 status = ares_uri_set_host(uri, addr);
1020 }
1021
1022 if (status != ARES_SUCCESS) {
1023 goto done;
1024 }
1025
1026 status = ares_uri_set_port(uri, server->udp_port);
1027 if (status != ARES_SUCCESS) {
1028 goto done;
1029 }
1030
1031 if (server->udp_port != server->tcp_port) {
1032 char port[6];
1033 snprintf(port, sizeof(port), "%d", server->tcp_port);
1034 status = ares_uri_set_query_key(uri, "tcpport", port);
1035 if (status != ARES_SUCCESS) {
1036 goto done;
1037 }
1038 }
1039
1040 status = ares_uri_write_buf(uri, buf);
1041 if (status != ARES_SUCCESS) {
1042 goto done;
1043 }
1044
1045 done:
1046 ares_uri_destroy(uri);
1047 return status;
1048 }
1049
1050 /* Write out the details of a server to a buffer */
ares_get_server_addr(const ares_server_t * server,ares_buf_t * buf)1051 ares_status_t ares_get_server_addr(const ares_server_t *server, ares_buf_t *buf)
1052 {
1053 ares_status_t status;
1054 char addr[INET6_ADDRSTRLEN];
1055
1056 if (ares_server_use_uri(server)) {
1057 return ares_get_server_addr_uri(server, buf);
1058 }
1059
1060 /* ipv4addr or [ipv6addr] */
1061 if (server->addr.family == AF_INET6) {
1062 status = ares_buf_append_byte(buf, '[');
1063 if (status != ARES_SUCCESS) {
1064 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1065 }
1066 }
1067
1068 ares_inet_ntop(server->addr.family, &server->addr.addr, addr, sizeof(addr));
1069
1070 status = ares_buf_append_str(buf, addr);
1071 if (status != ARES_SUCCESS) {
1072 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1073 }
1074
1075 if (server->addr.family == AF_INET6) {
1076 status = ares_buf_append_byte(buf, ']');
1077 if (status != ARES_SUCCESS) {
1078 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1079 }
1080 }
1081
1082 /* :port */
1083 status = ares_buf_append_byte(buf, ':');
1084 if (status != ARES_SUCCESS) {
1085 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1086 }
1087
1088 status = ares_buf_append_num_dec(buf, server->udp_port, 0);
1089 if (status != ARES_SUCCESS) {
1090 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1091 }
1092
1093 /* %iface */
1094 if (ares_strlen(server->ll_iface)) {
1095 status = ares_buf_append_byte(buf, '%');
1096 if (status != ARES_SUCCESS) {
1097 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1098 }
1099
1100 status = ares_buf_append_str(buf, server->ll_iface);
1101 if (status != ARES_SUCCESS) {
1102 return status; /* LCOV_EXCL_LINE: OutOfMemory */
1103 }
1104 }
1105
1106 return ARES_SUCCESS;
1107 }
1108
ares_get_servers(const ares_channel_t * channel,struct ares_addr_node ** servers)1109 int ares_get_servers(const ares_channel_t *channel,
1110 struct ares_addr_node **servers)
1111 {
1112 struct ares_addr_node *srvr_head = NULL;
1113 struct ares_addr_node *srvr_last = NULL;
1114 struct ares_addr_node *srvr_curr;
1115 ares_status_t status = ARES_SUCCESS;
1116 ares_slist_node_t *node;
1117
1118 if (channel == NULL) {
1119 return ARES_ENODATA;
1120 }
1121
1122 ares_channel_lock(channel);
1123
1124 for (node = ares_slist_node_first(channel->servers); node != NULL;
1125 node = ares_slist_node_next(node)) {
1126 const ares_server_t *server = ares_slist_node_val(node);
1127
1128 /* Allocate storage for this server node appending it to the list */
1129 srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_NODE);
1130 if (!srvr_curr) {
1131 status = ARES_ENOMEM;
1132 break;
1133 }
1134 if (srvr_last) {
1135 srvr_last->next = srvr_curr;
1136 } else {
1137 srvr_head = srvr_curr;
1138 }
1139 srvr_last = srvr_curr;
1140
1141 /* Fill this server node data */
1142 srvr_curr->family = server->addr.family;
1143 if (srvr_curr->family == AF_INET) {
1144 memcpy(&srvr_curr->addr.addr4, &server->addr.addr.addr4,
1145 sizeof(srvr_curr->addr.addr4));
1146 } else {
1147 memcpy(&srvr_curr->addr.addr6, &server->addr.addr.addr6,
1148 sizeof(srvr_curr->addr.addr6));
1149 }
1150 }
1151
1152 if (status != ARES_SUCCESS) {
1153 ares_free_data(srvr_head);
1154 srvr_head = NULL;
1155 }
1156
1157 *servers = srvr_head;
1158
1159 ares_channel_unlock(channel);
1160
1161 return (int)status;
1162 }
1163
ares_get_servers_ports(const ares_channel_t * channel,struct ares_addr_port_node ** servers)1164 int ares_get_servers_ports(const ares_channel_t *channel,
1165 struct ares_addr_port_node **servers)
1166 {
1167 struct ares_addr_port_node *srvr_head = NULL;
1168 struct ares_addr_port_node *srvr_last = NULL;
1169 struct ares_addr_port_node *srvr_curr;
1170 ares_status_t status = ARES_SUCCESS;
1171 ares_slist_node_t *node;
1172
1173 if (channel == NULL) {
1174 return ARES_ENODATA;
1175 }
1176
1177 ares_channel_lock(channel);
1178
1179 for (node = ares_slist_node_first(channel->servers); node != NULL;
1180 node = ares_slist_node_next(node)) {
1181 const ares_server_t *server = ares_slist_node_val(node);
1182
1183 /* Allocate storage for this server node appending it to the list */
1184 srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_PORT_NODE);
1185 if (!srvr_curr) {
1186 status = ARES_ENOMEM;
1187 break;
1188 }
1189 if (srvr_last) {
1190 srvr_last->next = srvr_curr;
1191 } else {
1192 srvr_head = srvr_curr;
1193 }
1194 srvr_last = srvr_curr;
1195
1196 /* Fill this server node data */
1197 srvr_curr->family = server->addr.family;
1198 srvr_curr->udp_port = server->udp_port;
1199 srvr_curr->tcp_port = server->tcp_port;
1200
1201 if (srvr_curr->family == AF_INET) {
1202 memcpy(&srvr_curr->addr.addr4, &server->addr.addr.addr4,
1203 sizeof(srvr_curr->addr.addr4));
1204 } else {
1205 memcpy(&srvr_curr->addr.addr6, &server->addr.addr.addr6,
1206 sizeof(srvr_curr->addr.addr6));
1207 }
1208 }
1209
1210 if (status != ARES_SUCCESS) {
1211 ares_free_data(srvr_head);
1212 srvr_head = NULL;
1213 }
1214
1215 *servers = srvr_head;
1216
1217 ares_channel_unlock(channel);
1218 return (int)status;
1219 }
1220
ares_set_servers(ares_channel_t * channel,const struct ares_addr_node * servers)1221 int ares_set_servers(ares_channel_t *channel,
1222 const struct ares_addr_node *servers)
1223 {
1224 ares_llist_t *slist;
1225 ares_status_t status;
1226
1227 if (channel == NULL) {
1228 return ARES_ENODATA;
1229 }
1230
1231 status = ares_addr_node_to_sconfig_llist(servers, &slist);
1232 if (status != ARES_SUCCESS) {
1233 return (int)status;
1234 }
1235
1236 ares_channel_lock(channel);
1237 status = ares_servers_update(channel, slist, ARES_TRUE);
1238 ares_channel_unlock(channel);
1239
1240 ares_llist_destroy(slist);
1241
1242 return (int)status;
1243 }
1244
ares_set_servers_ports(ares_channel_t * channel,const struct ares_addr_port_node * servers)1245 int ares_set_servers_ports(ares_channel_t *channel,
1246 const struct ares_addr_port_node *servers)
1247 {
1248 ares_llist_t *slist;
1249 ares_status_t status;
1250
1251 if (channel == NULL) {
1252 return ARES_ENODATA;
1253 }
1254
1255 status = ares_addrpnode_to_sconfig_llist(servers, &slist);
1256 if (status != ARES_SUCCESS) {
1257 return (int)status;
1258 }
1259
1260 ares_channel_lock(channel);
1261 status = ares_servers_update(channel, slist, ARES_TRUE);
1262 ares_channel_unlock(channel);
1263
1264 ares_llist_destroy(slist);
1265
1266 return (int)status;
1267 }
1268
1269 /* Incoming string format: host[:port][,host[:port]]... */
1270 /* IPv6 addresses with ports require square brackets [fe80::1]:53 */
set_servers_csv(ares_channel_t * channel,const char * _csv)1271 static ares_status_t set_servers_csv(ares_channel_t *channel, const char *_csv)
1272 {
1273 ares_status_t status;
1274 ares_llist_t *slist = NULL;
1275
1276 if (channel == NULL) {
1277 return ARES_ENODATA;
1278 }
1279
1280 if (ares_strlen(_csv) == 0) {
1281 /* blank all servers */
1282 ares_channel_lock(channel);
1283 status = ares_servers_update(channel, NULL, ARES_TRUE);
1284 ares_channel_unlock(channel);
1285 return status;
1286 }
1287
1288 status = ares_sconfig_append_fromstr(channel, &slist, _csv, ARES_FALSE);
1289 if (status != ARES_SUCCESS) {
1290 ares_llist_destroy(slist);
1291 return status;
1292 }
1293
1294 ares_channel_lock(channel);
1295 status = ares_servers_update(channel, slist, ARES_TRUE);
1296 ares_channel_unlock(channel);
1297
1298 ares_llist_destroy(slist);
1299
1300 return status;
1301 }
1302
1303 /* We'll go ahead and honor ports anyhow */
ares_set_servers_csv(ares_channel_t * channel,const char * _csv)1304 int ares_set_servers_csv(ares_channel_t *channel, const char *_csv)
1305 {
1306 return (int)set_servers_csv(channel, _csv);
1307 }
1308
ares_set_servers_ports_csv(ares_channel_t * channel,const char * _csv)1309 int ares_set_servers_ports_csv(ares_channel_t *channel, const char *_csv)
1310 {
1311 return (int)set_servers_csv(channel, _csv);
1312 }
1313
ares_get_servers_csv(const ares_channel_t * channel)1314 char *ares_get_servers_csv(const ares_channel_t *channel)
1315 {
1316 ares_buf_t *buf = NULL;
1317 char *out = NULL;
1318 ares_slist_node_t *node;
1319
1320 ares_channel_lock(channel);
1321
1322 buf = ares_buf_create();
1323 if (buf == NULL) {
1324 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
1325 }
1326
1327 for (node = ares_slist_node_first(channel->servers); node != NULL;
1328 node = ares_slist_node_next(node)) {
1329 ares_status_t status;
1330 const ares_server_t *server = ares_slist_node_val(node);
1331
1332 if (ares_buf_len(buf)) {
1333 status = ares_buf_append_byte(buf, ',');
1334 if (status != ARES_SUCCESS) {
1335 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
1336 }
1337 }
1338
1339 status = ares_get_server_addr(server, buf);
1340 if (status != ARES_SUCCESS) {
1341 goto done; /* LCOV_EXCL_LINE: OutOfMemory */
1342 }
1343 }
1344
1345 out = ares_buf_finish_str(buf, NULL);
1346 buf = NULL;
1347
1348 done:
1349 ares_channel_unlock(channel);
1350 ares_buf_destroy(buf);
1351 return out;
1352 }
1353
ares_set_server_state_callback(ares_channel_t * channel,ares_server_state_callback cb,void * data)1354 void ares_set_server_state_callback(ares_channel_t *channel,
1355 ares_server_state_callback cb, void *data)
1356 {
1357 if (channel == NULL) {
1358 return; /* LCOV_EXCL_LINE: DefensiveCoding */
1359 }
1360 channel->server_state_cb = cb;
1361 channel->server_state_cb_data = data;
1362 }
1363