diff options
Diffstat (limited to 'resolv/res_hconf.c')
-rw-r--r-- | resolv/res_hconf.c | 156 |
1 files changed, 89 insertions, 67 deletions
diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c index 56b9535d5e..59da9d6753 100644 --- a/resolv/res_hconf.c +++ b/resolv/res_hconf.c @@ -17,9 +17,9 @@ Boston, MA 02111-1307, USA. */ /* This file provides a Linux /etc/host.conf compatible front end to -the various name resolvers (/etc/hosts, named, NIS server, etc.). -Though mostly compatibly, the following differences exist compared -to the original implementation: + the various name resolvers (/etc/hosts, named, NIS server, etc.). + Though mostly compatibly, the following differences exist compared + to the original implementation: - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK environment variable (i.e., `off', `nowarn', or `warn'). @@ -27,13 +27,19 @@ to the original implementation: - line comments can appear anywhere (not just at the beginning of a line) */ + +#include <errno.h> #include <ctype.h> #include <memory.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <net/if.h> - +#include <sys/ioctl.h> +#include <unistd.h> +#include <netinet/in.h> +#include <bits/libc-lock.h> +#include "ifreq.h" #include "res_hconf.h" #define _PATH_HOSTCONF "/etc/host.conf" @@ -374,113 +380,118 @@ _res_hconf_init (void) } +/* List of known interfaces. */ +static struct netaddr +{ + int addrtype; + union + { + struct + { + u_int32_t addr; + u_int32_t mask; + } ipv4; + } u; +} *ifaddrs; + +/* We need to protect the dynamic buffer handling. */ +__libc_lock_define_initialized (static, lock); + /* Reorder addresses returned in a hostent such that the first address is an address on the local subnet, if there is such an address. - Otherwise, nothing is changed. */ + Otherwise, nothing is changed. + + Note that this function currently only handles IPv4 addresses. */ void _res_hconf_reorder_addrs (struct hostent *hp) { #if defined SIOCGIFCONF && defined SIOCGIFNETMASK - static int num_ifs = -1; /* number of interfaces */ - static struct netaddr - { - int addrtype; - union - { - struct - { - u_int32_t addr; - u_int32_t mask; - } ipv4 - } u; - } *ifaddrs; + int i, j; + /* Number of interfaces. */ + static int num_ifs = -1; + /* Only reorder if we're supposed to. */ + if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0) + return; + + /* Can't deal with anything but IPv4 for now... */ if (hp->h_addrtype != AF_INET) - return; /* can't deal with anything but IPv4 for now... */ + return; if (num_ifs <= 0) { - struct ifconf ifs; - struct ifreq *ifr; - size_t size, num; - int sd; - - /* initialize interface table: */ + struct ifreq *ifr, *cur_ifr; + int sd, num, i; + /* Save errno. */ + int save = errno; + + /* Initialize interface table. */ num_ifs = 0; - sd = __socket (AF_INET, SOCK_DGRAM, 0); + sd = __opensock (); if (sd < 0) return; - /* Now get list of interfaces. Since we don't know how many - interfaces there are, we keep increasing the buffer size - until we have at least sizeof(struct ifreq) too many bytes. - That implies that the ioctl() return because it ran out of - interfaces, not memory */ - size = 0; - ifs.ifc_buf = 0; - do - { - size += 4 * sizeof (struct ifreq); - ifs.ifc_buf = realloc (ifs.ifs_buf, size); - if (ifs.ifc_buf == NULL) - { - close (sd); - return; - } - ifs.ifc_len = size; - if (__ioctl (sd, SIOCGIFCONF, &ifs) < 0) - goto cleanup; - } - while (size - ifs.ifc_len < sizeof (struct ifreq)); + /* Get lock. */ + __libc_lock_lock (lock); - num = ifs.ifc_len / sizeof (struct ifreq); + /* Get a list of interfaces. */ + __ifreq (&ifr, &num); + if (!ifr) + goto cleanup; ifaddrs = malloc (num * sizeof (ifaddrs[0])); if (!ifaddrs) - goto cleanup; - - ifr = ifs.ifc_req; - for (i = 0; i < num; ++i) + goto cleanup1; + + /* Copy usable interfaces in ifaddrs structure. */ + for (cur_ifr = ifr, i = 0; i < num; ++cur_ifr, ++i) { - if (ifr->ifr_addr.sa_family != AF_INET) + if (cur_ifr->ifr_addr.sa_family != AF_INET) continue; + ifaddrs[num_ifs].addrtype = AF_INET; + ifaddrs[num_ifs].u.ipv4.addr = + ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr; - memcpy (&ifaddrs[num_ifs].u.ipv4.addr, - &((struct sockaddr_in *)ifr->ifr_addr)->sin_addr, 4); - - if (__ioctl (sd, SIOCGIFNETMASK, if) < 0) + if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0) continue; - memcpy (&ifaddrs[num_ifs].u.ipv4.mask, - ((struct sockaddr_in *)ifr->ifr_mask)->sin_addr, 4); - ++num_ifs; /* now we're committed to this entry */ + ifaddrs[num_ifs].u.ipv4.mask = + ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr; + + /* Now we're committed to this entry. */ + ++num_ifs; } - /* just keep enough memory to hold all the interfaces we want: */ + /* Just keep enough memory to hold all the interfaces we want. */ ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0])); + cleanup1: + __if_freereq (ifr); + cleanup: + /* Release lock, preserve error value, and close socket. */ + save = errno; + __libc_lock_unlock (lock); close (sd); - free (ifs.ifc_buf); } if (num_ifs == 0) return; - /* find an address for which we have a direct connection: */ + /* Find an address for which we have a direct connection. */ for (i = 0; hp->h_addr_list[i]; ++i) { - h_addr = (struct in_addr *) hp->h_addr_list[i]; + struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i]; for (j = 0; j < num_ifs; ++j) { - if_addr = ifaddrs[j].u.ipv4.addr; - if_netmask = ifaddrs[j].u.ipv4.mask; + u_int32_t if_addr = ifaddrs[j].u.ipv4.addr; + u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask; - if (((h_addr->s_addr ^ if_addr) & if_netmask) == 0) + if (((haddr->s_addr ^ if_addr) & if_netmask) == 0) { void *tmp; @@ -537,3 +548,14 @@ _res_hconf_trim_domains (struct hostent *hp) for (i = 0; hp->h_aliases[i]; ++i) _res_hconf_trim_domain (hp->h_aliases[i]); } + + +/* Free all resources if necessary. */ +static void __attribute__ ((unused)) +free_mem (void) +{ + if (ifaddrs != NULL) + free (ifaddrs); +} + +text_set_element (__libc_subfreeres, free_mem); |