diff options
Diffstat (limited to 'resolv/resolv_conf.c')
-rw-r--r-- | resolv/resolv_conf.c | 223 |
1 files changed, 216 insertions, 7 deletions
diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c index 76d55fc..dd66523 100644 --- a/resolv/resolv_conf.c +++ b/resolv/resolv_conf.c @@ -128,11 +128,80 @@ resolv_conf_get_1 (const struct __res_state *resp) return conf; } +/* Return true if both IPv4 addresses are equal. */ +static bool +same_address_v4 (const struct sockaddr_in *left, + const struct sockaddr_in *right) +{ + return left->sin_addr.s_addr == right->sin_addr.s_addr + && left->sin_port == right->sin_port; +} + +/* Return true if both IPv6 addresses are equal. This ignores the + flow label. */ +static bool +same_address_v6 (const struct sockaddr_in6 *left, + const struct sockaddr_in6 *right) +{ + return memcmp (&left->sin6_addr, &right->sin6_addr, + sizeof (left->sin6_addr)) == 0 + && left->sin6_port == right->sin6_port + && left->sin6_scope_id == right->sin6_scope_id; +} + +static bool +same_address (const struct sockaddr *left, const struct sockaddr *right) +{ + if (left->sa_family != right->sa_family) + return false; + switch (left->sa_family) + { + case AF_INET: + return same_address_v4 ((const struct sockaddr_in *) left, + (const struct sockaddr_in *) right); + case AF_INET6: + return same_address_v6 ((const struct sockaddr_in6 *) left, + (const struct sockaddr_in6 *) right); + } + return false; +} + /* Check that *RESP and CONF match. Used by __resolv_conf_get. */ static bool resolv_conf_matches (const struct __res_state *resp, const struct resolv_conf *conf) { + /* NB: Do not compare the options, retrans, retry, ndots. These can + be changed by applicaiton. */ + + /* Check that the name servers in *RESP have not been modified by + the application. */ + { + size_t nserv = conf->nameserver_list_size; + if (nserv > MAXNS) + nserv = MAXNS; + /* _ext.nscount is 0 until initialized by res_send.c. */ + if (resp->nscount != nserv + && (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv)) + return false; + for (size_t i = 0; i < nserv; ++i) + { + if (resp->nsaddr_list[i].sin_family == 0) + { + if (resp->_u._ext.nsaddrs[i]->sin6_family != AF_INET6) + return false; + if (!same_address ((struct sockaddr *) resp->_u._ext.nsaddrs[i], + conf->nameserver_list[i])) + return false; + } + else if (resp->nsaddr_list[i].sin_family != AF_INET) + return false; + else if (!same_address ((struct sockaddr *) &resp->nsaddr_list[i], + conf->nameserver_list[i])) + return false; + } + } + /* Check that the search list in *RESP has not been modified by the application. */ { @@ -161,6 +230,18 @@ resolv_conf_matches (const struct __res_state *resp, } } } + + /* Check that the sort list has not been modified. */ + { + size_t nsort = conf->sort_list_size; + if (nsort > MAXRESOLVSORT) + nsort = MAXRESOLVSORT; + for (size_t i = 0; i < nsort; ++i) + if (resp->sort_list[i].addr.s_addr != conf->sort_list[i].addr.s_addr + || resp->sort_list[i].mask != conf->sort_list[i].mask) + return false; + } + return true; } @@ -190,7 +271,28 @@ __resolv_conf_put (struct resolv_conf *conf) struct resolv_conf * __resolv_conf_allocate (const struct resolv_conf *init) { - /* Space needed by the strings. */ + /* Allocate in decreasing order of alignment. */ + _Static_assert (__alignof__ (const char *const *) + <= __alignof__ (struct resolv_conf), "alignment"); + _Static_assert (__alignof__ (struct sockaddr_in6) + <= __alignof__ (const char *const *), "alignment"); + _Static_assert (__alignof__ (struct sockaddr_in) + == __alignof__ (struct sockaddr_in6), "alignment"); + _Static_assert (__alignof__ (struct resolv_sortlist_entry) + <= __alignof__ (struct sockaddr_in), "alignment"); + + /* Space needed by the nameserver addresses. */ + size_t address_space = 0; + for (size_t i = 0; i < init->nameserver_list_size; ++i) + if (init->nameserver_list[i]->sa_family == AF_INET) + address_space += sizeof (struct sockaddr_in); + else + { + assert (init->nameserver_list[i]->sa_family == AF_INET6); + address_space += sizeof (struct sockaddr_in6); + } + + /* Space needed by the search list strings. */ size_t string_space = 0; for (size_t i = 0; i < init->search_list_size; ++i) string_space += strlen (init->search_list[i]) + 1; @@ -199,7 +301,10 @@ __resolv_conf_allocate (const struct resolv_conf *init) void *ptr; struct alloc_buffer buffer = alloc_buffer_allocate (sizeof (struct resolv_conf) + + init->nameserver_list_size * sizeof (init->nameserver_list[0]) + + address_space + init->search_list_size * sizeof (init->search_list[0]) + + init->sort_list_size * sizeof (init->sort_list[0]) + string_space, &ptr); struct resolv_conf *conf @@ -211,16 +316,57 @@ __resolv_conf_allocate (const struct resolv_conf *init) /* Initialize the contents. */ conf->__refcount = 1; + conf->retrans = init->retrans; + conf->retry = init->retry; + conf->options = init->options; + conf->ndots = init->ndots; conf->initstamp = __res_initstamp; - /* Allocate and fill the search list array. */ + /* Allocate the arrays with pointers. These must come first because + they have the highets alignment. */ + conf->nameserver_list_size = init->nameserver_list_size; + const struct sockaddr **nameserver_array = alloc_buffer_alloc_array + (&buffer, const struct sockaddr *, init->nameserver_list_size); + conf->nameserver_list = nameserver_array; + + conf->search_list_size = init->search_list_size; + const char **search_array = alloc_buffer_alloc_array + (&buffer, const char *, init->search_list_size); + conf->search_list = search_array; + + /* Fill the name server list array. */ + for (size_t i = 0; i < init->nameserver_list_size; ++i) + if (init->nameserver_list[i]->sa_family == AF_INET) + { + struct sockaddr_in *sa = alloc_buffer_alloc + (&buffer, struct sockaddr_in); + *sa = *(struct sockaddr_in *) init->nameserver_list[i]; + nameserver_array[i] = (struct sockaddr *) sa; + } + else + { + struct sockaddr_in6 *sa = alloc_buffer_alloc + (&buffer, struct sockaddr_in6); + *sa = *(struct sockaddr_in6 *) init->nameserver_list[i]; + nameserver_array[i] = (struct sockaddr *) sa; + } + + /* Allocate and fill the sort list array. */ + { + conf->sort_list_size = init->sort_list_size; + struct resolv_sortlist_entry *array = alloc_buffer_alloc_array + (&buffer, struct resolv_sortlist_entry, init->sort_list_size); + conf->sort_list = array; + for (size_t i = 0; i < init->sort_list_size; ++i) + array[i] = init->sort_list[i]; + } + + /* Fill the search list array. This must come last because the + strings are the least aligned part of the allocation. */ { - conf->search_list_size = init->search_list_size; - const char **array = alloc_buffer_alloc_array - (&buffer, const char *, init->search_list_size); - conf->search_list = array; for (size_t i = 0; i < init->search_list_size; ++i) - array[i] = alloc_buffer_copy_string (&buffer, init->search_list[i]); + search_array[i] = alloc_buffer_copy_string + (&buffer, init->search_list[i]); } assert (!alloc_buffer_has_failed (&buffer)); @@ -231,6 +377,56 @@ __resolv_conf_allocate (const struct resolv_conf *init) static __attribute__ ((nonnull (1, 2), warn_unused_result)) bool update_from_conf (struct __res_state *resp, const struct resolv_conf *conf) { + resp->defdname[0] = '\0'; + resp->pfcode = 0; + resp->_vcsock = -1; + resp->_flags = 0; + resp->ipv6_unavail = false; + resp->__glibc_unused_qhook = NULL; + resp->__glibc_unused_rhook = NULL; + + resp->retrans = conf->retrans; + resp->retry = conf->retry; + resp->options = conf->options; + resp->ndots = conf->ndots; + + /* Copy the name server addresses. */ + { + resp->nscount = 0; + resp->_u._ext.nscount = 0; + size_t nserv = conf->nameserver_list_size; + if (nserv > MAXNS) + nserv = MAXNS; + for (size_t i = 0; i < nserv; i++) + { + if (conf->nameserver_list[i]->sa_family == AF_INET) + { + resp->nsaddr_list[i] + = *(struct sockaddr_in *)conf->nameserver_list[i]; + resp->_u._ext.nsaddrs[i] = NULL; + } + else + { + assert (conf->nameserver_list[i]->sa_family == AF_INET6); + resp->nsaddr_list[i].sin_family = 0; + /* Make a defensive copy of the name server address, in + case the application overwrites it. */ + struct sockaddr_in6 *sa = malloc (sizeof (*sa)); + if (sa == NULL) + { + for (size_t j = 0; j < i; ++j) + free (resp->_u._ext.nsaddrs[j]); + return false; + } + *sa = *(struct sockaddr_in6 *)conf->nameserver_list[i]; + resp->_u._ext.nsaddrs[i] = sa; + } + resp->_u._ext.nssocks[i] = -1; + } + resp->nscount = nserv; + /* Leave resp->_u._ext.nscount at 0. res_send.c handles this. */ + } + /* Fill in the prefix of the search list. It is truncated either at MAXDNSRCH, or if reps->defdname has insufficient space. */ { @@ -249,6 +445,19 @@ update_from_conf (struct __res_state *resp, const struct resolv_conf *conf) resp->dnsrch[i] = NULL; } + /* Copy the sort list. */ + { + size_t nsort = conf->sort_list_size; + if (nsort > MAXRESOLVSORT) + nsort = MAXRESOLVSORT; + for (size_t i = 0; i < nsort; ++i) + { + resp->sort_list[i].addr = conf->sort_list[i].addr; + resp->sort_list[i].mask = conf->sort_list[i].mask; + } + resp->nsort = nsort; + } + /* The overlapping parts of both configurations should agree after initialization. */ assert (resolv_conf_matches (resp, conf)); |