diff options
Diffstat (limited to 'REORG.TODO/resolv/nss_dns')
-rw-r--r-- | REORG.TODO/resolv/nss_dns/dns-canon.c | 173 | ||||
-rw-r--r-- | REORG.TODO/resolv/nss_dns/dns-host.c | 1333 | ||||
-rw-r--r-- | REORG.TODO/resolv/nss_dns/dns-network.c | 472 |
3 files changed, 1978 insertions, 0 deletions
diff --git a/REORG.TODO/resolv/nss_dns/dns-canon.c b/REORG.TODO/resolv/nss_dns/dns-canon.c new file mode 100644 index 0000000000..4276eb6542 --- /dev/null +++ b/REORG.TODO/resolv/nss_dns/dns-canon.c @@ -0,0 +1,173 @@ +/* Copyright (C) 2004-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2004. + + 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 + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <netdb.h> +#include <resolv.h> +#include <stdlib.h> +#include <stdint.h> +#include <arpa/nameser.h> +#include <nsswitch.h> + + +#if PACKETSZ > 65536 +# define MAXPACKET PACKETSZ +#else +# define MAXPACKET 65536 +#endif + + +/* We need this time later. */ +typedef union querybuf +{ + HEADER hdr; + unsigned char buf[MAXPACKET]; +} querybuf; + + +static const short int qtypes[] = { ns_t_a, ns_t_aaaa }; +#define nqtypes (sizeof (qtypes) / sizeof (qtypes[0])) + + +enum nss_status +_nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen, + char **result,int *errnop, int *h_errnop) +{ + /* Just an alibi buffer, res_nquery will allocate a real buffer for + us. */ + unsigned char buf[20]; + union + { + querybuf *buf; + unsigned char *ptr; + } ansp = { .ptr = buf }; + enum nss_status status = NSS_STATUS_UNAVAIL; + + for (int i = 0; i < nqtypes; ++i) + { + int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i], + buf, sizeof (buf), &ansp.ptr, NULL, NULL, + NULL, NULL); + if (r > 0) + { + /* We need to decode the response. Just one question record. + And if we got no answers we bail out, too. */ + if (ansp.buf->hdr.qdcount != htons (1)) + continue; + + /* Number of answers. */ + unsigned int ancount = ntohs (ansp.buf->hdr.ancount); + + /* Beginning and end of the buffer with query, answer, and the + rest. */ + unsigned char *ptr = &ansp.buf->buf[sizeof (HEADER)]; + unsigned char *endptr = ansp.ptr + r; + + /* Skip over the query. This is the name, type, and class. */ + int s = __dn_skipname (ptr, endptr); + if (s < 0) + { + unavail: + status = NSS_STATUS_UNAVAIL; + break; + } + + /* Skip over the name and the two 16-bit values containing type + and class. */ + ptr += s + 2 * sizeof (uint16_t); + + while (ancount-- > 0) + { + /* Now the reply. First again the name from the query, + then type, class, TTL, and the length of the RDATA. + We remember the name start. */ + unsigned char *namestart = ptr; + s = __dn_skipname (ptr, endptr); + if (s < 0) + goto unavail; + + ptr += s; + + /* Check that there are enough bytes for the RR + metadata. */ + if (endptr - ptr < 10) + goto unavail; + + /* Check whether type and class match. */ + uint_fast16_t type; + NS_GET16 (type, ptr); + if (type == qtypes[i]) + { + /* We found the record. */ + s = __dn_expand (ansp.buf->buf, endptr, namestart, + buffer, buflen); + if (s < 0) + { + if (errno != EMSGSIZE) + goto unavail; + + /* The buffer is too small. */ + *errnop = ERANGE; + status = NSS_STATUS_TRYAGAIN; + h_errno = NETDB_INTERNAL; + } + else + { + /* Success. */ + *result = buffer; + status = NSS_STATUS_SUCCESS; + } + + goto out; + } + + if (type != ns_t_cname) + goto unavail; + + if (__ns_get16 (ptr) != ns_c_in) + goto unavail; + + /* Also skip over class and TTL. */ + ptr += sizeof (uint16_t) + sizeof (uint32_t); + + /* Skip over RDATA length and RDATA itself. */ + uint16_t rdatalen = __ns_get16 (ptr); + ptr += sizeof (uint16_t); + /* Not enough room for RDATA. */ + if (endptr - ptr < rdatalen) + goto unavail; + ptr += rdatalen; + } + } + + /* Restore original buffer before retry. */ + if (ansp.ptr != buf) + { + free (ansp.ptr); + ansp.ptr = buf; + } + } + + out: + *h_errnop = h_errno; + + if (ansp.ptr != buf) + free (ansp.ptr); + + return status; +} diff --git a/REORG.TODO/resolv/nss_dns/dns-host.c b/REORG.TODO/resolv/nss_dns/dns-host.c new file mode 100644 index 0000000000..f121aa3de7 --- /dev/null +++ b/REORG.TODO/resolv/nss_dns/dns-host.c @@ -0,0 +1,1333 @@ +/* Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996. + + 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 + <http://www.gnu.org/licenses/>. */ + +/* Parts of this file are plain copies of the file `gethtnamadr.c' from + the bind package and it has the following copyright. */ + +/* + * ++Copyright++ 1985, 1988, 1993 + * - + * Copyright (c) 1985, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * - + * --Copyright-- + */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +#include "nsswitch.h" +#include <arpa/nameser.h> + +/* Get implementeation for some internal functions. */ +#include <resolv/resolv-internal.h> +#include <resolv/mapv4v6addr.h> +#include <resolv/mapv4v6hostent.h> + +#define RESOLVSORT + +#if PACKETSZ > 65536 +# define MAXPACKET PACKETSZ +#else +# define MAXPACKET 65536 +#endif +/* As per RFC 1034 and 1035 a host name cannot exceed 255 octets in length. */ +#ifdef MAXHOSTNAMELEN +# undef MAXHOSTNAMELEN +#endif +#define MAXHOSTNAMELEN 256 + +/* We need this time later. */ +typedef union querybuf +{ + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +static enum nss_status getanswer_r (const querybuf *answer, int anslen, + const char *qname, int qtype, + struct hostent *result, char *buffer, + size_t buflen, int *errnop, int *h_errnop, + int map, int32_t *ttlp, char **canonp); + +static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1, + const querybuf *answer2, int anslen2, + const char *qname, + struct gaih_addrtuple **pat, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); + +extern enum nss_status _nss_dns_gethostbyname3_r (const char *name, int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp, + char **canonp); +hidden_proto (_nss_dns_gethostbyname3_r) + +/* Return the expected RDATA length for an address record type (A or + AAAA). */ +static int +rrtype_to_rdata_length (int type) +{ + switch (type) + { + case T_A: + return INADDRSZ; + case T_AAAA: + return IN6ADDRSZ; + default: + return -1; + } +} + +enum nss_status +_nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *h_errnop, int32_t *ttlp, char **canonp) +{ + union + { + querybuf *buf; + u_char *ptr; + } host_buffer; + querybuf *orig_host_buffer; + char tmp[NS_MAXDNAME]; + int size, type, n; + const char *cp; + int map = 0; + int olderr = errno; + enum nss_status status; + + if (__res_maybe_init (&_res, 0) == -1) + return NSS_STATUS_UNAVAIL; + + switch (af) { + case AF_INET: + size = INADDRSZ; + type = T_A; + break; + case AF_INET6: + size = IN6ADDRSZ; + type = T_AAAA; + break; + default: + *h_errnop = NO_DATA; + *errnop = EAFNOSUPPORT; + return NSS_STATUS_UNAVAIL; + } + + result->h_addrtype = af; + result->h_length = size; + + /* + * if there aren't any dots, it could be a user-level alias. + * this is also done in res_query() since we are not the only + * function that looks up host names. + */ + if (strchr (name, '.') == NULL + && (cp = res_hostalias (&_res, name, tmp, sizeof (tmp))) != NULL) + name = cp; + + host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024); + + n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf, + 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL); + if (n < 0) + { + switch (errno) + { + case ESRCH: + status = NSS_STATUS_TRYAGAIN; + h_errno = TRY_AGAIN; + break; + /* System has run out of file descriptors. */ + case EMFILE: + case ENFILE: + h_errno = NETDB_INTERNAL; + /* Fall through. */ + case ECONNREFUSED: + case ETIMEDOUT: + status = NSS_STATUS_UNAVAIL; + break; + default: + status = NSS_STATUS_NOTFOUND; + break; + } + *h_errnop = h_errno; + if (h_errno == TRY_AGAIN) + *errnop = EAGAIN; + else + __set_errno (olderr); + + /* If we are looking for an IPv6 address and mapping is enabled + by having the RES_USE_INET6 bit in _res.options set, we try + another lookup. */ + if (af == AF_INET6 && res_use_inet6 ()) + n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf, + host_buffer.buf != orig_host_buffer + ? MAXPACKET : 1024, &host_buffer.ptr, + NULL, NULL, NULL, NULL); + + if (n < 0) + { + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + return status; + } + + map = 1; + + result->h_addrtype = AF_INET; + result->h_length = INADDRSZ; + } + + status = getanswer_r (host_buffer.buf, n, name, type, result, buffer, buflen, + errnop, h_errnop, map, ttlp, canonp); + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + return status; +} +hidden_def (_nss_dns_gethostbyname3_r) + + +enum nss_status +_nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *h_errnop) +{ + return _nss_dns_gethostbyname3_r (name, af, result, buffer, buflen, errnop, + h_errnop, NULL, NULL); +} + + +enum nss_status +_nss_dns_gethostbyname_r (const char *name, struct hostent *result, + char *buffer, size_t buflen, int *errnop, + int *h_errnop) +{ + enum nss_status status = NSS_STATUS_NOTFOUND; + + if (res_use_inet6 ()) + status = _nss_dns_gethostbyname3_r (name, AF_INET6, result, buffer, + buflen, errnop, h_errnop, NULL, NULL); + if (status == NSS_STATUS_NOTFOUND) + status = _nss_dns_gethostbyname3_r (name, AF_INET, result, buffer, + buflen, errnop, h_errnop, NULL, NULL); + + return status; +} + + +enum nss_status +_nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat, + char *buffer, size_t buflen, int *errnop, + int *herrnop, int32_t *ttlp) +{ + if (__res_maybe_init (&_res, 0) == -1) + return NSS_STATUS_UNAVAIL; + + /* + * if there aren't any dots, it could be a user-level alias. + * this is also done in res_query() since we are not the only + * function that looks up host names. + */ + if (strchr (name, '.') == NULL) + { + char *tmp = alloca (NS_MAXDNAME); + const char *cp = res_hostalias (&_res, name, tmp, NS_MAXDNAME); + if (cp != NULL) + name = cp; + } + + union + { + querybuf *buf; + u_char *ptr; + } host_buffer; + querybuf *orig_host_buffer; + host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048); + u_char *ans2p = NULL; + int nans2p = 0; + int resplen2 = 0; + int ans2p_malloced = 0; + + int olderr = errno; + enum nss_status status; + int n = __libc_res_nsearch (&_res, name, C_IN, T_QUERY_A_AND_AAAA, + host_buffer.buf->buf, 2048, &host_buffer.ptr, + &ans2p, &nans2p, &resplen2, &ans2p_malloced); + if (n >= 0) + { + status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p, + resplen2, name, pat, buffer, buflen, + errnop, herrnop, ttlp); + } + else + { + switch (errno) + { + case ESRCH: + status = NSS_STATUS_TRYAGAIN; + h_errno = TRY_AGAIN; + break; + /* System has run out of file descriptors. */ + case EMFILE: + case ENFILE: + h_errno = NETDB_INTERNAL; + /* Fall through. */ + case ECONNREFUSED: + case ETIMEDOUT: + status = NSS_STATUS_UNAVAIL; + break; + default: + status = NSS_STATUS_NOTFOUND; + break; + } + + *herrnop = h_errno; + if (h_errno == TRY_AGAIN) + *errnop = EAGAIN; + else + __set_errno (olderr); + } + + /* Check whether ans2p was separately allocated. */ + if (ans2p_malloced) + free (ans2p); + + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + + return status; +} + + +extern enum nss_status _nss_dns_gethostbyaddr2_r (const void *addr, + socklen_t len, int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); +hidden_proto (_nss_dns_gethostbyaddr2_r) + +enum nss_status +_nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af, + struct hostent *result, char *buffer, size_t buflen, + int *errnop, int *h_errnop, int32_t *ttlp) +{ + static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff }; + static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 }; + static const u_char v6local[] = { 0,0, 0,1 }; + const u_char *uaddr = (const u_char *)addr; + struct host_data + { + char *aliases[MAX_NR_ALIASES]; + unsigned char host_addr[16]; /* IPv4 or IPv6 */ + char *h_addr_ptrs[MAX_NR_ADDRS + 1]; + char linebuffer[0]; + } *host_data = (struct host_data *) buffer; + union + { + querybuf *buf; + u_char *ptr; + } host_buffer; + querybuf *orig_host_buffer; + char qbuf[MAXDNAME+1], *qp = NULL; + size_t size; + int n, status; + int olderr = errno; + + uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data); + buffer += pad; + buflen = buflen > pad ? buflen - pad : 0; + + if (__glibc_unlikely (buflen < sizeof (struct host_data))) + { + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + host_data = (struct host_data *) buffer; + + if (__res_maybe_init (&_res, 0) == -1) + return NSS_STATUS_UNAVAIL; + + if (af == AF_INET6 && len == IN6ADDRSZ + && (memcmp (uaddr, mapped, sizeof mapped) == 0 + || (memcmp (uaddr, tunnelled, sizeof tunnelled) == 0 + && memcmp (&uaddr[sizeof tunnelled], v6local, sizeof v6local)))) + { + /* Unmap. */ + addr += sizeof mapped; + uaddr += sizeof mapped; + af = AF_INET; + len = INADDRSZ; + } + + switch (af) + { + case AF_INET: + size = INADDRSZ; + break; + case AF_INET6: + size = IN6ADDRSZ; + break; + default: + *errnop = EAFNOSUPPORT; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_UNAVAIL; + } + if (size > len) + { + *errnop = EAFNOSUPPORT; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_UNAVAIL; + } + + host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024); + + switch (af) + { + case AF_INET: + sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff), + (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff)); + break; + case AF_INET6: + qp = qbuf; + for (n = IN6ADDRSZ - 1; n >= 0; n--) + { + static const char nibblechar[16] = "0123456789abcdef"; + *qp++ = nibblechar[uaddr[n] & 0xf]; + *qp++ = '.'; + *qp++ = nibblechar[(uaddr[n] >> 4) & 0xf]; + *qp++ = '.'; + } + strcpy(qp, "ip6.arpa"); + break; + default: + /* Cannot happen. */ + break; + } + + n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, + 1024, &host_buffer.ptr, NULL, NULL, NULL, NULL); + if (n < 0) + { + *h_errnop = h_errno; + __set_errno (olderr); + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; + } + + status = getanswer_r (host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen, + errnop, h_errnop, 0 /* XXX */, ttlp, NULL); + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + if (status != NSS_STATUS_SUCCESS) + return status; + + result->h_addrtype = af; + result->h_length = len; + memcpy (host_data->host_addr, addr, len); + host_data->h_addr_ptrs[0] = (char *) host_data->host_addr; + host_data->h_addr_ptrs[1] = NULL; + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; +} +hidden_def (_nss_dns_gethostbyaddr2_r) + + +enum nss_status +_nss_dns_gethostbyaddr_r (const void *addr, socklen_t len, int af, + struct hostent *result, char *buffer, size_t buflen, + int *errnop, int *h_errnop) +{ + return _nss_dns_gethostbyaddr2_r (addr, len, af, result, buffer, buflen, + errnop, h_errnop, NULL); +} + +static void addrsort (char **ap, int num); + +static void +addrsort (char **ap, int num) +{ + int i, j; + char **p; + short aval[MAX_NR_ADDRS]; + int needsort = 0; + + p = ap; + if (num > MAX_NR_ADDRS) + num = MAX_NR_ADDRS; + for (i = 0; i < num; i++, p++) + { + for (j = 0 ; (unsigned)j < _res.nsort; j++) + if (_res.sort_list[j].addr.s_addr == + (((struct in_addr *)(*p))->s_addr & _res.sort_list[j].mask)) + break; + aval[i] = j; + if (needsort == 0 && i > 0 && j < aval[i-1]) + needsort = i; + } + if (!needsort) + return; + + while (needsort++ < num) + for (j = needsort - 2; j >= 0; j--) + if (aval[j] > aval[j+1]) + { + char *hp; + + i = aval[j]; + aval[j] = aval[j+1]; + aval[j+1] = i; + + hp = ap[j]; + ap[j] = ap[j+1]; + ap[j+1] = hp; + } + else + break; +} + +static enum nss_status +getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, + struct hostent *result, char *buffer, size_t buflen, + int *errnop, int *h_errnop, int map, int32_t *ttlp, char **canonp) +{ + struct host_data + { + char *aliases[MAX_NR_ALIASES]; + unsigned char host_addr[16]; /* IPv4 or IPv6 */ + char *h_addr_ptrs[0]; + } *host_data; + int linebuflen; + const HEADER *hp; + const u_char *end_of_message, *cp; + int n, ancount, qdcount; + int haveanswer, had_error; + char *bp, **ap, **hap; + char tbuf[MAXDNAME]; + const char *tname; + int (*name_ok) (const char *); + u_char packtmp[NS_MAXCDNAME]; + int have_to_map = 0; + uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data); + buffer += pad; + buflen = buflen > pad ? buflen - pad : 0; + if (__glibc_unlikely (buflen < sizeof (struct host_data))) + { + /* The buffer is too small. */ + too_small: + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + host_data = (struct host_data *) buffer; + linebuflen = buflen - sizeof (struct host_data); + if (buflen - sizeof (struct host_data) != linebuflen) + linebuflen = INT_MAX; + + tname = qname; + result->h_name = NULL; + end_of_message = answer->buf + anslen; + switch (qtype) + { + case T_A: + case T_AAAA: + name_ok = res_hnok; + break; + case T_PTR: + name_ok = res_dnok; + break; + default: + *errnop = ENOENT; + return NSS_STATUS_UNAVAIL; /* XXX should be abort(); */ + } + + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs (hp->ancount); + qdcount = ntohs (hp->qdcount); + cp = answer->buf + HFIXEDSZ; + if (__glibc_unlikely (qdcount != 1)) + { + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen) + goto too_small; + bp = (char *) &host_data->h_addr_ptrs[ancount + 1]; + linebuflen -= (ancount + 1) * sizeof (char *); + + n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) + { + if (__glibc_unlikely (errno == EMSGSIZE)) + goto too_small; + + n = -1; + } + + if (n > 0 && bp[0] == '.') + bp[0] = '\0'; + + if (__glibc_unlikely (n < 0)) + { + *errnop = errno; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + if (__glibc_unlikely (name_ok (bp) == 0)) + { + errno = EBADMSG; + *errnop = EBADMSG; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + cp += n + QFIXEDSZ; + + if (qtype == T_A || qtype == T_AAAA) + { + /* res_send() has already verified that the query name is the + * same as the one we sent; this just gets the expanded name + * (i.e., with the succeeding search-domain tacked on). + */ + n = strlen (bp) + 1; /* for the \0 */ + if (n >= MAXHOSTNAMELEN) + { + *h_errnop = NO_RECOVERY; + *errnop = ENOENT; + return NSS_STATUS_TRYAGAIN; + } + result->h_name = bp; + bp += n; + linebuflen -= n; + if (linebuflen < 0) + goto too_small; + /* The qname can be abbreviated, but h_name is now absolute. */ + qname = result->h_name; + } + + ap = host_data->aliases; + *ap = NULL; + result->h_aliases = host_data->aliases; + hap = host_data->h_addr_ptrs; + *hap = NULL; + result->h_addr_list = host_data->h_addr_ptrs; + haveanswer = 0; + had_error = 0; + + while (ancount-- > 0 && cp < end_of_message && had_error == 0) + { + int type, class; + + n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) + { + if (__glibc_unlikely (errno == EMSGSIZE)) + goto too_small; + + n = -1; + } + + if (__glibc_unlikely (n < 0 || (*name_ok) (bp) == 0)) + { + ++had_error; + continue; + } + cp += n; /* name */ + + if (__glibc_unlikely (cp + 10 > end_of_message)) + { + ++had_error; + continue; + } + + type = __ns_get16 (cp); + cp += INT16SZ; /* type */ + class = __ns_get16 (cp); + cp += INT16SZ; /* class */ + int32_t ttl = __ns_get32 (cp); + cp += INT32SZ; /* TTL */ + n = __ns_get16 (cp); + cp += INT16SZ; /* len */ + + if (end_of_message - cp < n) + { + /* RDATA extends beyond the end of the packet. */ + ++had_error; + continue; + } + + if (__glibc_unlikely (class != C_IN)) + { + /* XXX - debug? syslog? */ + cp += n; + continue; /* XXX - had_error++ ? */ + } + + if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) + { + /* A CNAME could also have a TTL entry. */ + if (ttlp != NULL && ttl < *ttlp) + *ttlp = ttl; + + if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1]) + continue; + n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf); + if (__glibc_unlikely (n < 0 || (*name_ok) (tbuf) == 0)) + { + ++had_error; + continue; + } + cp += n; + /* Store alias. */ + *ap++ = bp; + n = strlen (bp) + 1; /* For the \0. */ + if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) + { + ++had_error; + continue; + } + bp += n; + linebuflen -= n; + /* Get canonical name. */ + n = strlen (tbuf) + 1; /* For the \0. */ + if (__glibc_unlikely (n > linebuflen)) + goto too_small; + if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) + { + ++had_error; + continue; + } + result->h_name = bp; + bp = __mempcpy (bp, tbuf, n); /* Cannot overflow. */ + linebuflen -= n; + continue; + } + + if (qtype == T_PTR && type == T_CNAME) + { + /* A CNAME could also have a TTL entry. */ + if (ttlp != NULL && ttl < *ttlp) + *ttlp = ttl; + + n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf); + if (__glibc_unlikely (n < 0 || res_dnok (tbuf) == 0)) + { + ++had_error; + continue; + } + cp += n; + /* Get canonical name. */ + n = strlen (tbuf) + 1; /* For the \0. */ + if (__glibc_unlikely (n > linebuflen)) + goto too_small; + if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) + { + ++had_error; + continue; + } + tname = bp; + bp = __mempcpy (bp, tbuf, n); /* Cannot overflow. */ + linebuflen -= n; + continue; + } + + if (type == T_A && qtype == T_AAAA && map) + have_to_map = 1; + else if (__glibc_unlikely (type != qtype)) + { + cp += n; + continue; /* XXX - had_error++ ? */ + } + + switch (type) + { + case T_PTR: + if (__glibc_unlikely (strcasecmp (tname, bp) != 0)) + { + cp += n; + continue; /* XXX - had_error++ ? */ + } + + n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) + { + if (__glibc_unlikely (errno == EMSGSIZE)) + goto too_small; + + n = -1; + } + + if (__glibc_unlikely (n < 0 || res_hnok (bp) == 0)) + { + ++had_error; + break; + } + if (ttlp != NULL && ttl < *ttlp) + *ttlp = ttl; + /* bind would put multiple PTR records as aliases, but we don't do + that. */ + result->h_name = bp; + if (have_to_map) + { + n = strlen (bp) + 1; /* for the \0 */ + if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) + { + ++had_error; + break; + } + bp += n; + linebuflen -= n; + if (map_v4v6_hostent (result, &bp, &linebuflen)) + goto too_small; + } + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; + case T_A: + case T_AAAA: + if (__glibc_unlikely (strcasecmp (result->h_name, bp) != 0)) + { + cp += n; + continue; /* XXX - had_error++ ? */ + } + + /* Stop parsing at a record whose length is incorrect. */ + if (n != rrtype_to_rdata_length (type)) + { + ++had_error; + break; + } + + /* Skip records of the wrong type. */ + if (n != result->h_length) + { + cp += n; + continue; + } + if (!haveanswer) + { + int nn; + + /* We compose a single hostent out of the entire chain of + entries, so the TTL of the hostent is essentially the lowest + TTL in the chain. */ + if (ttlp != NULL && ttl < *ttlp) + *ttlp = ttl; + if (canonp != NULL) + *canonp = bp; + result->h_name = bp; + nn = strlen (bp) + 1; /* for the \0 */ + bp += nn; + linebuflen -= nn; + } + + linebuflen -= sizeof (align) - ((u_long) bp % sizeof (align)); + bp += sizeof (align) - ((u_long) bp % sizeof (align)); + + if (__glibc_unlikely (n > linebuflen)) + goto too_small; + bp = __mempcpy (*hap++ = bp, cp, n); + cp += n; + linebuflen -= n; + break; + default: + abort (); + } + if (had_error == 0) + ++haveanswer; + } + + if (haveanswer > 0) + { + *ap = NULL; + *hap = NULL; + /* + * Note: we sort even if host can take only one address + * in its return structures - should give it the "best" + * address in that case, not some random one + */ + if (_res.nsort && haveanswer > 1 && qtype == T_A) + addrsort (host_data->h_addr_ptrs, haveanswer); + + if (result->h_name == NULL) + { + n = strlen (qname) + 1; /* For the \0. */ + if (n > linebuflen) + goto too_small; + if (n >= MAXHOSTNAMELEN) + goto no_recovery; + result->h_name = bp; + bp = __mempcpy (bp, qname, n); /* Cannot overflow. */ + linebuflen -= n; + } + + if (have_to_map) + if (map_v4v6_hostent (result, &bp, &linebuflen)) + goto too_small; + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; + } + no_recovery: + *h_errnop = NO_RECOVERY; + *errnop = ENOENT; + /* Special case here: if the resolver sent a result but it only + contains a CNAME while we are looking for a T_A or T_AAAA record, + we fail with NOTFOUND instead of TRYAGAIN. */ + return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases + ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN); +} + + +static enum nss_status +gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname, + struct gaih_addrtuple ***patp, + char **bufferp, size_t *buflenp, + int *errnop, int *h_errnop, int32_t *ttlp, int *firstp) +{ + char *buffer = *bufferp; + size_t buflen = *buflenp; + + struct gaih_addrtuple **pat = *patp; + const HEADER *hp = &answer->hdr; + int ancount = ntohs (hp->ancount); + int qdcount = ntohs (hp->qdcount); + const u_char *cp = answer->buf + HFIXEDSZ; + const u_char *end_of_message = answer->buf + anslen; + if (__glibc_unlikely (qdcount != 1)) + { + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + + u_char packtmp[NS_MAXCDNAME]; + int n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + /* We unpack the name to check it for validity. But we do not need + it later. */ + if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1) + { + if (__glibc_unlikely (errno == EMSGSIZE)) + { + too_small: + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + n = -1; + } + + if (__glibc_unlikely (n < 0)) + { + *errnop = errno; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + if (__glibc_unlikely (res_hnok (buffer) == 0)) + { + errno = EBADMSG; + *errnop = EBADMSG; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } + cp += n + QFIXEDSZ; + + int haveanswer = 0; + int had_error = 0; + char *canon = NULL; + char *h_name = NULL; + int h_namelen = 0; + + if (ancount == 0) + { + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + while (ancount-- > 0 && cp < end_of_message && had_error == 0) + { + n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + if (n != -1 && + (h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1) + { + if (__glibc_unlikely (errno == EMSGSIZE)) + goto too_small; + + n = -1; + } + if (__glibc_unlikely (n < 0 || res_hnok (buffer) == 0)) + { + ++had_error; + continue; + } + if (*firstp && canon == NULL) + { + h_name = buffer; + buffer += h_namelen; + buflen -= h_namelen; + } + + cp += n; /* name */ + + if (__glibc_unlikely (cp + 10 > end_of_message)) + { + ++had_error; + continue; + } + + int type = __ns_get16 (cp); + cp += INT16SZ; /* type */ + int class = __ns_get16 (cp); + cp += INT16SZ; /* class */ + int32_t ttl = __ns_get32 (cp); + cp += INT32SZ; /* TTL */ + n = __ns_get16 (cp); + cp += INT16SZ; /* len */ + + if (end_of_message - cp < n) + { + /* RDATA extends beyond the end of the packet. */ + ++had_error; + continue; + } + + if (class != C_IN) + { + cp += n; + continue; + } + + if (type == T_CNAME) + { + char tbuf[MAXDNAME]; + + /* A CNAME could also have a TTL entry. */ + if (ttlp != NULL && ttl < *ttlp) + *ttlp = ttl; + + n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf); + if (__glibc_unlikely (n < 0 || res_hnok (tbuf) == 0)) + { + ++had_error; + continue; + } + cp += n; + + if (*firstp) + { + /* Reclaim buffer space. */ + if (h_name + h_namelen == buffer) + { + buffer = h_name; + buflen += h_namelen; + } + + n = strlen (tbuf) + 1; + if (__glibc_unlikely (n > buflen)) + goto too_small; + if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) + { + ++had_error; + continue; + } + + canon = buffer; + buffer = __mempcpy (buffer, tbuf, n); + buflen -= n; + h_namelen = 0; + } + continue; + } + + /* Stop parsing if we encounter a record with incorrect RDATA + length. */ + if (type == T_A || type == T_AAAA) + { + if (n != rrtype_to_rdata_length (type)) + { + ++had_error; + continue; + } + } + else + { + /* Skip unknown records. */ + cp += n; + continue; + } + + assert (type == T_A || type == T_AAAA); + if (*pat == NULL) + { + uintptr_t pad = (-(uintptr_t) buffer + % __alignof__ (struct gaih_addrtuple)); + buffer += pad; + buflen = buflen > pad ? buflen - pad : 0; + + if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple))) + goto too_small; + + *pat = (struct gaih_addrtuple *) buffer; + buffer += sizeof (struct gaih_addrtuple); + buflen -= sizeof (struct gaih_addrtuple); + } + + (*pat)->name = NULL; + (*pat)->next = NULL; + + if (*firstp) + { + /* We compose a single hostent out of the entire chain of + entries, so the TTL of the hostent is essentially the lowest + TTL in the chain. */ + if (ttlp != NULL && ttl < *ttlp) + *ttlp = ttl; + + (*pat)->name = canon ?: h_name; + + *firstp = 0; + } + + (*pat)->family = type == T_A ? AF_INET : AF_INET6; + memcpy ((*pat)->addr, cp, n); + cp += n; + (*pat)->scopeid = 0; + + pat = &((*pat)->next); + + haveanswer = 1; + } + + if (haveanswer) + { + *patp = pat; + *bufferp = buffer; + *buflenp = buflen; + + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; + } + + /* Special case here: if the resolver sent a result but it only + contains a CNAME while we are looking for a T_A or T_AAAA record, + we fail with NOTFOUND instead of TRYAGAIN. */ + if (canon != NULL) + { + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; +} + + +static enum nss_status +gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2, + int anslen2, const char *qname, + struct gaih_addrtuple **pat, char *buffer, size_t buflen, + int *errnop, int *h_errnop, int32_t *ttlp) +{ + int first = 1; + + enum nss_status status = NSS_STATUS_NOTFOUND; + + /* Combining the NSS status of two distinct queries requires some + compromise and attention to symmetry (A or AAAA queries can be + returned in any order). What follows is a breakdown of how this + code is expected to work and why. We discuss only SUCCESS, + TRYAGAIN, NOTFOUND and UNAVAIL, since they are the only returns + that apply (though RETURN and MERGE exist). We make a distinction + between TRYAGAIN (recoverable) and TRYAGAIN' (not-recoverable). + A recoverable TRYAGAIN is almost always due to buffer size issues + and returns ERANGE in errno and the caller is expected to retry + with a larger buffer. + + Lastly, you may be tempted to make significant changes to the + conditions in this code to bring about symmetry between responses. + Please don't change anything without due consideration for + expected application behaviour. Some of the synthesized responses + aren't very well thought out and sometimes appear to imply that + IPv4 responses are always answer 1, and IPv6 responses are always + answer 2, but that's not true (see the implementation of send_dg + and send_vc to see response can arrive in any order, particularly + for UDP). However, we expect it holds roughly enough of the time + that this code works, but certainly needs to be fixed to make this + a more robust implementation. + + ---------------------------------------------- + | Answer 1 Status / | Synthesized | Reason | + | Answer 2 Status | Status | | + |--------------------------------------------| + | SUCCESS/SUCCESS | SUCCESS | [1] | + | SUCCESS/TRYAGAIN | TRYAGAIN | [5] | + | SUCCESS/TRYAGAIN' | SUCCESS | [1] | + | SUCCESS/NOTFOUND | SUCCESS | [1] | + | SUCCESS/UNAVAIL | SUCCESS | [1] | + | TRYAGAIN/SUCCESS | TRYAGAIN | [2] | + | TRYAGAIN/TRYAGAIN | TRYAGAIN | [2] | + | TRYAGAIN/TRYAGAIN' | TRYAGAIN | [2] | + | TRYAGAIN/NOTFOUND | TRYAGAIN | [2] | + | TRYAGAIN/UNAVAIL | TRYAGAIN | [2] | + | TRYAGAIN'/SUCCESS | SUCCESS | [3] | + | TRYAGAIN'/TRYAGAIN | TRYAGAIN | [3] | + | TRYAGAIN'/TRYAGAIN' | TRYAGAIN' | [3] | + | TRYAGAIN'/NOTFOUND | TRYAGAIN' | [3] | + | TRYAGAIN'/UNAVAIL | UNAVAIL | [3] | + | NOTFOUND/SUCCESS | SUCCESS | [3] | + | NOTFOUND/TRYAGAIN | TRYAGAIN | [3] | + | NOTFOUND/TRYAGAIN' | TRYAGAIN' | [3] | + | NOTFOUND/NOTFOUND | NOTFOUND | [3] | + | NOTFOUND/UNAVAIL | UNAVAIL | [3] | + | UNAVAIL/SUCCESS | UNAVAIL | [4] | + | UNAVAIL/TRYAGAIN | UNAVAIL | [4] | + | UNAVAIL/TRYAGAIN' | UNAVAIL | [4] | + | UNAVAIL/NOTFOUND | UNAVAIL | [4] | + | UNAVAIL/UNAVAIL | UNAVAIL | [4] | + ---------------------------------------------- + + [1] If the first response is a success we return success. + This ignores the state of the second answer and in fact + incorrectly sets errno and h_errno to that of the second + answer. However because the response is a success we ignore + *errnop and *h_errnop (though that means you touched errno on + success). We are being conservative here and returning the + likely IPv4 response in the first answer as a success. + + [2] If the first response is a recoverable TRYAGAIN we return + that instead of looking at the second response. The + expectation here is that we have failed to get an IPv4 response + and should retry both queries. + + [3] If the first response was not a SUCCESS and the second + response is not NOTFOUND (had a SUCCESS, need to TRYAGAIN, + or failed entirely e.g. TRYAGAIN' and UNAVAIL) then use the + result from the second response, otherwise the first responses + status is used. Again we have some odd side-effects when the + second response is NOTFOUND because we overwrite *errnop and + *h_errnop that means that a first answer of NOTFOUND might see + its *errnop and *h_errnop values altered. Whether it matters + in practice that a first response NOTFOUND has the wrong + *errnop and *h_errnop is undecided. + + [4] If the first response is UNAVAIL we return that instead of + looking at the second response. The expectation here is that + it will have failed similarly e.g. configuration failure. + + [5] Testing this code is complicated by the fact that truncated + second response buffers might be returned as SUCCESS if the + first answer is a SUCCESS. To fix this we add symmetry to + TRYAGAIN with the second response. If the second response + is a recoverable error we now return TRYAGIN even if the first + response was SUCCESS. */ + + if (anslen1 > 0) + status = gaih_getanswer_slice(answer1, anslen1, qname, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, + &first); + + if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND + || (status == NSS_STATUS_TRYAGAIN + /* We want to look at the second answer in case of an + NSS_STATUS_TRYAGAIN only if the error is non-recoverable, i.e. + *h_errnop is NO_RECOVERY. If not, and if the failure was due to + an insufficient buffer (ERANGE), then we need to drop the results + and pass on the NSS_STATUS_TRYAGAIN to the caller so that it can + repeat the query with a larger buffer. */ + && (*errnop != ERANGE || *h_errnop == NO_RECOVERY))) + && answer2 != NULL && anslen2 > 0) + { + enum nss_status status2 = gaih_getanswer_slice(answer2, anslen2, qname, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, + &first); + /* Use the second response status in some cases. */ + if (status != NSS_STATUS_SUCCESS && status2 != NSS_STATUS_NOTFOUND) + status = status2; + /* Do not return a truncated second response (unless it was + unavoidable e.g. unrecoverable TRYAGAIN). */ + if (status == NSS_STATUS_SUCCESS + && (status2 == NSS_STATUS_TRYAGAIN + && *errnop == ERANGE && *h_errnop != NO_RECOVERY)) + status = NSS_STATUS_TRYAGAIN; + } + + return status; +} diff --git a/REORG.TODO/resolv/nss_dns/dns-network.c b/REORG.TODO/resolv/nss_dns/dns-network.c new file mode 100644 index 0000000000..2be72d33a3 --- /dev/null +++ b/REORG.TODO/resolv/nss_dns/dns-network.c @@ -0,0 +1,472 @@ +/* Copyright (C) 1996-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996. + + 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 + <http://www.gnu.org/licenses/>. */ + +/* Parts of this file are plain copies of the file `getnetnamadr.c' from + the bind package and it has the following copyright. */ + +/* Copyright (c) 1993 Carlos Leandro and Rui Salgueiro + * Dep. Matematica Universidade de Coimbra, Portugal, Europe + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + */ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <stddef.h> + +#include "nsswitch.h" +#include <arpa/inet.h> +#include <arpa/nameser.h> + +/* Maximum number of aliases we allow. */ +#define MAX_NR_ALIASES 48 + + +#if PACKETSZ > 65536 +# define MAXPACKET PACKETSZ +#else +# define MAXPACKET 65536 +#endif + + +typedef enum +{ + BYADDR, + BYNAME +} lookup_method; + + +/* We need this time later. */ +typedef union querybuf +{ + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +/* Prototypes for local functions. */ +static enum nss_status getanswer_r (const querybuf *answer, int anslen, + struct netent *result, char *buffer, + size_t buflen, int *errnop, int *h_errnop, + lookup_method net_i); + + +enum nss_status +_nss_dns_getnetbyname_r (const char *name, struct netent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop) +{ + /* Return entry for network with NAME. */ + union + { + querybuf *buf; + u_char *ptr; + } net_buffer; + querybuf *orig_net_buffer; + int anslen; + enum nss_status status; + + if (__res_maybe_init (&_res, 0) == -1) + return NSS_STATUS_UNAVAIL; + + net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); + + anslen = __libc_res_nsearch (&_res, name, C_IN, T_PTR, net_buffer.buf->buf, + 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL); + if (anslen < 0) + { + /* Nothing found. */ + *errnop = errno; + if (net_buffer.buf != orig_net_buffer) + free (net_buffer.buf); + return (errno == ECONNREFUSED + || errno == EPFNOSUPPORT + || errno == EAFNOSUPPORT) + ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; + } + + status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen, + errnop, herrnop, BYNAME); + if (net_buffer.buf != orig_net_buffer) + free (net_buffer.buf); + return status; +} + + +enum nss_status +_nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result, + char *buffer, size_t buflen, int *errnop, + int *herrnop) +{ + /* Return entry for network with NAME. */ + enum nss_status status; + union + { + querybuf *buf; + u_char *ptr; + } net_buffer; + querybuf *orig_net_buffer; + unsigned int net_bytes[4]; + char qbuf[MAXDNAME]; + int cnt, anslen; + u_int32_t net2; + int olderr = errno; + + /* No net address lookup for IPv6 yet. */ + if (type != AF_INET) + return NSS_STATUS_UNAVAIL; + + if (__res_maybe_init (&_res, 0) == -1) + return NSS_STATUS_UNAVAIL; + + net2 = (u_int32_t) net; + for (cnt = 4; net2 != 0; net2 >>= 8) + net_bytes[--cnt] = net2 & 0xff; + + switch (cnt) + { + case 3: + /* Class A network. */ + sprintf (qbuf, "0.0.0.%u.in-addr.arpa", net_bytes[3]); + break; + case 2: + /* Class B network. */ + sprintf (qbuf, "0.0.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2]); + break; + case 1: + /* Class C network. */ + sprintf (qbuf, "0.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2], + net_bytes[1]); + break; + case 0: + /* Class D - E network. */ + sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2], + net_bytes[1], net_bytes[0]); + break; + } + + net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); + + anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf, + 1024, &net_buffer.ptr, NULL, NULL, NULL, NULL); + if (anslen < 0) + { + /* Nothing found. */ + int err = errno; + __set_errno (olderr); + if (net_buffer.buf != orig_net_buffer) + free (net_buffer.buf); + return (err == ECONNREFUSED + || err == EPFNOSUPPORT + || err == EAFNOSUPPORT) + ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; + } + + status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen, + errnop, herrnop, BYADDR); + if (net_buffer.buf != orig_net_buffer) + free (net_buffer.buf); + if (status == NSS_STATUS_SUCCESS) + { + /* Strip trailing zeros. */ + unsigned int u_net = net; /* Maybe net should be unsigned? */ + + while ((u_net & 0xff) == 0 && u_net != 0) + u_net >>= 8; + result->n_net = u_net; + } + + return status; +} + + +static enum nss_status +getanswer_r (const querybuf *answer, int anslen, struct netent *result, + char *buffer, size_t buflen, int *errnop, int *h_errnop, + lookup_method net_i) +{ + /* + * Find first satisfactory answer + * + * answer --> +------------+ ( MESSAGE ) + * | Header | + * +------------+ + * | Question | the question for the name server + * +------------+ + * | Answer | RRs answering the question + * +------------+ + * | Authority | RRs pointing toward an authority + * | Additional | RRs holding additional information + * +------------+ + */ + struct net_data + { + char *aliases[MAX_NR_ALIASES]; + char linebuffer[0]; + } *net_data; + + uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct net_data); + buffer += pad; + + if (__glibc_unlikely (buflen < sizeof (*net_data) + pad)) + { + /* The buffer is too small. */ + too_small: + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + buflen -= pad; + + net_data = (struct net_data *) buffer; + int linebuflen = buflen - offsetof (struct net_data, linebuffer); + if (buflen - offsetof (struct net_data, linebuffer) != linebuflen) + linebuflen = INT_MAX; + const unsigned char *end_of_message = &answer->buf[anslen]; + const HEADER *header_pointer = &answer->hdr; + /* #/records in the answer section. */ + int answer_count = ntohs (header_pointer->ancount); + /* #/entries in the question section. */ + int question_count = ntohs (header_pointer->qdcount); + char *bp = net_data->linebuffer; + const unsigned char *cp = &answer->buf[HFIXEDSZ]; + char **alias_pointer; + int have_answer; + u_char packtmp[NS_MAXCDNAME]; + + if (question_count == 0) + { + /* FIXME: the Sun version uses for host name lookup an additional + parameter for pointing to h_errno. this is missing here. + OSF/1 has a per-thread h_errno variable. */ + if (header_pointer->aa != 0) + { + __set_h_errno (HOST_NOT_FOUND); + return NSS_STATUS_NOTFOUND; + } + else + { + __set_h_errno (TRY_AGAIN); + return NSS_STATUS_TRYAGAIN; + } + } + + /* Skip the question part. */ + while (question_count-- > 0) + { + int n = __dn_skipname (cp, end_of_message); + if (n < 0 || end_of_message - (cp + n) < QFIXEDSZ) + { + __set_h_errno (NO_RECOVERY); + return NSS_STATUS_UNAVAIL; + } + cp += n + QFIXEDSZ; + } + + alias_pointer = result->n_aliases = &net_data->aliases[0]; + *alias_pointer = NULL; + have_answer = 0; + + while (--answer_count >= 0 && cp < end_of_message) + { + int n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) + { + if (errno == EMSGSIZE) + goto too_small; + + n = -1; + } + + if (n > 0 && bp[0] == '.') + bp[0] = '\0'; + + if (n < 0 || res_dnok (bp) == 0) + break; + cp += n; + + if (end_of_message - cp < 10) + { + __set_h_errno (NO_RECOVERY); + return NSS_STATUS_UNAVAIL; + } + + int type, class; + GETSHORT (type, cp); + GETSHORT (class, cp); + cp += INT32SZ; /* TTL */ + uint16_t rdatalen; + GETSHORT (rdatalen, cp); + if (end_of_message - cp < rdatalen) + { + __set_h_errno (NO_RECOVERY); + return NSS_STATUS_UNAVAIL; + } + + if (class == C_IN && type == T_PTR) + { + n = __ns_name_unpack (answer->buf, end_of_message, cp, + packtmp, sizeof packtmp); + if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) + { + if (errno == EMSGSIZE) + goto too_small; + + n = -1; + } + + if (n < 0 || !res_hnok (bp)) + { + /* XXX What does this mean? The original form from bind + returns NULL. Incrementing cp has no effect in any case. + What should I return here. ??? */ + cp += n; + return NSS_STATUS_UNAVAIL; + } + cp += rdatalen; + if (alias_pointer + 2 < &net_data->aliases[MAX_NR_ALIASES]) + { + *alias_pointer++ = bp; + n = strlen (bp) + 1; + bp += n; + linebuflen -= n; + result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC; + ++have_answer; + } + } + else + /* Skip over unknown record data. */ + cp += rdatalen; + } + + if (have_answer) + { + *alias_pointer = NULL; + switch (net_i) + { + case BYADDR: + result->n_name = *result->n_aliases++; + result->n_net = 0L; + return NSS_STATUS_SUCCESS; + + case BYNAME: + { + char **ap; + for (ap = result->n_aliases; *ap != NULL; ++ap) + { + /* Check each alias name for being of the forms: + 4.3.2.1.in-addr.arpa = net 1.2.3.4 + 3.2.1.in-addr.arpa = net 0.1.2.3 + 2.1.in-addr.arpa = net 0.0.1.2 + 1.in-addr.arpa = net 0.0.0.1 + */ + uint32_t val = 0; /* Accumulator for n_net value. */ + unsigned int shift = 0; /* Which part we are parsing now. */ + const char *p = *ap; /* Consuming the string. */ + do + { + /* Match the leading 0 or 0[xX] base indicator. */ + unsigned int base = 10; + if (*p == '0' && p[1] != '.') + { + base = 8; + ++p; + if (*p == 'x' || *p == 'X') + { + base = 16; + ++p; + if (*p == '.') + break; /* No digit here. Give up on alias. */ + } + if (*p == '\0') + break; + } + + uint32_t part = 0; /* Accumulates this part's number. */ + do + { + if (isdigit (*p) && (*p - '0' < base)) + part = (part * base) + (*p - '0'); + else if (base == 16 && isxdigit (*p)) + part = (part << 4) + 10 + (tolower (*p) - 'a'); + ++p; + } while (*p != '\0' && *p != '.'); + + if (*p != '.') + break; /* Bad form. Give up on this name. */ + + /* Install this as the next more significant byte. */ + val |= part << shift; + shift += 8; + ++p; + + /* If we are out of digits now, there are two cases: + 1. We are done with digits and now see "in-addr.arpa". + 2. This is not the droid we are looking for. */ + if (!isdigit (*p) && !strcasecmp (p, "in-addr.arpa")) + { + result->n_net = val; + return NSS_STATUS_SUCCESS; + } + + /* Keep going when we have seen fewer than 4 parts. */ + } while (shift < 32); + } + } + break; + } + } + + __set_h_errno (TRY_AGAIN); + return NSS_STATUS_TRYAGAIN; +} |