• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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