|
Packit |
6c4009 |
/* Extended resolver state separate from struct __res_state.
|
|
Packit |
6c4009 |
Copyright (C) 2017-2018 Free Software Foundation, Inc.
|
|
Packit |
6c4009 |
This file is part of the GNU C Library.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
The GNU C Library is free software; you can redistribute it and/or
|
|
Packit |
6c4009 |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6c4009 |
License as published by the Free Software Foundation; either
|
|
Packit |
6c4009 |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
The GNU C Library is distributed in the hope that it will be useful,
|
|
Packit |
6c4009 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6c4009 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6c4009 |
Lesser General Public License for more details.
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6c4009 |
License along with the GNU C Library; if not, see
|
|
Packit |
6c4009 |
<http://www.gnu.org/licenses/>. */
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#include <resolv_conf.h>
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
#include <alloc_buffer.h>
|
|
Packit |
6c4009 |
#include <assert.h>
|
|
Packit |
6c4009 |
#include <libc-lock.h>
|
|
Packit |
6c4009 |
#include <resolv-internal.h>
|
|
Packit |
6c4009 |
#include <sys/stat.h>
|
|
Packit |
6c4009 |
#include <libc-symbols.h>
|
|
Packit Service |
662601 |
#include <file_change_detection.h>
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* _res._u._ext.__glibc_extension_index is used as an index into a
|
|
Packit |
6c4009 |
struct resolv_conf_array object. The intent of this construction
|
|
Packit |
6c4009 |
is to make reasonably sure that even if struct __res_state objects
|
|
Packit |
6c4009 |
are copied around and patched by applications, we can still detect
|
|
Packit |
6c4009 |
accesses to stale extended resolver state. The array elements are
|
|
Packit |
6c4009 |
either struct resolv_conf * pointers (if the LSB is cleared) or
|
|
Packit |
6c4009 |
free list entries (if the LSB is set). The free list is used to
|
|
Packit |
6c4009 |
speed up finding available entries in the array. */
|
|
Packit |
6c4009 |
#define DYNARRAY_STRUCT resolv_conf_array
|
|
Packit |
6c4009 |
#define DYNARRAY_ELEMENT uintptr_t
|
|
Packit |
6c4009 |
#define DYNARRAY_PREFIX resolv_conf_array_
|
|
Packit |
6c4009 |
#define DYNARRAY_INITIAL_SIZE 0
|
|
Packit |
6c4009 |
#include <malloc/dynarray-skeleton.c>
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* A magic constant for XORing the extension index
|
|
Packit |
6c4009 |
(_res._u._ext.__glibc_extension_index). This makes it less likely
|
|
Packit |
6c4009 |
that a valid index is created by accident. In particular, a zero
|
|
Packit |
6c4009 |
value leads to an invalid index. */
|
|
Packit |
6c4009 |
#define INDEX_MAGIC 0x26a8fa5e48af8061ULL
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Global resolv.conf-related state. */
|
|
Packit |
6c4009 |
struct resolv_conf_global
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* struct __res_state objects contain the extension index
|
|
Packit |
6c4009 |
(_res._u._ext.__glibc_extension_index ^ INDEX_MAGIC), which
|
|
Packit |
6c4009 |
refers to an element of this array. When a struct resolv_conf
|
|
Packit |
6c4009 |
object (extended resolver state) is associated with a struct
|
|
Packit |
6c4009 |
__res_state object (legacy resolver state), its reference count
|
|
Packit |
6c4009 |
is increased and added to this array. Conversely, if the
|
|
Packit |
6c4009 |
extended state is detached from the basic state (during
|
|
Packit |
6c4009 |
reinitialization or deallocation), the index is decremented, and
|
|
Packit |
6c4009 |
the array element is overwritten with NULL. */
|
|
Packit |
6c4009 |
struct resolv_conf_array array;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Start of the free list in the array. Zero if the free list is
|
|
Packit |
6c4009 |
empty. Otherwise, free_list_start >> 1 is the first element of
|
|
Packit |
6c4009 |
the free list (and the free list entries all have their LSB set
|
|
Packit |
6c4009 |
and are shifted one to the left). */
|
|
Packit |
6c4009 |
uintptr_t free_list_start;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Cached current configuration object for /etc/resolv.conf. */
|
|
Packit |
6c4009 |
struct resolv_conf *conf_current;
|
|
Packit |
6c4009 |
|
|
Packit Service |
662601 |
/* File system identification for /etc/resolv.conf. */
|
|
Packit Service |
662601 |
struct file_change_detection file_resolve_conf;
|
|
Packit |
6c4009 |
};
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Lazily allocated storage for struct resolv_conf_global. */
|
|
Packit |
6c4009 |
static struct resolv_conf_global *global;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* The lock synchronizes access to global and *global. It also
|
|
Packit |
6c4009 |
protects the __refcount member of struct resolv_conf. */
|
|
Packit |
6c4009 |
__libc_lock_define_initialized (static, lock);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Ensure that GLOBAL is allocated and lock it. Return NULL if
|
|
Packit |
6c4009 |
memory allocation failes. */
|
|
Packit |
6c4009 |
static struct resolv_conf_global *
|
|
Packit |
6c4009 |
get_locked_global (void)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
__libc_lock_lock (lock);
|
|
Packit |
6c4009 |
/* Use relaxed MO through because of load outside the lock in
|
|
Packit |
6c4009 |
__resolv_conf_detach. */
|
|
Packit |
6c4009 |
struct resolv_conf_global *global_copy = atomic_load_relaxed (&global);
|
|
Packit |
6c4009 |
if (global_copy == NULL)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
global_copy = calloc (1, sizeof (*global));
|
|
Packit |
6c4009 |
if (global_copy == NULL)
|
|
Packit |
6c4009 |
return NULL;
|
|
Packit |
6c4009 |
atomic_store_relaxed (&global, global_copy);
|
|
Packit |
6c4009 |
resolv_conf_array_init (&global_copy->array);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
return global_copy;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Relinquish the lock acquired by get_locked_global. */
|
|
Packit |
6c4009 |
static void
|
|
Packit |
6c4009 |
put_locked_global (struct resolv_conf_global *global_copy)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
__libc_lock_unlock (lock);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Decrement the reference counter. The caller must acquire the lock
|
|
Packit |
6c4009 |
around the function call. */
|
|
Packit |
6c4009 |
static void
|
|
Packit |
6c4009 |
conf_decrement (struct resolv_conf *conf)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
assert (conf->__refcount > 0);
|
|
Packit |
6c4009 |
if (--conf->__refcount == 0)
|
|
Packit |
6c4009 |
free (conf);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
struct resolv_conf *
|
|
Packit |
6c4009 |
__resolv_conf_get_current (void)
|
|
Packit |
6c4009 |
{
|
|
Packit Service |
662601 |
struct file_change_detection initial;
|
|
Packit Service |
bbd36c |
if (!__file_change_detection_for_path (&initial, _PATH_RESCONF))
|
|
Packit Service |
662601 |
return NULL;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
struct resolv_conf_global *global_copy = get_locked_global ();
|
|
Packit |
6c4009 |
if (global_copy == NULL)
|
|
Packit |
6c4009 |
return NULL;
|
|
Packit |
6c4009 |
struct resolv_conf *conf;
|
|
Packit |
6c4009 |
if (global_copy->conf_current != NULL
|
|
Packit Service |
bbd36c |
&& __file_is_unchanged (&initial, &global_copy->file_resolve_conf))
|
|
Packit |
6c4009 |
/* We can reuse the cached configuration object. */
|
|
Packit |
6c4009 |
conf = global_copy->conf_current;
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Parse configuration while holding the lock. This avoids
|
|
Packit |
6c4009 |
duplicate work. */
|
|
Packit Service |
b2851f |
struct file_change_detection after_load;
|
|
Packit Service |
b2851f |
conf = __resolv_conf_load (NULL, &after_load);
|
|
Packit |
6c4009 |
if (conf != NULL)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (global_copy->conf_current != NULL)
|
|
Packit |
6c4009 |
conf_decrement (global_copy->conf_current);
|
|
Packit |
6c4009 |
global_copy->conf_current = conf; /* Takes ownership. */
|
|
Packit |
6c4009 |
|
|
Packit Service |
b2851f |
/* Update file change detection data, but only if it matches
|
|
Packit Service |
b2851f |
the initial measurement. This avoids an ABA race in case
|
|
Packit Service |
b2851f |
/etc/resolv.conf is temporarily replaced while the file
|
|
Packit Service |
b2851f |
is read (after the initial measurement), and restored to
|
|
Packit Service |
b2851f |
the initial version later. */
|
|
Packit Service |
bbd36c |
if (__file_is_unchanged (&initial, &after_load))
|
|
Packit Service |
b2851f |
global_copy->file_resolve_conf = after_load;
|
|
Packit Service |
b2851f |
else
|
|
Packit Service |
b2851f |
/* If there is a discrepancy, trigger a reload during the
|
|
Packit Service |
b2851f |
next use. */
|
|
Packit Service |
b2851f |
global_copy->file_resolve_conf.size = -1;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if (conf != NULL)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Return an additional reference. */
|
|
Packit |
6c4009 |
assert (conf->__refcount > 0);
|
|
Packit |
6c4009 |
++conf->__refcount;
|
|
Packit |
6c4009 |
assert (conf->__refcount > 0);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
put_locked_global (global_copy);
|
|
Packit |
6c4009 |
return conf;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Internal implementation of __resolv_conf_get, without validation
|
|
Packit |
6c4009 |
against *RESP. */
|
|
Packit |
6c4009 |
static struct resolv_conf *
|
|
Packit |
6c4009 |
resolv_conf_get_1 (const struct __res_state *resp)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Not initialized, and therefore no assoicated context. */
|
|
Packit |
6c4009 |
if (!(resp->options & RES_INIT))
|
|
Packit |
6c4009 |
return NULL;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
struct resolv_conf_global *global_copy = get_locked_global ();
|
|
Packit |
6c4009 |
if (global_copy == NULL)
|
|
Packit |
6c4009 |
/* A memory allocation failure here means that no associated
|
|
Packit |
6c4009 |
contexts exists, so returning NULL is correct. */
|
|
Packit |
6c4009 |
return NULL;
|
|
Packit |
6c4009 |
size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
|
|
Packit |
6c4009 |
struct resolv_conf *conf = NULL;
|
|
Packit |
6c4009 |
if (index < resolv_conf_array_size (&global_copy->array))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
|
|
Packit |
6c4009 |
if (!(*slot & 1))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
conf = (struct resolv_conf *) *slot;
|
|
Packit |
6c4009 |
assert (conf->__refcount > 0);
|
|
Packit |
6c4009 |
++conf->__refcount;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
put_locked_global (global_copy);
|
|
Packit |
6c4009 |
return conf;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Return true if both IPv4 addresses are equal. */
|
|
Packit |
6c4009 |
static bool
|
|
Packit |
6c4009 |
same_address_v4 (const struct sockaddr_in *left,
|
|
Packit |
6c4009 |
const struct sockaddr_in *right)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
return left->sin_addr.s_addr == right->sin_addr.s_addr
|
|
Packit |
6c4009 |
&& left->sin_port == right->sin_port;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Return true if both IPv6 addresses are equal. This ignores the
|
|
Packit |
6c4009 |
flow label. */
|
|
Packit |
6c4009 |
static bool
|
|
Packit |
6c4009 |
same_address_v6 (const struct sockaddr_in6 *left,
|
|
Packit |
6c4009 |
const struct sockaddr_in6 *right)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
return memcmp (&left->sin6_addr, &right->sin6_addr,
|
|
Packit |
6c4009 |
sizeof (left->sin6_addr)) == 0
|
|
Packit |
6c4009 |
&& left->sin6_port == right->sin6_port
|
|
Packit |
6c4009 |
&& left->sin6_scope_id == right->sin6_scope_id;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
static bool
|
|
Packit |
6c4009 |
same_address (const struct sockaddr *left, const struct sockaddr *right)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (left->sa_family != right->sa_family)
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
switch (left->sa_family)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
case AF_INET:
|
|
Packit |
6c4009 |
return same_address_v4 ((const struct sockaddr_in *) left,
|
|
Packit |
6c4009 |
(const struct sockaddr_in *) right);
|
|
Packit |
6c4009 |
case AF_INET6:
|
|
Packit |
6c4009 |
return same_address_v6 ((const struct sockaddr_in6 *) left,
|
|
Packit |
6c4009 |
(const struct sockaddr_in6 *) right);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check that *RESP and CONF match. Used by __resolv_conf_get. */
|
|
Packit |
6c4009 |
static bool
|
|
Packit |
6c4009 |
resolv_conf_matches (const struct __res_state *resp,
|
|
Packit |
6c4009 |
const struct resolv_conf *conf)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* NB: Do not compare the options, retrans, retry, ndots. These can
|
|
Packit |
6c4009 |
be changed by applicaiton. */
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check that the name servers in *RESP have not been modified by
|
|
Packit |
6c4009 |
the application. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
size_t nserv = conf->nameserver_list_size;
|
|
Packit |
6c4009 |
if (nserv > MAXNS)
|
|
Packit |
6c4009 |
nserv = MAXNS;
|
|
Packit |
6c4009 |
/* _ext.nscount is 0 until initialized by res_send.c. */
|
|
Packit |
6c4009 |
if (resp->nscount != nserv
|
|
Packit |
6c4009 |
|| (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < nserv; ++i)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (resp->nsaddr_list[i].sin_family == 0)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (resp->_u._ext.nsaddrs[i]->sin6_family != AF_INET6)
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
if (!same_address ((struct sockaddr *) resp->_u._ext.nsaddrs[i],
|
|
Packit |
6c4009 |
conf->nameserver_list[i]))
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
else if (resp->nsaddr_list[i].sin_family != AF_INET)
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
else if (!same_address ((struct sockaddr *) &resp->nsaddr_list[i],
|
|
Packit |
6c4009 |
conf->nameserver_list[i]))
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check that the search list in *RESP has not been modified by the
|
|
Packit |
6c4009 |
application. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (resp->dnsrch[0] == NULL)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Empty search list. No default domain name. */
|
|
Packit |
6c4009 |
return conf->search_list_size == 0 && resp->defdname[0] == '\0';
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if (resp->dnsrch[0] != resp->defdname)
|
|
Packit |
6c4009 |
/* If the search list is not empty, it must start with the
|
|
Packit |
6c4009 |
default domain name. */
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
size_t nsearch;
|
|
Packit |
6c4009 |
for (nsearch = 0; nsearch < MAXDNSRCH; ++nsearch)
|
|
Packit |
6c4009 |
if (resp->dnsrch[nsearch] == NULL)
|
|
Packit |
6c4009 |
break;
|
|
Packit |
6c4009 |
if (nsearch > MAXDNSRCH)
|
|
Packit |
6c4009 |
/* Search list is not null-terminated. */
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
size_t search_list_size = 0;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < conf->search_list_size; ++i)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (resp->dnsrch[i] != NULL)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
search_list_size += strlen (resp->dnsrch[i]) + 1;
|
|
Packit |
6c4009 |
if (strcmp (resp->dnsrch[i], conf->search_list[i]) != 0)
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* resp->dnsrch is truncated if the number of elements
|
|
Packit |
6c4009 |
exceeds MAXDNSRCH, or if the combined storage space for
|
|
Packit |
6c4009 |
the search list exceeds what can be stored in
|
|
Packit |
6c4009 |
resp->defdname. */
|
|
Packit |
6c4009 |
if (i == MAXDNSRCH || search_list_size > sizeof (resp->dnsrch))
|
|
Packit |
6c4009 |
break;
|
|
Packit |
6c4009 |
/* Otherwise, a mismatch indicates a match failure. */
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Check that the sort list has not been modified. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
size_t nsort = conf->sort_list_size;
|
|
Packit |
6c4009 |
if (nsort > MAXRESOLVSORT)
|
|
Packit |
6c4009 |
nsort = MAXRESOLVSORT;
|
|
Packit |
6c4009 |
if (resp->nsort != nsort)
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < nsort; ++i)
|
|
Packit |
6c4009 |
if (resp->sort_list[i].addr.s_addr != conf->sort_list[i].addr.s_addr
|
|
Packit |
6c4009 |
|| resp->sort_list[i].mask != conf->sort_list[i].mask)
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
return true;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
struct resolv_conf *
|
|
Packit |
6c4009 |
__resolv_conf_get (struct __res_state *resp)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
struct resolv_conf *conf = resolv_conf_get_1 (resp);
|
|
Packit |
6c4009 |
if (conf == NULL)
|
|
Packit |
6c4009 |
return NULL;
|
|
Packit |
6c4009 |
if (resolv_conf_matches (resp, conf))
|
|
Packit |
6c4009 |
return conf;
|
|
Packit |
6c4009 |
__resolv_conf_put (conf);
|
|
Packit |
6c4009 |
return NULL;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
void
|
|
Packit |
6c4009 |
__resolv_conf_put (struct resolv_conf *conf)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (conf == NULL)
|
|
Packit |
6c4009 |
return;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
__libc_lock_lock (lock);
|
|
Packit |
6c4009 |
conf_decrement (conf);
|
|
Packit |
6c4009 |
__libc_lock_unlock (lock);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
struct resolv_conf *
|
|
Packit |
6c4009 |
__resolv_conf_allocate (const struct resolv_conf *init)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Allocate in decreasing order of alignment. */
|
|
Packit |
6c4009 |
_Static_assert (__alignof__ (const char *const *)
|
|
Packit |
6c4009 |
<= __alignof__ (struct resolv_conf), "alignment");
|
|
Packit |
6c4009 |
_Static_assert (__alignof__ (struct sockaddr_in6)
|
|
Packit |
6c4009 |
<= __alignof__ (const char *const *), "alignment");
|
|
Packit |
6c4009 |
_Static_assert (__alignof__ (struct sockaddr_in)
|
|
Packit |
6c4009 |
== __alignof__ (struct sockaddr_in6), "alignment");
|
|
Packit |
6c4009 |
_Static_assert (__alignof__ (struct resolv_sortlist_entry)
|
|
Packit |
6c4009 |
<= __alignof__ (struct sockaddr_in), "alignment");
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Space needed by the nameserver addresses. */
|
|
Packit |
6c4009 |
size_t address_space = 0;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < init->nameserver_list_size; ++i)
|
|
Packit |
6c4009 |
if (init->nameserver_list[i]->sa_family == AF_INET)
|
|
Packit |
6c4009 |
address_space += sizeof (struct sockaddr_in);
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
assert (init->nameserver_list[i]->sa_family == AF_INET6);
|
|
Packit |
6c4009 |
address_space += sizeof (struct sockaddr_in6);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Space needed by the search list strings. */
|
|
Packit |
6c4009 |
size_t string_space = 0;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < init->search_list_size; ++i)
|
|
Packit |
6c4009 |
string_space += strlen (init->search_list[i]) + 1;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Allocate the buffer. */
|
|
Packit |
6c4009 |
void *ptr;
|
|
Packit |
6c4009 |
struct alloc_buffer buffer = alloc_buffer_allocate
|
|
Packit |
6c4009 |
(sizeof (struct resolv_conf)
|
|
Packit |
6c4009 |
+ init->nameserver_list_size * sizeof (init->nameserver_list[0])
|
|
Packit |
6c4009 |
+ address_space
|
|
Packit |
6c4009 |
+ init->search_list_size * sizeof (init->search_list[0])
|
|
Packit |
6c4009 |
+ init->sort_list_size * sizeof (init->sort_list[0])
|
|
Packit |
6c4009 |
+ string_space,
|
|
Packit |
6c4009 |
&ptr);
|
|
Packit |
6c4009 |
struct resolv_conf *conf
|
|
Packit |
6c4009 |
= alloc_buffer_alloc (&buffer, struct resolv_conf);
|
|
Packit |
6c4009 |
if (conf == NULL)
|
|
Packit |
6c4009 |
/* Memory allocation failure. */
|
|
Packit |
6c4009 |
return NULL;
|
|
Packit |
6c4009 |
assert (conf == ptr);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Initialize the contents. */
|
|
Packit |
6c4009 |
conf->__refcount = 1;
|
|
Packit |
6c4009 |
conf->retrans = init->retrans;
|
|
Packit |
6c4009 |
conf->retry = init->retry;
|
|
Packit |
6c4009 |
conf->options = init->options;
|
|
Packit |
6c4009 |
conf->ndots = init->ndots;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Allocate the arrays with pointers. These must come first because
|
|
Packit |
6c4009 |
they have the highets alignment. */
|
|
Packit |
6c4009 |
conf->nameserver_list_size = init->nameserver_list_size;
|
|
Packit |
6c4009 |
const struct sockaddr **nameserver_array = alloc_buffer_alloc_array
|
|
Packit |
6c4009 |
(&buffer, const struct sockaddr *, init->nameserver_list_size);
|
|
Packit |
6c4009 |
conf->nameserver_list = nameserver_array;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
conf->search_list_size = init->search_list_size;
|
|
Packit |
6c4009 |
const char **search_array = alloc_buffer_alloc_array
|
|
Packit |
6c4009 |
(&buffer, const char *, init->search_list_size);
|
|
Packit |
6c4009 |
conf->search_list = search_array;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Fill the name server list array. */
|
|
Packit |
6c4009 |
for (size_t i = 0; i < init->nameserver_list_size; ++i)
|
|
Packit |
6c4009 |
if (init->nameserver_list[i]->sa_family == AF_INET)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
struct sockaddr_in *sa = alloc_buffer_alloc
|
|
Packit |
6c4009 |
(&buffer, struct sockaddr_in);
|
|
Packit |
6c4009 |
*sa = *(struct sockaddr_in *) init->nameserver_list[i];
|
|
Packit |
6c4009 |
nameserver_array[i] = (struct sockaddr *) sa;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
struct sockaddr_in6 *sa = alloc_buffer_alloc
|
|
Packit |
6c4009 |
(&buffer, struct sockaddr_in6);
|
|
Packit |
6c4009 |
*sa = *(struct sockaddr_in6 *) init->nameserver_list[i];
|
|
Packit |
6c4009 |
nameserver_array[i] = (struct sockaddr *) sa;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Allocate and fill the sort list array. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
conf->sort_list_size = init->sort_list_size;
|
|
Packit |
6c4009 |
struct resolv_sortlist_entry *array = alloc_buffer_alloc_array
|
|
Packit |
6c4009 |
(&buffer, struct resolv_sortlist_entry, init->sort_list_size);
|
|
Packit |
6c4009 |
conf->sort_list = array;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < init->sort_list_size; ++i)
|
|
Packit |
6c4009 |
array[i] = init->sort_list[i];
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Fill the search list array. This must come last because the
|
|
Packit |
6c4009 |
strings are the least aligned part of the allocation. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
for (size_t i = 0; i < init->search_list_size; ++i)
|
|
Packit |
6c4009 |
search_array[i] = alloc_buffer_copy_string
|
|
Packit |
6c4009 |
(&buffer, init->search_list[i]);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
assert (!alloc_buffer_has_failed (&buffer));
|
|
Packit |
6c4009 |
return conf;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Update *RESP from the extended state. */
|
|
Packit |
6c4009 |
static __attribute__ ((nonnull (1, 2), warn_unused_result)) bool
|
|
Packit |
6c4009 |
update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
resp->defdname[0] = '\0';
|
|
Packit |
6c4009 |
resp->pfcode = 0;
|
|
Packit |
6c4009 |
resp->_vcsock = -1;
|
|
Packit |
6c4009 |
resp->_flags = 0;
|
|
Packit |
6c4009 |
resp->ipv6_unavail = false;
|
|
Packit |
6c4009 |
resp->__glibc_unused_qhook = NULL;
|
|
Packit |
6c4009 |
resp->__glibc_unused_rhook = NULL;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
resp->retrans = conf->retrans;
|
|
Packit |
6c4009 |
resp->retry = conf->retry;
|
|
Packit |
6c4009 |
resp->options = conf->options;
|
|
Packit |
6c4009 |
resp->ndots = conf->ndots;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Copy the name server addresses. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
resp->nscount = 0;
|
|
Packit |
6c4009 |
resp->_u._ext.nscount = 0;
|
|
Packit |
6c4009 |
size_t nserv = conf->nameserver_list_size;
|
|
Packit |
6c4009 |
if (nserv > MAXNS)
|
|
Packit |
6c4009 |
nserv = MAXNS;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < nserv; i++)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (conf->nameserver_list[i]->sa_family == AF_INET)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
resp->nsaddr_list[i]
|
|
Packit |
6c4009 |
= *(struct sockaddr_in *)conf->nameserver_list[i];
|
|
Packit |
6c4009 |
resp->_u._ext.nsaddrs[i] = NULL;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
assert (conf->nameserver_list[i]->sa_family == AF_INET6);
|
|
Packit |
6c4009 |
resp->nsaddr_list[i].sin_family = 0;
|
|
Packit |
6c4009 |
/* Make a defensive copy of the name server address, in
|
|
Packit |
6c4009 |
case the application overwrites it. */
|
|
Packit |
6c4009 |
struct sockaddr_in6 *sa = malloc (sizeof (*sa));
|
|
Packit |
6c4009 |
if (sa == NULL)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
for (size_t j = 0; j < i; ++j)
|
|
Packit |
6c4009 |
free (resp->_u._ext.nsaddrs[j]);
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
*sa = *(struct sockaddr_in6 *)conf->nameserver_list[i];
|
|
Packit |
6c4009 |
resp->_u._ext.nsaddrs[i] = sa;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
resp->_u._ext.nssocks[i] = -1;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
resp->nscount = nserv;
|
|
Packit |
6c4009 |
/* Leave resp->_u._ext.nscount at 0. res_send.c handles this. */
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Fill in the prefix of the search list. It is truncated either at
|
|
Packit |
6c4009 |
MAXDNSRCH, or if reps->defdname has insufficient space. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
struct alloc_buffer buffer
|
|
Packit |
6c4009 |
= alloc_buffer_create (resp->defdname, sizeof (resp->defdname));
|
|
Packit |
6c4009 |
size_t size = conf->search_list_size;
|
|
Packit |
6c4009 |
size_t i;
|
|
Packit |
6c4009 |
for (i = 0; i < size && i < MAXDNSRCH; ++i)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
resp->dnsrch[i] = alloc_buffer_copy_string
|
|
Packit |
6c4009 |
(&buffer, conf->search_list[i]);
|
|
Packit |
6c4009 |
if (resp->dnsrch[i] == NULL)
|
|
Packit |
6c4009 |
/* No more space in resp->defdname. Truncate. */
|
|
Packit |
6c4009 |
break;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
resp->dnsrch[i] = NULL;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Copy the sort list. */
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
size_t nsort = conf->sort_list_size;
|
|
Packit |
6c4009 |
if (nsort > MAXRESOLVSORT)
|
|
Packit |
6c4009 |
nsort = MAXRESOLVSORT;
|
|
Packit |
6c4009 |
for (size_t i = 0; i < nsort; ++i)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
resp->sort_list[i].addr = conf->sort_list[i].addr;
|
|
Packit |
6c4009 |
resp->sort_list[i].mask = conf->sort_list[i].mask;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
resp->nsort = nsort;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* The overlapping parts of both configurations should agree after
|
|
Packit |
6c4009 |
initialization. */
|
|
Packit |
6c4009 |
assert (resolv_conf_matches (resp, conf));
|
|
Packit |
6c4009 |
return true;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Decrement the configuration object at INDEX and free it if the
|
|
Packit |
6c4009 |
reference counter reaches 0. *GLOBAL_COPY must be locked and
|
|
Packit |
6c4009 |
remains so. */
|
|
Packit |
6c4009 |
static void
|
|
Packit |
6c4009 |
decrement_at_index (struct resolv_conf_global *global_copy, size_t index)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (index < resolv_conf_array_size (&global_copy->array))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Index found. */
|
|
Packit |
6c4009 |
uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
|
|
Packit |
6c4009 |
/* Check that the slot is not already part of the free list. */
|
|
Packit |
6c4009 |
if (!(*slot & 1))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
struct resolv_conf *conf = (struct resolv_conf *) *slot;
|
|
Packit |
6c4009 |
conf_decrement (conf);
|
|
Packit |
6c4009 |
/* Put the slot onto the free list. */
|
|
Packit |
6c4009 |
*slot = global_copy->free_list_start;
|
|
Packit |
6c4009 |
global_copy->free_list_start = (index << 1) | 1;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
bool
|
|
Packit |
6c4009 |
__resolv_conf_attach (struct __res_state *resp, struct resolv_conf *conf)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
assert (conf->__refcount > 0);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
struct resolv_conf_global *global_copy = get_locked_global ();
|
|
Packit |
6c4009 |
if (global_copy == NULL)
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Try to find an unused index in the array. */
|
|
Packit |
6c4009 |
size_t index;
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (global_copy->free_list_start & 1)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Unlink from the free list. */
|
|
Packit |
6c4009 |
index = global_copy->free_list_start >> 1;
|
|
Packit |
6c4009 |
uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
|
|
Packit |
6c4009 |
global_copy->free_list_start = *slot;
|
|
Packit |
6c4009 |
assert (global_copy->free_list_start == 0
|
|
Packit |
6c4009 |
|| global_copy->free_list_start & 1);
|
|
Packit |
6c4009 |
/* Install the configuration pointer. */
|
|
Packit |
6c4009 |
*slot = (uintptr_t) conf;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
else
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
size_t size = resolv_conf_array_size (&global_copy->array);
|
|
Packit |
6c4009 |
/* No usable index found. Increase the array size. */
|
|
Packit |
6c4009 |
resolv_conf_array_add (&global_copy->array, (uintptr_t) conf);
|
|
Packit |
6c4009 |
if (resolv_conf_array_has_failed (&global_copy->array))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
put_locked_global (global_copy);
|
|
Packit |
6c4009 |
__set_errno (ENOMEM);
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
/* The new array element was added at the end. */
|
|
Packit |
6c4009 |
index = size;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* We have added a new reference to the object. */
|
|
Packit |
6c4009 |
++conf->__refcount;
|
|
Packit |
6c4009 |
assert (conf->__refcount > 0);
|
|
Packit |
6c4009 |
put_locked_global (global_copy);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if (!update_from_conf (resp, conf))
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* Drop the reference we acquired. Reacquire the lock. The
|
|
Packit |
6c4009 |
object has already been allocated, so it cannot be NULL this
|
|
Packit |
6c4009 |
time. */
|
|
Packit |
6c4009 |
global_copy = get_locked_global ();
|
|
Packit |
6c4009 |
decrement_at_index (global_copy, index);
|
|
Packit |
6c4009 |
put_locked_global (global_copy);
|
|
Packit |
6c4009 |
return false;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
resp->_u._ext.__glibc_extension_index = index ^ INDEX_MAGIC;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
return true;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
void
|
|
Packit |
6c4009 |
__resolv_conf_detach (struct __res_state *resp)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
if (atomic_load_relaxed (&global) == NULL)
|
|
Packit |
6c4009 |
/* Detach operation after a shutdown, or without any prior
|
|
Packit |
6c4009 |
attachment. We cannot free the data (and there might not be
|
|
Packit |
6c4009 |
anything to free anyway). */
|
|
Packit |
6c4009 |
return;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
struct resolv_conf_global *global_copy = get_locked_global ();
|
|
Packit |
6c4009 |
size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
|
|
Packit |
6c4009 |
decrement_at_index (global_copy, index);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Clear the index field, so that accidental reuse is less
|
|
Packit |
6c4009 |
likely. */
|
|
Packit |
6c4009 |
resp->_u._ext.__glibc_extension_index = 0;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
put_locked_global (global_copy);
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Deallocate the global data. */
|
|
Packit |
6c4009 |
libc_freeres_fn (freeres)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
/* No locking because this function is supposed to be called when
|
|
Packit |
6c4009 |
the process has turned single-threaded. */
|
|
Packit |
6c4009 |
if (global == NULL)
|
|
Packit |
6c4009 |
return;
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
if (global->conf_current != NULL)
|
|
Packit |
6c4009 |
{
|
|
Packit |
6c4009 |
conf_decrement (global->conf_current);
|
|
Packit |
6c4009 |
global->conf_current = NULL;
|
|
Packit |
6c4009 |
}
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Note that this frees only the array itself. The pointed-to
|
|
Packit |
6c4009 |
configuration objects should have been deallocated by res_nclose
|
|
Packit |
6c4009 |
and per-thread cleanup functions. */
|
|
Packit |
6c4009 |
resolv_conf_array_free (&global->array);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
free (global);
|
|
Packit |
6c4009 |
|
|
Packit |
6c4009 |
/* Stop potential future __resolv_conf_detach calls from accessing
|
|
Packit |
6c4009 |
deallocated memory. */
|
|
Packit |
6c4009 |
global = NULL;
|
|
Packit |
6c4009 |
}
|