/* MIT License * * Copyright (c) 1998 Massachusetts Institute of Technology * Copyright (c) 2007 Daniel Stenberg * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * SPDX-License-Identifier: MIT */ #include "ares_private.h" #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_NETDB_H # include #endif #ifdef HAVE_ARPA_INET_H # include #endif #include "ares_nameser.h" #if defined(ANDROID) || defined(__ANDROID__) # include # include "ares_android.h" /* From the Bionic sources */ # define DNS_PROP_NAME_PREFIX "net.dns" # define MAX_DNS_PROPERTIES 8 #endif #if defined(CARES_USE_LIBRESOLV) # include #endif #if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H) # include #endif #include "ares_inet_net_pton.h" #include "event/ares_event.h" int ares_init(ares_channel_t **channelptr) { return ares_init_options(channelptr, NULL, 0); } static int ares_query_timeout_cmp_cb(const void *arg1, const void *arg2) { const ares_query_t *q1 = arg1; const ares_query_t *q2 = arg2; if (q1->timeout.sec > q2->timeout.sec) { return 1; } if (q1->timeout.sec < q2->timeout.sec) { return -1; } if (q1->timeout.usec > q2->timeout.usec) { return 1; } if (q1->timeout.usec < q2->timeout.usec) { return -1; } return 0; } static int server_sort_cb(const void *data1, const void *data2) { const ares_server_t *s1 = data1; const ares_server_t *s2 = data2; if (s1->consec_failures < s2->consec_failures) { return -1; } if (s1->consec_failures > s2->consec_failures) { return 1; } if (s1->idx < s2->idx) { return -1; } if (s1->idx > s2->idx) { return 1; } return 0; } static void server_destroy_cb(void *data) { if (data == NULL) { return; /* LCOV_EXCL_LINE: DefensiveCoding */ } ares_destroy_server(data); } static ares_status_t init_by_defaults(ares_channel_t *channel) { char *hostname = NULL; ares_status_t rc = ARES_SUCCESS; #ifdef HAVE_GETHOSTNAME const char *dot; #endif struct ares_addr addr; ares_llist_t *sconfig = NULL; /* Enable EDNS by default */ if (!(channel->optmask & ARES_OPT_FLAGS)) { channel->flags = ARES_FLAG_EDNS; } if (channel->ednspsz == 0) { channel->ednspsz = EDNSPACKETSZ; } if (channel->timeout == 0) { channel->timeout = DEFAULT_TIMEOUT; } if (channel->tries == 0) { #if OHOS_DNS_PROXY_BY_NETSYS channel->tries = 2; // change default tries from 3 to 2 #else channel->tries = DEFAULT_TRIES; #endif } if (ares_slist_len(channel->servers) == 0) { /* Add a default local named server to the channel unless configured not * to (in which case return an error). */ if (channel->flags & ARES_FLAG_NO_DFLT_SVR) { rc = ARES_ENOSERVER; goto error; } addr.family = AF_INET; addr.addr.addr4.s_addr = htonl(INADDR_LOOPBACK); rc = ares_sconfig_append(channel, &sconfig, &addr, 0, 0, NULL); if (rc != ARES_SUCCESS) { goto error; /* LCOV_EXCL_LINE: OutOfMemory */ } rc = ares_servers_update(channel, sconfig, ARES_FALSE); ares_llist_destroy(sconfig); if (rc != ARES_SUCCESS) { goto error; } } if (channel->ndomains == 0) { /* Derive a default domain search list from the kernel hostname, * or set it to empty if the hostname isn't helpful. */ #ifndef HAVE_GETHOSTNAME channel->ndomains = 0; /* default to none */ #else size_t len = 256; channel->ndomains = 0; /* default to none */ hostname = ares_malloc(len); if (!hostname) { rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ goto error; /* LCOV_EXCL_LINE: OutOfMemory */ } if (gethostname(hostname, (GETHOSTNAME_TYPE_ARG2)len) != 0) { /* Lets not treat a gethostname failure as critical, since we * are ok if gethostname doesn't even exist */ *hostname = '\0'; } dot = strchr(hostname, '.'); if (dot) { /* a dot was found */ channel->domains = ares_malloc(sizeof(char *)); if (!channel->domains) { rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ goto error; /* LCOV_EXCL_LINE: OutOfMemory */ } channel->domains[0] = ares_strdup(dot + 1); if (!channel->domains[0]) { rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ goto error; /* LCOV_EXCL_LINE: OutOfMemory */ } channel->ndomains = 1; } #endif } if (channel->nsort == 0) { channel->sortlist = NULL; } if (!channel->lookups) { channel->lookups = ares_strdup("fb"); if (!channel->lookups) { rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ } } /* Set default fields for server failover behavior */ if (!(channel->optmask & ARES_OPT_SERVER_FAILOVER)) { channel->server_retry_chance = DEFAULT_SERVER_RETRY_CHANCE; channel->server_retry_delay = DEFAULT_SERVER_RETRY_DELAY; } error: if (hostname) { ares_free(hostname); } return rc; } int ares_init_options(ares_channel_t **channelptr, const struct ares_options *options, int optmask) { ares_channel_t *channel; ares_status_t status = ARES_SUCCESS; if (ares_library_initialized() != ARES_SUCCESS) { return ARES_ENOTINITIALIZED; /* LCOV_EXCL_LINE: n/a on non-WinSock */ } channel = ares_malloc_zero(sizeof(*channel)); if (!channel) { *channelptr = NULL; return ARES_ENOMEM; } /* We are in a good state */ channel->sys_up = ARES_TRUE; /* One option where zero is valid, so set default value here */ channel->ndots = 1; status = ares_channel_threading_init(channel); if (status != ARES_SUCCESS) { goto done; } /* Generate random key */ channel->rand_state = ares_init_rand_state(); if (channel->rand_state == NULL) { status = ARES_ENOMEM; DEBUGF(fprintf(stderr, "Error: init_id_key failed: %s\n", ares_strerror(status))); goto done; } /* Initialize Server List */ channel->servers = ares_slist_create(channel->rand_state, server_sort_cb, server_destroy_cb); if (channel->servers == NULL) { status = ARES_ENOMEM; goto done; } /* Initialize our lists of queries */ channel->all_queries = ares_llist_create(NULL); if (channel->all_queries == NULL) { status = ARES_ENOMEM; goto done; } channel->queries_by_qid = ares_htable_szvp_create(NULL); if (channel->queries_by_qid == NULL) { status = ARES_ENOMEM; goto done; } channel->queries_by_timeout = ares_slist_create(channel->rand_state, ares_query_timeout_cmp_cb, NULL); if (channel->queries_by_timeout == NULL) { status = ARES_ENOMEM; goto done; } channel->connnode_by_socket = ares_htable_asvp_create(NULL); if (channel->connnode_by_socket == NULL) { status = ARES_ENOMEM; goto done; } /* Initialize configuration by each of the four sources, from highest * precedence to lowest. */ status = ares_init_by_options(channel, options, optmask); if (status != ARES_SUCCESS) { DEBUGF(fprintf(stderr, "Error: init_by_options failed: %s\n", ares_strerror(status))); /* If we fail to apply user-specified options, fail the whole init process */ goto done; } /* Go ahead and let it initialize the query cache even if the ttl is 0 and * completely unused. This reduces the number of different code paths that * might be followed even if there is a minor performance hit. */ status = ares_qcache_create(channel->rand_state, channel->qcache_max_ttl, &channel->qcache); if (status != ARES_SUCCESS) { goto done; /* LCOV_EXCL_LINE: OutOfMemory */ } if (status == ARES_SUCCESS) { status = ares_init_by_sysconfig(channel); if (status != ARES_SUCCESS) { DEBUGF(fprintf(stderr, "Error: init_by_sysconfig failed: %s\n", ares_strerror(status))); } } /* * No matter what failed or succeeded, seed defaults to provide * useful behavior for things that we missed. */ status = init_by_defaults(channel); if (status != ARES_SUCCESS) { DEBUGF(fprintf(stderr, "Error: init_by_defaults failed: %s\n", ares_strerror(status))); goto done; } ares_set_socket_functions_def(channel); /* Initialize the event thread */ if (channel->optmask & ARES_OPT_EVENT_THREAD) { ares_event_thread_t *e = NULL; status = ares_event_thread_init(channel); if (status != ARES_SUCCESS) { goto done; /* LCOV_EXCL_LINE: UntestablePath */ } /* Initialize monitor for configuration changes. In some rare cases, * ARES_ENOTIMP may occur (OpenWatcom), ignore this. */ e = channel->sock_state_cb_data; status = ares_event_configchg_init(&e->configchg, e); if (status != ARES_SUCCESS && status != ARES_ENOTIMP) { goto done; /* LCOV_EXCL_LINE: UntestablePath */ } status = ARES_SUCCESS; } done: if (status != ARES_SUCCESS) { ares_destroy(channel); return (int)status; } *channelptr = channel; return ARES_SUCCESS; } static void *ares_reinit_thread(void *arg) { ares_channel_t *channel = arg; ares_status_t status; /* ares_init_by_sysconfig() will lock when applying the config, but not * when retrieving. */ status = ares_init_by_sysconfig(channel); if (status != ARES_SUCCESS) { DEBUGF(fprintf(stderr, "Error: init_by_sysconfig failed: %s\n", ares_strerror(status))); } ares_channel_lock(channel); /* Flush cached queries on reinit */ if (status == ARES_SUCCESS && channel->qcache) { ares_qcache_flush(channel->qcache); } channel->reinit_pending = ARES_FALSE; ares_channel_unlock(channel); return NULL; } ares_status_t ares_reinit(ares_channel_t *channel) { ares_status_t status = ARES_SUCCESS; if (channel == NULL) { return ARES_EFORMERR; } ares_channel_lock(channel); /* If a reinit is already in process, lets not do it again. Or if we are * shutting down, skip. */ if (!channel->sys_up || channel->reinit_pending) { ares_channel_unlock(channel); return ARES_SUCCESS; } channel->reinit_pending = ARES_TRUE; ares_channel_unlock(channel); if (ares_threadsafety()) { /* clean up the prior reinit process's thread. We know the thread isn't * running since reinit_pending was false */ if (channel->reinit_thread != NULL) { void *rv; ares_thread_join(channel->reinit_thread, &rv); channel->reinit_thread = NULL; } /* Spawn a new thread */ status = ares_thread_create(&channel->reinit_thread, ares_reinit_thread, channel); if (status != ARES_SUCCESS) { /* LCOV_EXCL_START: UntestablePath */ ares_channel_lock(channel); channel->reinit_pending = ARES_FALSE; ares_channel_unlock(channel); /* LCOV_EXCL_STOP */ } } else { /* Threading support not available, call directly */ ares_reinit_thread(channel); } return status; } /* ares_dup() duplicates a channel handle with all its options and returns a new channel handle */ int ares_dup(ares_channel_t **dest, const ares_channel_t *src) { struct ares_options opts; ares_status_t rc; int optmask; if (dest == NULL || src == NULL) { return ARES_EFORMERR; } *dest = NULL; /* in case of failure return NULL explicitly */ /* First get the options supported by the old ares_save_options() function, which is most of them */ rc = (ares_status_t)ares_save_options(src, &opts, &optmask); if (rc != ARES_SUCCESS) { ares_destroy_options(&opts); goto done; } /* Then create the new channel with those options */ rc = (ares_status_t)ares_init_options(dest, &opts, optmask); /* destroy the options copy to not leak any memory */ ares_destroy_options(&opts); if (rc != ARES_SUCCESS) { goto done; } ares_channel_lock(src); /* Now clone the options that ares_save_options() doesn't support, but are * user-provided */ (*dest)->sock_create_cb = src->sock_create_cb; (*dest)->sock_create_cb_data = src->sock_create_cb_data; (*dest)->sock_config_cb = src->sock_config_cb; (*dest)->sock_config_cb_data = src->sock_config_cb_data; memcpy(&(*dest)->sock_funcs, &(src->sock_funcs), sizeof((*dest)->sock_funcs)); (*dest)->sock_func_cb_data = src->sock_func_cb_data; (*dest)->legacy_sock_funcs = src->legacy_sock_funcs; (*dest)->legacy_sock_funcs_cb_data = src->legacy_sock_funcs_cb_data; (*dest)->server_state_cb = src->server_state_cb; (*dest)->server_state_cb_data = src->server_state_cb_data; ares_strcpy((*dest)->local_dev_name, src->local_dev_name, sizeof((*dest)->local_dev_name)); (*dest)->local_ip4 = src->local_ip4; memcpy((*dest)->local_ip6, src->local_ip6, sizeof(src->local_ip6)); ares_channel_unlock(src); /* Servers are a bit unique as ares_init_options() only allows ipv4 servers * and not a port per server, but there are other user specified ways, that * too will toggle the optmask ARES_OPT_SERVERS to let us know. If that's * the case, pull them in. * * We don't want to clone system-configuration servers though. * * We must use the "csv" format to get things like link-local address support */ if (optmask & ARES_OPT_SERVERS) { char *csv = ares_get_servers_csv(src); if (csv == NULL) { /* LCOV_EXCL_START: OutOfMemory */ ares_destroy(*dest); *dest = NULL; rc = ARES_ENOMEM; goto done; /* LCOV_EXCL_STOP */ } rc = (ares_status_t)ares_set_servers_ports_csv(*dest, csv); ares_free_string(csv); if (rc != ARES_SUCCESS) { /* LCOV_EXCL_START: OutOfMemory */ ares_destroy(*dest); *dest = NULL; goto done; /* LCOV_EXCL_STOP */ } } rc = ARES_SUCCESS; done: return (int)rc; /* everything went fine */ } void ares_set_local_ip4(ares_channel_t *channel, unsigned int local_ip) { if (channel == NULL) { return; } ares_channel_lock(channel); channel->local_ip4 = local_ip; ares_channel_unlock(channel); } /* local_ip6 should be 16 bytes in length */ void ares_set_local_ip6(ares_channel_t *channel, const unsigned char *local_ip6) { if (channel == NULL) { return; } ares_channel_lock(channel); memcpy(&channel->local_ip6, local_ip6, sizeof(channel->local_ip6)); ares_channel_unlock(channel); } /* local_dev_name should be null terminated. */ void ares_set_local_dev(ares_channel_t *channel, const char *local_dev_name) { if (channel == NULL) { return; } ares_channel_lock(channel); ares_strcpy(channel->local_dev_name, local_dev_name, sizeof(channel->local_dev_name)); channel->local_dev_name[sizeof(channel->local_dev_name) - 1] = 0; ares_channel_unlock(channel); } int ares_set_sortlist(ares_channel_t *channel, const char *sortstr) { size_t nsort = 0; struct apattern *sortlist = NULL; ares_status_t status; if (!channel) { return ARES_ENODATA; } ares_channel_lock(channel); status = ares_parse_sortlist(&sortlist, &nsort, sortstr); if (status == ARES_SUCCESS && sortlist) { if (channel->sortlist) { ares_free(channel->sortlist); } channel->sortlist = sortlist; channel->nsort = nsort; /* Save sortlist as if it was passed in as an option */ channel->optmask |= ARES_OPT_SORTLIST; } ares_channel_unlock(channel); return (int)status; }