From 0872db85cda0f4d3d1772f365732731f2c0a3c3c Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Thu, 19 Feb 2015 15:52:08 +0100 Subject: [PATCH] Rewrite handling of nameserver configuration in resolver [BZ #13028] [BZ #17053] * resolv/res_private.h: New file. * resolv/resolv.h (struct __res_state): Add pointer to struct __res_state_ext member, rename ext.nsmap, ext.nscount6, ext.nsinit, ext.nsaddrs members to __glibc_reserved*. * resolv/res_init.c (__res_vinit): Rewrite handling of nameserver addresses. (__res_iclose): Deallocate __res_state_ext memory if free_addr. * resolv/res_send.c (__libc_res_nsend): Rewrite nameserver setup. (get_nsaddr): New function. (res_ourserver_p, send_vc, reopen): Use it instead of accessing statp directly. --- resolv/res_init.c | 125 +++++++++++++++--------------------- resolv/res_private.h | 29 +++++++++ resolv/res_send.c | 176 ++++++++++++++++++++++----------------------------- resolv/resolv.h | 9 +-- 4 files changed, 160 insertions(+), 179 deletions(-) create mode 100644 resolv/res_private.h diff --git a/resolv/res_init.c b/resolv/res_init.c index 553ba12..567afaa 100644 --- a/resolv/res_init.c +++ b/resolv/res_init.c @@ -89,6 +89,8 @@ static const char rcsid[] = "$BINDId: res_init.c,v 8.16 2000/05/09 07:10:12 vixi #include +#include "res_private.h" + /* Options. Should all be left alone. */ #define RESOLVSORT #define RFC1535 @@ -153,10 +155,8 @@ __res_vinit(res_state statp, int preinit) { char *cp, **pp; int n; char buf[BUFSIZ]; - int nserv = 0; /* number of IPv4 nameservers read from file */ -#ifdef _LIBC - int nservall = 0; /* number of (IPv4 + IPV6) nameservers read from file */ -#endif + int nserv = 0; /* number of nameservers read from file */ + int have_serv6 = 0; int haveenv = 0; int havesearch = 0; #ifdef RESOLVSORT @@ -184,15 +184,12 @@ __res_vinit(res_state statp, int preinit) { statp->_flags = 0; statp->qhook = NULL; statp->rhook = NULL; - statp->_u._ext.nsinit = 0; statp->_u._ext.nscount = 0; -#ifdef _LIBC - statp->_u._ext.nscount6 = 0; - for (n = 0; n < MAXNS; n++) { - statp->_u._ext.nsaddrs[n] = NULL; - statp->_u._ext.nsmap[n] = MAXNS; + if (statp->_u._ext.ext == NULL) { + statp->_u._ext.ext = malloc (sizeof (*statp->_u._ext.ext)); + if (statp->_u._ext.ext != NULL) + memset (statp->_u._ext.ext, 0, sizeof (*statp->_u._ext.ext)); } -#endif /* Allow user to override the local domain definition */ if ((cp = getenv("LOCALDOMAIN")) != NULL) { @@ -296,11 +293,7 @@ __res_vinit(res_state statp, int preinit) { continue; } /* read nameservers to query */ -#ifdef _LIBC - if (MATCH(buf, "nameserver") && nservall < MAXNS) { -#else if (MATCH(buf, "nameserver") && nserv < MAXNS) { -#endif struct in_addr a; cp = buf + sizeof("nameserver") - 1; @@ -308,13 +301,11 @@ __res_vinit(res_state statp, int preinit) { cp++; if ((*cp != '\0') && (*cp != '\n') && __inet_aton(cp, &a)) { - statp->nsaddr_list[nservall].sin_addr = a; - statp->nsaddr_list[nservall].sin_family = AF_INET; - statp->nsaddr_list[nservall].sin_port = + statp->nsaddr_list[nserv].sin_addr = a; + statp->nsaddr_list[nserv].sin_family = AF_INET; + statp->nsaddr_list[nserv].sin_port = htons(NAMESERVER_PORT); nserv++; -#ifdef _LIBC - nservall++; } else { struct in6_addr a6; char *el; @@ -324,45 +315,40 @@ __res_vinit(res_state statp, int preinit) { if ((el = strchr(cp, SCOPE_DELIMITER)) != NULL) *el = '\0'; if ((*cp != '\0') && - (__inet_pton(AF_INET6, cp, &a6) > 0)) { - struct sockaddr_in6 *sa6; - - sa6 = malloc(sizeof(*sa6)); - if (sa6 != NULL) { - sa6->sin6_family = AF_INET6; - sa6->sin6_port = htons(NAMESERVER_PORT); - sa6->sin6_flowinfo = 0; - sa6->sin6_addr = a6; - - if (__glibc_likely (el == NULL)) - sa6->sin6_scope_id = 0; - else { - int try_numericscope = 1; - if (IN6_IS_ADDR_LINKLOCAL (&a6) - || IN6_IS_ADDR_MC_LINKLOCAL (&a6)) { - sa6->sin6_scope_id - = __if_nametoindex (el + 1); - if (sa6->sin6_scope_id != 0) - try_numericscope = 0; - } - - if (try_numericscope) { - char *end; - sa6->sin6_scope_id - = (uint32_t) strtoul (el + 1, &end, - 10); - if (*end != '\0') - sa6->sin6_scope_id = 0; - } + (__inet_pton(AF_INET6, cp, &a6) > 0) + && statp->_u._ext.ext != NULL) { + struct sockaddr_in6 *sa6 + = &statp->_u._ext.ext->nsaddrs[nserv].sin6; + + sa6->sin6_family = AF_INET6; + sa6->sin6_port = htons(NAMESERVER_PORT); + sa6->sin6_flowinfo = 0; + sa6->sin6_addr = a6; + + if (__glibc_likely (el == NULL)) + sa6->sin6_scope_id = 0; + else { + int try_numericscope = 1; + if (IN6_IS_ADDR_LINKLOCAL (&a6) + || IN6_IS_ADDR_MC_LINKLOCAL (&a6)) { + sa6->sin6_scope_id + = __if_nametoindex (el + 1); + if (sa6->sin6_scope_id != 0) + try_numericscope = 0; } - statp->_u._ext.nsaddrs[nservall] = sa6; - statp->_u._ext.nssocks[nservall] = -1; - statp->_u._ext.nsmap[nservall] = MAXNS + 1; - nservall++; + if (try_numericscope) { + char *end; + sa6->sin6_scope_id + = (uint32_t) strtoul (el + 1, &end, 10); + if (*end != '\0') + sa6->sin6_scope_id = 0; + } } + statp->nsaddr_list[nserv].sin_family = 0; + have_serv6 = 1; + nserv++; } -#endif } continue; } @@ -414,10 +400,9 @@ __res_vinit(res_state statp, int preinit) { continue; } } - statp->nscount = nservall; + statp->nscount = nserv; #ifdef _LIBC - if (nservall - nserv > 0) { - statp->_u._ext.nscount6 = nservall - nserv; + if (have_serv6) { /* We try IPv6 servers again. */ statp->ipv6_unavail = false; } @@ -606,23 +591,15 @@ __res_iclose(res_state statp, bool free_addr) { statp->_vcsock = -1; statp->_flags &= ~(RES_F_VC | RES_F_CONN); } -#ifdef _LIBC - for (ns = 0; ns < MAXNS; ns++) -#else - for (ns = 0; ns < statp->_u._ext.nscount; ns++) -#endif - if (statp->_u._ext.nsaddrs[ns]) { - if (statp->_u._ext.nssocks[ns] != -1) { - close_not_cancel_no_status(statp->_u._ext.nssocks[ns]); - statp->_u._ext.nssocks[ns] = -1; - } - if (free_addr) { - free (statp->_u._ext.nsaddrs[ns]); - statp->_u._ext.nsaddrs[ns] = NULL; - } + for (ns = 0; ns < statp->nscount; ns++) + if (statp->_u._ext.nssocks[ns] != -1) { + close_not_cancel_no_status(statp->_u._ext.nssocks[ns]); + statp->_u._ext.nssocks[ns] = -1; } - if (free_addr) - statp->_u._ext.nsinit = 0; + if (free_addr) { + free (statp->_u._ext.ext); + statp->_u._ext.ext = NULL; + } } libc_hidden_def (__res_iclose) diff --git a/resolv/res_private.h b/resolv/res_private.h new file mode 100644 index 0000000..deb495b --- /dev/null +++ b/resolv/res_private.h @@ -0,0 +1,29 @@ +/* Private definitions for the stub resolver. + Copyright (C) 2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +union res_sockaddr_union +{ + struct sockaddr s; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; + +struct __res_state_ext +{ + union res_sockaddr_union nsaddrs[MAXNS]; +}; diff --git a/resolv/res_send.c b/resolv/res_send.c index c35fb66..1e6d217 100644 --- a/resolv/res_send.c +++ b/resolv/res_send.c @@ -179,11 +179,13 @@ evNowTime(struct timespec *res) { /* Options. Leave them on. */ /* #undef DEBUG */ #include "res_debug.h" +#include "res_private.h" #define EXT(res) ((res)->_u._ext) /* Forward. */ +static struct sockaddr *get_nsaddr (res_state, int); static int send_vc(res_state, const u_char *, int, const u_char *, int, u_char **, int *, int *, int, u_char **, @@ -221,20 +223,21 @@ res_ourserver_p(const res_state statp, const struct sockaddr_in6 *inp) in_port_t port = in4p->sin_port; in_addr_t addr = in4p->sin_addr.s_addr; - for (ns = 0; ns < MAXNS; ns++) { + for (ns = 0; ns < statp->nscount; ns++) { const struct sockaddr_in *srv = - (struct sockaddr_in *)EXT(statp).nsaddrs[ns]; + (struct sockaddr_in *) get_nsaddr (statp, ns); - if ((srv != NULL) && (srv->sin_family == AF_INET) && + if ((srv->sin_family == AF_INET) && (srv->sin_port == port) && (srv->sin_addr.s_addr == INADDR_ANY || srv->sin_addr.s_addr == addr)) return (1); } } else if (inp->sin6_family == AF_INET6) { - for (ns = 0; ns < MAXNS; ns++) { - const struct sockaddr_in6 *srv = EXT(statp).nsaddrs[ns]; - if ((srv != NULL) && (srv->sin6_family == AF_INET6) && + for (ns = 0; ns < statp->nscount; ns++) { + const struct sockaddr_in6 *srv + = (struct sockaddr_in6 *) get_nsaddr (statp, ns); + if ((srv->sin6_family == AF_INET6) && (srv->sin6_port == inp->sin6_port) && !(memcmp(&srv->sin6_addr, &in6addr_any, sizeof (struct in6_addr)) && @@ -384,80 +387,40 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, * If the ns_addr_list in the resolver context has changed, then * invalidate our cached copy and the associated timing data. */ - if (EXT(statp).nsinit) { + if (EXT(statp).nscount != 0) { int needclose = 0; if (EXT(statp).nscount != statp->nscount) needclose++; else - for (ns = 0; ns < MAXNS; ns++) { - unsigned int map = EXT(statp).nsmap[ns]; - if (map < MAXNS + for (ns = 0; ns < statp->nscount; ns++) { + if (statp->nsaddr_list[ns].sin_family != 0 && !sock_eq((struct sockaddr_in6 *) - &statp->nsaddr_list[map], - EXT(statp).nsaddrs[ns])) + &statp->nsaddr_list[ns], + &EXT(statp).ext->nsaddrs[ns].sin6)) { needclose++; break; } } - if (needclose) + if (needclose) { __res_iclose(statp, false); + EXT(statp).nscount = 0; + } } /* * Maybe initialize our private copy of the ns_addr_list. */ - if (EXT(statp).nsinit == 0) { - unsigned char map[MAXNS]; - - memset (map, MAXNS, sizeof (map)); - for (n = 0; n < MAXNS; n++) { - ns = EXT(statp).nsmap[n]; - if (ns < statp->nscount) - map[ns] = n; - else if (ns < MAXNS) { - free(EXT(statp).nsaddrs[n]); - EXT(statp).nsaddrs[n] = NULL; - EXT(statp).nsmap[n] = MAXNS; - } + if (EXT(statp).nscount == 0) { + for (ns = 0; ns < statp->nscount; ns++) { + EXT(statp).nssocks[ns] = -1; + if (statp->nsaddr_list[ns].sin_family != 0 + && EXT(statp).ext != NULL) + EXT(statp).ext->nsaddrs[ns].sin + = statp->nsaddr_list[ns]; } - n = statp->nscount; - if (statp->nscount > EXT(statp).nscount) - for (n = EXT(statp).nscount, ns = 0; - n < statp->nscount; n++) { - while (ns < MAXNS - && EXT(statp).nsmap[ns] != MAXNS) - ns++; - if (ns == MAXNS) - break; - /* NS never exceeds MAXNS, but gcc 4.9 somehow - does not see this. */ - DIAG_PUSH_NEEDS_COMMENT; - DIAG_IGNORE_NEEDS_COMMENT (4.9, - "-Warray-bounds"); - EXT(statp).nsmap[ns] = n; - DIAG_POP_NEEDS_COMMENT; - map[n] = ns++; - } - EXT(statp).nscount = n; - for (ns = 0; ns < EXT(statp).nscount; ns++) { - n = map[ns]; - if (EXT(statp).nsaddrs[n] == NULL) - EXT(statp).nsaddrs[n] = - malloc(sizeof (struct sockaddr_in6)); - if (EXT(statp).nsaddrs[n] != NULL) { - memset (mempcpy(EXT(statp).nsaddrs[n], - &statp->nsaddr_list[ns], - sizeof (struct sockaddr_in)), - '\0', - sizeof (struct sockaddr_in6) - - sizeof (struct sockaddr_in)); - EXT(statp).nssocks[n] = -1; - n++; - } - } - EXT(statp).nsinit = 1; + EXT(statp).nscount = statp->nscount; } /* @@ -466,44 +429,41 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, */ if (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) && (statp->options & RES_BLAST) == 0) { - struct sockaddr_in6 *ina; - unsigned int map; - - n = 0; - while (n < MAXNS && EXT(statp).nsmap[n] == MAXNS) - n++; - if (n < MAXNS) { - ina = EXT(statp).nsaddrs[n]; - map = EXT(statp).nsmap[n]; - for (;;) { - ns = n + 1; - while (ns < MAXNS - && EXT(statp).nsmap[ns] == MAXNS) - ns++; - if (ns == MAXNS) - break; - EXT(statp).nsaddrs[n] = EXT(statp).nsaddrs[ns]; - EXT(statp).nsmap[n] = EXT(statp).nsmap[ns]; - n = ns; - } - EXT(statp).nsaddrs[n] = ina; - EXT(statp).nsmap[n] = map; + union res_sockaddr_union inu; + struct sockaddr_in ina; + int lastns = statp->nscount - 1; + int fd; + + if (EXT(statp).ext != NULL) + inu = EXT(statp).ext->nsaddrs[0]; + ina = statp->nsaddr_list[0]; + fd = EXT(statp).nssocks[0]; + for (ns = 0; ns < lastns; ns++) { + if (EXT(statp).ext != NULL) + EXT(statp).ext->nsaddrs[ns] + = EXT(statp).ext->nsaddrs[ns + 1]; + statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1]; + EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1]; } + if (EXT(statp).ext != NULL) + EXT(statp).ext->nsaddrs[lastns] = inu; + statp->nsaddr_list[lastns] = ina; + EXT(statp).nssocks[lastns] = fd; } /* * Send request, RETRY times, or until successful. */ for (try = 0; try < statp->retry; try++) { - for (ns = 0; ns < MAXNS; ns++) + for (ns = 0; ns < statp->nscount; ns++) { #ifdef DEBUG char tmpbuf[40]; #endif - struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; +#if defined USE_HOOKS || defined DEBUG + struct sockaddr *nsap = get_nsaddr (statp, ns); +#endif - if (nsap == NULL) - goto next_ns; same_ns: #ifdef USE_HOOKS if (__glibc_unlikely (statp->qhook != NULL)) { @@ -542,9 +502,9 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, Dprint(statp->options & RES_DEBUG, (stdout, ";; Querying server (# %d) address = %s\n", - ns + 1, inet_ntop(nsap->sin6_family, - (nsap->sin6_family == AF_INET6 - ? &nsap->sin6_addr + ns + 1, inet_ntop(nsap->sa_family, + (nsap->sa_family == AF_INET6 + ? &((struct sockaddr_in6 *) nsap)->sin6_addr : &((struct sockaddr_in *) nsap)->sin_addr), tmpbuf, sizeof (tmpbuf)))); @@ -660,6 +620,22 @@ libresolv_hidden_def (res_nsend) /* Private */ +static struct sockaddr * +get_nsaddr (res_state statp, int n) +{ + + if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) + /* - EXT(statp).ext->nsaddrs[n] holds an address that is larger + than struct sockaddr, and + - user code did not update statp->nsaddr_list[n]. */ + return &EXT(statp).ext->nsaddrs[n].s; + else + /* - user code updated statp->nsaddr_list[n], or + - statp->nsaddr_list[n] has the same content as + EXT(statp).ext->nsaddrs[n]. */ + return (struct sockaddr *) (void *) &statp->nsaddr_list[n]; +} + static int send_vc(res_state statp, const u_char *buf, int buflen, const u_char *buf2, int buflen2, @@ -674,7 +650,7 @@ send_vc(res_state statp, // XXX REMOVE // int anssiz = *anssizp; HEADER *anhp = (HEADER *) ans; - struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; + struct sockaddr *nsap = get_nsaddr (statp, ns); int truncating, connreset, n; /* On some architectures compiler might emit a warning indicating 'resplen' may be used uninitialized. However if buf2 == NULL @@ -711,8 +687,8 @@ send_vc(res_state statp, if (getpeername(statp->_vcsock, (struct sockaddr *)&peer, &size) < 0 || - !sock_eq(&peer, nsap)) { - __res_iclose(statp, false); + !sock_eq(&peer, (struct sockaddr_in6 *) nsap)) { + __res_iclose(statp, false); statp->_flags &= ~RES_F_VC; } } @@ -721,20 +697,19 @@ send_vc(res_state statp, if (statp->_vcsock >= 0) __res_iclose(statp, false); - statp->_vcsock = socket(nsap->sin6_family, SOCK_STREAM, 0); + statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0); if (statp->_vcsock < 0) { *terrno = errno; Perror(statp, stderr, "socket(vc)", errno); return (-1); } __set_errno (0); - if (connect(statp->_vcsock, (struct sockaddr *)nsap, - nsap->sin6_family == AF_INET + if (connect(statp->_vcsock, nsap, + nsap->sa_family == AF_INET ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)) < 0) { *terrno = errno; - Aerror(statp, stderr, "connect/vc", errno, - (struct sockaddr *) nsap); + Aerror(statp, stderr, "connect/vc", errno, nsap); __res_iclose(statp, false); return (0); } @@ -945,8 +920,7 @@ static int reopen (res_state statp, int *terrno, int ns) { if (EXT(statp).nssocks[ns] == -1) { - struct sockaddr *nsap - = (struct sockaddr *) EXT(statp).nsaddrs[ns]; + struct sockaddr *nsap = get_nsaddr (statp, ns); socklen_t slen; /* only try IPv6 if IPv6 NS and if not failed before */ diff --git a/resolv/resolv.h b/resolv/resolv.h index 53c3bba..dffd1db 100644 --- a/resolv/resolv.h +++ b/resolv/resolv.h @@ -100,6 +100,7 @@ typedef res_sendhookact (*res_send_rhook) (const struct sockaddr_in *__ns, # define RES_MAXRETRY 5 /* only for resolv.conf/RES_OPTIONS */ # define RES_DFLRETRY 2 /* Default #/tries. */ # define RES_MAXTIME 65535 /* Infinity, in milliseconds. */ +struct __res_state_ext; struct __res_state { int retrans; /* retransmition time interval */ @@ -133,11 +134,11 @@ struct __res_state { char pad[52]; /* On an i386 this means 512b total. */ struct { u_int16_t nscount; - u_int16_t nsmap[MAXNS]; + u_int16_t __glibc_reserved1[MAXNS]; int nssocks[MAXNS]; - u_int16_t nscount6; - u_int16_t nsinit; - struct sockaddr_in6 *nsaddrs[MAXNS]; + u_int16_t __glibc_reserved2[2]; + struct __res_state_ext *ext; /* extension for IPv6 */ + void *__glibc_reserved3[MAXNS-1]; #ifdef _LIBC unsigned long long int initstamp __attribute__((packed)); -- 2.3.0