quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

ares_update_servers.c (37906B)


      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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  */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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
    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 
    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 
    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 
    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 */
   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 
   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 
   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 
   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 
   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 */
   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 */
   1304 int ares_set_servers_csv(ares_channel_t *channel, const char *_csv)
   1305 {
   1306   return (int)set_servers_csv(channel, _csv);
   1307 }
   1308 
   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 
   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 
   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 }