#include "ares_private.h"
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#include "ares_nameser.h"
#if defined(ANDROID) || defined(__ANDROID__)
# include <sys/system_properties.h>
# include "ares_android.h"
# define DNS_PROP_NAME_PREFIX "net.dns"
# define MAX_DNS_PROPERTIES 8
#endif
#if defined(CARES_USE_LIBRESOLV)
# include <resolv.h>
#endif
#if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H)
# include <iphlpapi.h>
#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
;
}
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;
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) {
channel->tries = DEFAULT_TRIES;
}
if
(ares_slist_len(channel->servers) == 0) {
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;
}
rc = ares_servers_update(channel, sconfig, ARES_FALSE);
ares_llist_destroy(sconfig);
if
(rc != ARES_SUCCESS) {
goto
error;
}
}
if
(channel->ndomains == 0) {
#ifndef HAVE_GETHOSTNAME
channel->ndomains = 0;
#else
size_t
len = 256;
channel->ndomains = 0;
hostname = ares_malloc(len);
if
(!hostname) {
rc = ARES_ENOMEM;
goto
error;
}
if
(gethostname(hostname, (GETHOSTNAME_TYPE_ARG2)len) != 0) {
*hostname =
'\0'
;
}
dot =
strchr
(hostname,
'.'
);
if
(dot) {
channel->domains = ares_malloc(
sizeof
(
char
*));
if
(!channel->domains) {
rc = ARES_ENOMEM;
goto
error;
}
channel->domains[0] = ares_strdup(dot + 1);
if
(!channel->domains[0]) {
rc = ARES_ENOMEM;
goto
error;
}
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;
}
}
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;
}
channel = ares_malloc_zero(
sizeof
(*channel));
if
(!channel) {
*channelptr = NULL;
return
ARES_ENOMEM;
}
channel->sys_up = ARES_TRUE;
channel->ndots = 1;
status = ares_channel_threading_init(channel);
if
(status != ARES_SUCCESS) {
goto
done;
}
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;
}
channel->servers =
ares_slist_create(channel->rand_state, server_sort_cb, server_destroy_cb);
if
(channel->servers == NULL) {
status = ARES_ENOMEM;
goto
done;
}
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;
}
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)));
goto
done;
}
status = ares_qcache_create(channel->rand_state, channel->qcache_max_ttl,
&channel->qcache);
if
(status != ARES_SUCCESS) {
goto
done;
}
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)));
}
}
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);
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;
}
e = channel->sock_state_cb_data;
status = ares_event_configchg_init(&e->configchg, e);
if
(status != ARES_SUCCESS && status != ARES_ENOTIMP) {
goto
done;
}
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;
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);
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
(!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()) {
if
(channel->reinit_thread != NULL) {
void
*rv;
ares_thread_join(channel->reinit_thread, &rv);
channel->reinit_thread = NULL;
}
status =
ares_thread_create(&channel->reinit_thread, ares_reinit_thread, channel);
if
(status != ARES_SUCCESS) {
ares_channel_lock(channel);
channel->reinit_pending = ARES_FALSE;
ares_channel_unlock(channel);
}
}
else
{
ares_reinit_thread(channel);
}
return
status;
}
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;
rc = (ares_status_t)ares_save_options(src, &opts, &optmask);
if
(rc != ARES_SUCCESS) {
ares_destroy_options(&opts);
goto
done;
}
rc = (ares_status_t)ares_init_options(dest, &opts, optmask);
ares_destroy_options(&opts);
if
(rc != ARES_SUCCESS) {
goto
done;
}
ares_channel_lock(src);
(*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);
if
(optmask & ARES_OPT_SERVERS) {
char
*csv = ares_get_servers_csv(src);
if
(csv == NULL) {
ares_destroy(*dest);
*dest = NULL;
rc = ARES_ENOMEM;
goto
done;
}
rc = (ares_status_t)ares_set_servers_ports_csv(*dest, csv);
ares_free_string(csv);
if
(rc != ARES_SUCCESS) {
ares_destroy(*dest);
*dest = NULL;
goto
done;
}
}
rc = ARES_SUCCESS;
done:
return
(
int
)rc;
}
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);
}
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);
}
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;
channel->optmask |= ARES_OPT_SORTLIST;
}
ares_channel_unlock(channel);
return
(
int
)status;
}