• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* MIT License
2  *
3  * Copyright (c) 1998 Massachusetts Institute of Technology
4  * Copyright (c) 2007 Daniel Stenberg
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  *
25  * SPDX-License-Identifier: MIT
26  */
27 
28 #include "ares_private.h"
29 
30 #ifdef HAVE_SYS_PARAM_H
31 #  include <sys/param.h>
32 #endif
33 
34 #ifdef HAVE_NETINET_IN_H
35 #  include <netinet/in.h>
36 #endif
37 
38 #ifdef HAVE_NETDB_H
39 #  include <netdb.h>
40 #endif
41 
42 #ifdef HAVE_ARPA_INET_H
43 #  include <arpa/inet.h>
44 #endif
45 
46 #if defined(ANDROID) || defined(__ANDROID__)
47 #  include <sys/system_properties.h>
48 #  include "ares_android.h"
49 /* From the Bionic sources */
50 #  define DNS_PROP_NAME_PREFIX "net.dns"
51 #  define MAX_DNS_PROPERTIES   8
52 #endif
53 
54 #if defined(CARES_USE_LIBRESOLV)
55 #  include <resolv.h>
56 #endif
57 
58 #include "ares_inet_net_pton.h"
59 
60 #if OHOS_DNS_PROXY_BY_NETSYS
61 #define MAX_SERVER_NUM 8
62 #define MAX_SERVER_LENGTH 50
63 
64 struct resolv_config {
65   int32_t error;
66   int32_t timeout_ms;
67   uint32_t retry_count;
68   uint32_t non_public;
69   char nameservers[MAX_SERVER_NUM][MAX_SERVER_LENGTH + 1];
70 };
71 
72 int32_t NetSysGetResolvConfExt(uint16_t netid, struct resolv_config *config);
73 
ares_init_sysconfig_netsys(const ares_channel_t * channel,ares_sysconfig_t * sysconfig)74 static ares_status_t ares_init_sysconfig_netsys(const ares_channel_t *channel,
75                                                  ares_sysconfig_t *sysconfig)
76 {
77   struct resolv_config config = {0};
78   int ret = 0;
79   int netid = 0;
80   ares_status_t status = ARES_EFILE;
81 
82   ret = NetSysGetResolvConfExt(netid, &config);
83   if (ret < 0) {
84     return ARES_ENONAME;
85   }
86   for (int i = 0; i < MAX_SERVER_NUM; ++i) {
87     if (config.nameservers[i] == NULL || config.nameservers[i][0] == 0) {
88       continue;
89     }
90     status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig,
91                                          config.nameservers[i], ARES_TRUE);
92     if (status != ARES_SUCCESS) {
93       return status;
94     }
95   }
96   return status;
97 }
98 #endif
99 
100 #if defined(__MVS__)
ares_init_sysconfig_mvs(const ares_channel_t * channel,ares_sysconfig_t * sysconfig)101 static ares_status_t ares_init_sysconfig_mvs(const ares_channel_t *channel,
102                                              ares_sysconfig_t     *sysconfig)
103 {
104   struct __res_state *res = 0;
105   size_t              count4;
106   size_t              count6;
107   int                 i;
108   __STATEEXTIPV6     *v6;
109   arse__llist_t      *sconfig = NULL;
110   ares_status_t       status;
111 
112   if (0 == res) {
113     int rc = res_init();
114     while (rc == -1 && h_errno == TRY_AGAIN) {
115       rc = res_init();
116     }
117     if (rc == -1) {
118       return ARES_ENOMEM;
119     }
120     res = __res();
121   }
122 
123   v6 = res->__res_extIPv6;
124   if (res->nscount > 0) {
125     count4 = (size_t)res->nscount;
126   }
127 
128   if (v6 && v6->__stat_nscount > 0) {
129     count6 = (size_t)v6->__stat_nscount;
130   } else {
131     count6 = 0;
132   }
133 
134   for (i = 0; i < count4; i++) {
135     struct sockaddr_in *addr_in = &(res->nsaddr_list[i]);
136     struct ares_addr    addr;
137 
138     addr.addr.addr4.s_addr = addr_in->sin_addr.s_addr;
139     addr.family            = AF_INET;
140 
141     status = ares_sconfig_append(channel, &sysconfig->sconfig, &addr,
142                                  htons(addr_in->sin_port),
143                                  htons(addr_in->sin_port), NULL);
144 
145     if (status != ARES_SUCCESS) {
146       return status;
147     }
148   }
149 
150   for (i = 0; i < count6; i++) {
151     struct sockaddr_in6 *addr_in = &(v6->__stat_nsaddr_list[i]);
152     struct ares_addr     addr;
153 
154     addr.family = AF_INET6;
155     memcpy(&(addr.addr.addr6), &(addr_in->sin6_addr),
156            sizeof(addr_in->sin6_addr));
157 
158     status = ares_sconfig_append(channel, &sysconfig->sconfig, &addr,
159                                  htons(addr_in->sin_port),
160                                  htons(addr_in->sin_port), NULL);
161 
162     if (status != ARES_SUCCESS) {
163       return status;
164     }
165   }
166 
167   return ARES_SUCCESS;
168 }
169 #endif
170 
171 #if defined(__riscos__)
ares_init_sysconfig_riscos(const ares_channel_t * channel,ares_sysconfig_t * sysconfig)172 static ares_status_t ares_init_sysconfig_riscos(const ares_channel_t *channel,
173                                                 ares_sysconfig_t     *sysconfig)
174 {
175   char         *line;
176   ares_status_t status = ARES_SUCCESS;
177 
178   /* Under RISC OS, name servers are listed in the
179      system variable Inet$Resolvers, space separated. */
180   line = getenv("Inet$Resolvers");
181   if (line) {
182     char *resolvers = ares_strdup(line);
183     char *pos;
184     char *space;
185 
186     if (!resolvers) {
187       return ARES_ENOMEM;
188     }
189 
190     pos = resolvers;
191     do {
192       space = strchr(pos, ' ');
193       if (space) {
194         *space = '\0';
195       }
196       status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, pos,
197                                            ARES_TRUE);
198       if (status != ARES_SUCCESS) {
199         break;
200       }
201       pos = space + 1;
202     } while (space);
203 
204     ares_free(resolvers);
205   }
206 
207   return status;
208 }
209 #endif
210 
211 #if defined(WATT32)
ares_init_sysconfig_watt32(const ares_channel_t * channel,ares_sysconfig_t * sysconfig)212 static ares_status_t ares_init_sysconfig_watt32(const ares_channel_t *channel,
213                                                 ares_sysconfig_t     *sysconfig)
214 {
215   size_t        i;
216   ares_status_t status;
217 
218   sock_init();
219 
220   for (i = 0; def_nameservers[i]; i++) {
221     struct ares_addr addr;
222 
223     addr.family            = AF_INET;
224     addr.addr.addr4.s_addr = htonl(def_nameservers[i]);
225 
226     status =
227       ares_sconfig_append(channel, &sysconfig->sconfig, &addr, 0, 0, NULL);
228 
229     if (status != ARES_SUCCESS) {
230       return status;
231     }
232   }
233 
234   return ARES_SUCCESS;
235 }
236 #endif
237 
238 #if defined(ANDROID) || defined(__ANDROID__)
ares_init_sysconfig_android(const ares_channel_t * channel,ares_sysconfig_t * sysconfig)239 static ares_status_t ares_init_sysconfig_android(const ares_channel_t *channel,
240                                                  ares_sysconfig_t *sysconfig)
241 {
242   size_t        i;
243   char        **dns_servers;
244   char         *domains;
245   size_t        num_servers;
246   ares_status_t status = ARES_EFILE;
247 
248   /* Use the Android connectivity manager to get a list
249    * of DNS servers. As of Android 8 (Oreo) net.dns#
250    * system properties are no longer available. Google claims this
251    * improves privacy. Apps now need the ACCESS_NETWORK_STATE
252    * permission and must use the ConnectivityManager which
253    * is Java only. */
254   dns_servers = ares_get_android_server_list(MAX_DNS_PROPERTIES, &num_servers);
255   if (dns_servers != NULL) {
256     for (i = 0; i < num_servers; i++) {
257       status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig,
258                                            dns_servers[i], ARES_TRUE);
259       if (status != ARES_SUCCESS) {
260         return status;
261       }
262     }
263     for (i = 0; i < num_servers; i++) {
264       ares_free(dns_servers[i]);
265     }
266     ares_free(dns_servers);
267   }
268 
269   domains            = ares_get_android_search_domains_list();
270   sysconfig->domains = ares_strsplit(domains, ", ", &sysconfig->ndomains);
271   ares_free(domains);
272 
273 #  ifdef HAVE___SYSTEM_PROPERTY_GET
274   /* Old way using the system property still in place as
275    * a fallback. Older android versions can still use this.
276    * it's possible for older apps not not have added the new
277    * permission and we want to try to avoid breaking those.
278    *
279    * We'll only run this if we don't have any dns servers
280    * because this will get the same ones (if it works). */
281   if (sysconfig->sconfig == NULL) {
282     char propname[PROP_NAME_MAX];
283     char propvalue[PROP_VALUE_MAX] = "";
284     for (i = 1; i <= MAX_DNS_PROPERTIES; i++) {
285       snprintf(propname, sizeof(propname), "%s%zu", DNS_PROP_NAME_PREFIX, i);
286       if (__system_property_get(propname, propvalue) < 1) {
287         break;
288       }
289       status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig,
290                                            propvalue, ARES_TRUE);
291       if (status != ARES_SUCCESS) {
292         return status;
293       }
294     }
295   }
296 #  endif /* HAVE___SYSTEM_PROPERTY_GET */
297 
298   return status;
299 }
300 #endif
301 
302 #if defined(__QNX__)
303 static ares_status_t
ares_init_sysconfig_qnx(const ares_channel_t * channel,ares_sysconfig_t * sysconfig)304   ares_init_sysconfig_qnx(const ares_channel_t *channel,
305                           ares_sysconfig_t     *sysconfig)
306 {
307   /* QNX:
308    *   1. use confstr(_CS_RESOLVE, ...) as primary resolv.conf data, replacing
309    *      "_" with " ".  If that is empty, then do normal /etc/resolv.conf
310    *      processing.
311    *   2. We want to process /etc/nsswitch.conf as normal.
312    *   3. if confstr(_CS_DOMAIN, ...) this is the domain name.  Use this as
313    *      preference over anything else found.
314    */
315   ares_buf_t    *buf                = ares_buf_create();
316   unsigned char *data               = NULL;
317   size_t         data_size          = 0;
318   ares_bool_t    process_resolvconf = ARES_TRUE;
319   ares_status_t  status             = ARES_SUCCESS;
320 
321   /* Prefer confstr(_CS_RESOLVE, ...) */
322   buf = ares_buf_create();
323   if (buf == NULL) {
324     status = ARES_ENOMEM;
325     goto done;
326   }
327 
328   data_size = 1024;
329   data      = ares_buf_append_start(buf, &data_size);
330   if (data == NULL) {
331     status = ARES_ENOMEM;
332     goto done;
333   }
334 
335   data_size = confstr(_CS_RESOLVE, (char *)data, data_size);
336   if (data_size > 1) {
337     /* confstr returns byte for NULL terminator, strip */
338     data_size--;
339 
340     ares_buf_append_finish(buf, data_size);
341     /* Its odd, this uses _ instead of " " between keywords, otherwise the
342      * format is the same as resolv.conf, replace. */
343     ares_buf_replace(buf, (const unsigned char *)"_", 1,
344                      (const unsigned char *)" ", 1);
345 
346     status = ares_sysconfig_process_buf(channel, sysconfig, buf,
347                                         ares_sysconfig_parse_resolv_line);
348     if (status != ARES_SUCCESS) {
349       /* ENOMEM is really the only error we'll get here */
350       goto done;
351     }
352 
353     /* don't read resolv.conf if we processed *any* nameservers */
354     if (ares_llist_len(sysconfig->sconfig) != 0) {
355       process_resolvconf = ARES_FALSE;
356     }
357   }
358 
359   /* Process files */
360   status = ares_init_sysconfig_files(channel, sysconfig, process_resolvconf);
361   if (status != ARES_SUCCESS) {
362     goto done;
363   }
364 
365   /* Read confstr(_CS_DOMAIN, ...), but if we had a search path specified with
366    * more than one domain, lets prefer that instead.  Its not exactly clear
367    * the best way to handle this. */
368   if (sysconfig->ndomains <= 1) {
369     char   domain[256];
370     size_t domain_len;
371 
372     domain_len = confstr(_CS_DOMAIN, domain, sizeof(domain_len));
373     if (domain_len != 0) {
374       ares_strsplit_free(sysconfig->domains, sysconfig->ndomains);
375       sysconfig->domains = ares_strsplit(domain, ", ", &sysconfig->ndomains);
376       if (sysconfig->domains == NULL) {
377         status = ARES_ENOMEM;
378         goto done;
379       }
380     }
381   }
382 
383 done:
384   ares_buf_destroy(buf);
385 
386   return status;
387 }
388 #endif
389 
390 #if defined(CARES_USE_LIBRESOLV)
391 static ares_status_t
ares_init_sysconfig_libresolv(const ares_channel_t * channel,ares_sysconfig_t * sysconfig)392   ares_init_sysconfig_libresolv(const ares_channel_t *channel,
393                                 ares_sysconfig_t     *sysconfig)
394 {
395   struct __res_state       res;
396   ares_status_t            status = ARES_SUCCESS;
397   union res_sockaddr_union addr[MAXNS];
398   int                      nscount;
399   size_t                   i;
400   size_t                   entries = 0;
401   ares_buf_t              *ipbuf   = NULL;
402 
403   memset(&res, 0, sizeof(res));
404 
405   if (res_ninit(&res) != 0 || !(res.options & RES_INIT)) {
406     return ARES_EFILE;
407   }
408 
409   nscount = res_getservers(&res, addr, MAXNS);
410 
411   for (i = 0; i < (size_t)nscount; ++i) {
412     char           ipaddr[INET6_ADDRSTRLEN] = "";
413     char          *ipstr                    = NULL;
414     unsigned short port                     = 0;
415     unsigned int   ll_scope                 = 0;
416 
417     sa_family_t    family = addr[i].sin.sin_family;
418     if (family == AF_INET) {
419       ares_inet_ntop(family, &addr[i].sin.sin_addr, ipaddr, sizeof(ipaddr));
420       port = ntohs(addr[i].sin.sin_port);
421     } else if (family == AF_INET6) {
422       ares_inet_ntop(family, &addr[i].sin6.sin6_addr, ipaddr, sizeof(ipaddr));
423       port     = ntohs(addr[i].sin6.sin6_port);
424       ll_scope = addr[i].sin6.sin6_scope_id;
425     } else {
426       continue;
427     }
428 
429 
430     /* [ip]:port%iface */
431     ipbuf = ares_buf_create();
432     if (ipbuf == NULL) {
433       status = ARES_ENOMEM;
434       goto done;
435     }
436 
437     status = ares_buf_append_str(ipbuf, "[");
438     if (status != ARES_SUCCESS) {
439       goto done;
440     }
441 
442     status = ares_buf_append_str(ipbuf, ipaddr);
443     if (status != ARES_SUCCESS) {
444       goto done;
445     }
446 
447     status = ares_buf_append_str(ipbuf, "]");
448     if (status != ARES_SUCCESS) {
449       goto done;
450     }
451 
452     if (port) {
453       status = ares_buf_append_str(ipbuf, ":");
454       if (status != ARES_SUCCESS) {
455         goto done;
456       }
457       status = ares_buf_append_num_dec(ipbuf, port, 0);
458       if (status != ARES_SUCCESS) {
459         goto done;
460       }
461     }
462 
463     if (ll_scope) {
464       status = ares_buf_append_str(ipbuf, "%");
465       if (status != ARES_SUCCESS) {
466         goto done;
467       }
468       status = ares_buf_append_num_dec(ipbuf, ll_scope, 0);
469       if (status != ARES_SUCCESS) {
470         goto done;
471       }
472     }
473 
474     ipstr = ares_buf_finish_str(ipbuf, NULL);
475     ipbuf = NULL;
476     if (ipstr == NULL) {
477       status = ARES_ENOMEM;
478       goto done;
479     }
480 
481     status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, ipstr,
482                                          ARES_TRUE);
483 
484     ares_free(ipstr);
485     if (status != ARES_SUCCESS) {
486       goto done;
487     }
488   }
489 
490   while ((entries < MAXDNSRCH) && res.dnsrch[entries]) {
491     entries++;
492   }
493 
494   if (entries) {
495     sysconfig->domains = ares_malloc_zero(entries * sizeof(char *));
496     if (sysconfig->domains == NULL) {
497       status = ARES_ENOMEM;
498       goto done;
499     } else {
500       sysconfig->ndomains = entries;
501       for (i = 0; i < sysconfig->ndomains; i++) {
502         sysconfig->domains[i] = ares_strdup(res.dnsrch[i]);
503         if (sysconfig->domains[i] == NULL) {
504           status = ARES_ENOMEM;
505           goto done;
506         }
507       }
508     }
509   }
510 
511   if (res.ndots >= 0) {
512     sysconfig->ndots = (size_t)res.ndots;
513   }
514 /* Apple does not allow configuration of retry, so this is a static dummy
515  * value, ignore */
516 #  ifndef __APPLE__
517   if (res.retry > 0) {
518     sysconfig->tries = (size_t)res.retry;
519   }
520 #  endif
521   if (res.options & RES_ROTATE) {
522     sysconfig->rotate = ARES_TRUE;
523   }
524 
525   if (res.retrans > 0) {
526 /* Apple does not allow configuration of retrans, so this is a dummy value
527  * that is extremely high (5s) */
528 #  ifndef __APPLE__
529     if (res.retrans > 0) {
530       sysconfig->timeout_ms = (unsigned int)res.retrans * 1000;
531     }
532 #  endif
533   }
534 
535 done:
536   ares_buf_destroy(ipbuf);
537   res_ndestroy(&res);
538   return status;
539 }
540 #endif
541 
ares_sysconfig_free(ares_sysconfig_t * sysconfig)542 static void ares_sysconfig_free(ares_sysconfig_t *sysconfig)
543 {
544   ares_llist_destroy(sysconfig->sconfig);
545   ares_strsplit_free(sysconfig->domains, sysconfig->ndomains);
546   ares_free(sysconfig->sortlist);
547   ares_free(sysconfig->lookups);
548   memset(sysconfig, 0, sizeof(*sysconfig));
549 }
550 
ares_sysconfig_apply(ares_channel_t * channel,const ares_sysconfig_t * sysconfig)551 static ares_status_t ares_sysconfig_apply(ares_channel_t         *channel,
552                                           const ares_sysconfig_t *sysconfig)
553 {
554   ares_status_t status;
555 
556   if (sysconfig->sconfig && !(channel->optmask & ARES_OPT_SERVERS)) {
557     status = ares_servers_update(channel, sysconfig->sconfig, ARES_FALSE);
558     if (status != ARES_SUCCESS) {
559       return status;
560     }
561   }
562 
563   if (sysconfig->domains && !(channel->optmask & ARES_OPT_DOMAINS)) {
564     /* Make sure we duplicate first then replace so even if there is
565      * ARES_ENOMEM, the channel stays in a good state */
566     char **temp =
567       ares_strsplit_duplicate(sysconfig->domains, sysconfig->ndomains);
568     if (temp == NULL) {
569       return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
570     }
571 
572     ares_strsplit_free(channel->domains, channel->ndomains);
573     channel->domains  = temp;
574     channel->ndomains = sysconfig->ndomains;
575   }
576 
577   if (sysconfig->lookups && !(channel->optmask & ARES_OPT_LOOKUPS)) {
578     char *temp = ares_strdup(sysconfig->lookups);
579     if (temp == NULL) {
580       return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
581     }
582 
583     ares_free(channel->lookups);
584     channel->lookups = temp;
585   }
586 
587   if (sysconfig->sortlist && !(channel->optmask & ARES_OPT_SORTLIST)) {
588     struct apattern *temp =
589       ares_malloc(sizeof(*channel->sortlist) * sysconfig->nsortlist);
590     if (temp == NULL) {
591       return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
592     }
593     memcpy(temp, sysconfig->sortlist,
594            sizeof(*channel->sortlist) * sysconfig->nsortlist);
595 
596     ares_free(channel->sortlist);
597     channel->sortlist = temp;
598     channel->nsort    = sysconfig->nsortlist;
599   }
600 
601   if (!(channel->optmask & ARES_OPT_NDOTS)) {
602     channel->ndots = sysconfig->ndots;
603   }
604 
605   if (sysconfig->tries && !(channel->optmask & ARES_OPT_TRIES)) {
606     channel->tries = sysconfig->tries;
607   }
608 
609   if (sysconfig->timeout_ms && !(channel->optmask & ARES_OPT_TIMEOUTMS)) {
610     channel->timeout = sysconfig->timeout_ms;
611   }
612 
613   if (!(channel->optmask & (ARES_OPT_ROTATE | ARES_OPT_NOROTATE))) {
614     channel->rotate = sysconfig->rotate;
615   }
616 
617   if (sysconfig->usevc) {
618     channel->flags |= ARES_FLAG_USEVC;
619   }
620 
621   return ARES_SUCCESS;
622 }
623 
ares_init_by_sysconfig(ares_channel_t * channel)624 ares_status_t ares_init_by_sysconfig(ares_channel_t *channel)
625 {
626   ares_status_t    status;
627   ares_sysconfig_t sysconfig;
628 
629   memset(&sysconfig, 0, sizeof(sysconfig));
630   sysconfig.ndots = 1; /* Default value if not otherwise set */
631 
632 #if defined(USE_WINSOCK)
633   status = ares_init_sysconfig_windows(channel, &sysconfig);
634 #elif defined(__MVS__)
635   status = ares_init_sysconfig_mvs(channel, &sysconfig);
636 #elif defined(__riscos__)
637   status = ares_init_sysconfig_riscos(channel, &sysconfig);
638 #elif defined(WATT32)
639   status = ares_init_sysconfig_watt32(channel, &sysconfig);
640 #elif defined(ANDROID) || defined(__ANDROID__)
641   status = ares_init_sysconfig_android(channel, &sysconfig);
642 #elif defined(__APPLE__)
643   status = ares_init_sysconfig_macos(channel, &sysconfig);
644 #elif defined(CARES_USE_LIBRESOLV)
645   status = ares_init_sysconfig_libresolv(channel, &sysconfig);
646 #elif defined(__QNX__)
647   status = ares_init_sysconfig_qnx(channel, &sysconfig);
648 #elif OHOS_DNS_PROXY_BY_NETSYS
649   status = ares_init_sysconfig_netsys(channel, &sysconfig);
650 #else
651   status = ares_init_sysconfig_files(channel, &sysconfig, ARES_TRUE);
652 #endif
653 
654   if (status != ARES_SUCCESS) {
655     goto done;
656   }
657 
658   /* Environment is supposed to override sysconfig */
659   status = ares_init_by_environment(&sysconfig);
660   if (status != ARES_SUCCESS) {
661     goto done;
662   }
663 
664   /* Lock when applying the configuration to the channel.  Don't need to
665    * lock prior to this. */
666 
667   ares_channel_lock(channel);
668 
669   status = ares_sysconfig_apply(channel, &sysconfig);
670   ares_channel_unlock(channel);
671 
672   if (status != ARES_SUCCESS) {
673     goto done;
674   }
675 
676 done:
677   ares_sysconfig_free(&sysconfig);
678 
679   return status;
680 }
681