From 1eb946b93509b94db2bddce741f2f3b483418a6d Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 10 May 2008 23:27:39 +0000 Subject: * include/resolv.h: Adjust __libc_res_nquery and __libc_res_nsend prototypes. * include/arpa/nameser_compat.h: Define T_UNSPEC. * nis/Versions (libnss_nis): Export _nss_nis_gethostbyname4_r. (libnss_nisplus): Export _nss_nisplus_gethostbyname4_r. * nis/nss_nis/nis-hosts.c (LINE_PARSER): Change to also handle af==AF_UNSPEC. (_nss_nis_gethostbyname4_r): New function. * nis/nss_nisplus/nisplus-hosts.c (_nss_nisplus_parse_hostent): Change to also handle af==AF_UNSPEC. (get_tablename): New function. Use it to avoid duplication. (_nss_nisplus_gethostbyname4_r): New function. * nscd/aicache.c (addhstaiX): Use gethostbyname4_r function is available. * nss/Versions (libnss_files): Export _nss_files_gethostbyname4_r. * nss/nss.h: Define struct gaih_addrtuple. * nss/nss_files/files-hosts.c (LINE_PARSER): Change to also handle af==AF_UNSPEC. (_nss_files_gethostbyname4_r): New function. * resolv/Versions (libnss_dns): Export _nss_dns_gethostbyname4_r. * resolv/gethnmaddr.c: Adjust __libc_res_nsearch and __libc_res_nquery calls. * resolv/res_query.c (__libc_res_nquery): Take two additional parameters for second answer buffer. Handle type=T_UNSPEC to mean look up IPv4 and IPv6. Change all callers. * resolv/res_send.c (__libc_res_nsend): Take five aditional parameters for an additional query and answer buffer. Pass to send_vc and send_dg. (send_vc): Send possibly two requests and receive two answers. (send_dg): Likewise. * resolv/nss_dns/dns-host.c: Adjust calls to __libc_res_nsearch and __libc_res_nquery. (_nss_dns_gethostbyname4_r): New function. (gaih_getanswer_slice): Likewise. (gaih_getanswer): Likewise. * resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Adjust __libc_res_nquery call. * resolv/nss_dns/dns-network.c (_nss_dns_getnetbyaddr_r): Likewise. (_nss_dns_getnetbyname_r): Adjust __libc_res_nsearch call. * sysdeps/posix/getaddrinfo.c: Use gethostbyname4_r function is available. --- resolv/Versions | 1 + resolv/gethnamaddr.c | 6 +- resolv/nss_dns/dns-canon.c | 4 +- resolv/nss_dns/dns-host.c | 337 +++++++++++++++++++++++++++++++++++++++++-- resolv/nss_dns/dns-network.c | 6 +- resolv/res_query.c | 149 ++++++++++++++----- resolv/res_send.c | 323 ++++++++++++++++++++++++++++++----------- 7 files changed, 693 insertions(+), 133 deletions(-) (limited to 'resolv') diff --git a/resolv/Versions b/resolv/Versions index fc2111a1cb..355d95d606 100644 --- a/resolv/Versions +++ b/resolv/Versions @@ -89,6 +89,7 @@ libnss_dns { _nss_dns_gethostbyname_r; _nss_dns_getnetbyaddr_r; _nss_dns_getnetbyname_r; _nss_dns_getcanonname_r; _nss_dns_gethostbyaddr2_r; + _nss_dns_gethostbyname4_r; } } diff --git a/resolv/gethnamaddr.c b/resolv/gethnamaddr.c index 7be23158d0..2a9bd0b3c2 100644 --- a/resolv/gethnamaddr.c +++ b/resolv/gethnamaddr.c @@ -621,7 +621,7 @@ gethostbyname2(name, af) buf.buf = origbuf = (querybuf *) alloca (1024); if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024, - &buf.ptr)) < 0) { + &buf.ptr, NULL, NULL)) < 0) { if (buf.buf != origbuf) free (buf.buf); Dprintf("res_nsearch failed (%d)\n", n); @@ -716,12 +716,12 @@ gethostbyaddr(addr, len, af) buf.buf = orig_buf = (querybuf *) alloca (1024); n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024, - &buf.ptr); + &buf.ptr, NULL, NULL); if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) { strcpy(qp, "ip6.int"); n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, buf.buf != orig_buf ? MAXPACKET : 1024, - &buf.ptr); + &buf.ptr, NULL, NULL); } if (n < 0) { if (buf.buf != orig_buf) diff --git a/resolv/nss_dns/dns-canon.c b/resolv/nss_dns/dns-canon.c index fca6cd8997..47949b862f 100644 --- a/resolv/nss_dns/dns-canon.c +++ b/resolv/nss_dns/dns-canon.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2004, 2006 Free Software Foundation, Inc. +/* Copyright (C) 2004, 2006, 2008 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 2004. @@ -61,7 +61,7 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen, for (int i = 0; i < nqtypes; ++i) { int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i], - buf, sizeof (buf), &ansp.ptr); + buf, sizeof (buf), &ansp.ptr, NULL, NULL); if (r > 0) { /* We need to decode the response. Just one question record. diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c index dfdf9c5dcb..c52f9f7f84 100644 --- a/resolv/nss_dns/dns-host.c +++ b/resolv/nss_dns/dns-host.c @@ -71,6 +71,7 @@ * --Copyright-- */ +#include #include #include #include @@ -127,6 +128,14 @@ static enum nss_status getanswer_r (const querybuf *answer, int anslen, 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, @@ -186,11 +195,11 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result, 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); + 1024, &host_buffer.ptr, NULL, NULL); if (n < 0) { - enum nss_status status = (errno == ECONNREFUSED - ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); + status = (errno == ECONNREFUSED + ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); *h_errnop = h_errno; if (h_errno == TRY_AGAIN) *errnop = EAGAIN; @@ -203,7 +212,8 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result, if (af == AF_INET6 && (_res.options & 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); + ? MAXPACKET : 1024, &host_buffer.ptr, + NULL, NULL); if (n < 0) { @@ -255,6 +265,70 @@ _nss_dns_gethostbyname_r (const char *name, struct hostent *result, } +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; + + char tmp[NS_MAXDNAME]; + + /* + * 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) + { + const char *cp = res_hostalias (&_res, name, tmp, sizeof (tmp)); + 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 olderr = errno; + enum nss_status status; + int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC, + host_buffer.buf->buf, 2048, &host_buffer.ptr, + &ans2p, &nans2p); + if (n < 0) + { + status = (errno == ECONNREFUSED + ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND); + *herrnop = h_errno; + if (h_errno == TRY_AGAIN) + *errnop = EAGAIN; + else + __set_errno (olderr); + + if (host_buffer.buf != orig_host_buffer) + free (host_buffer.buf); + + return status; + } + + status = gaih_getanswer(host_buffer.buf, n, (const querybuf *) ans2p, + nans2p, name, pat, buffer, buflen, + errnop, herrnop, ttlp); + + 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, @@ -342,7 +416,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af, qp += sprintf (qp, "%02hhx", uaddr[n]); strcpy (qp, "].ip6.arpa"); n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, - host_buffer.buf->buf, 1024, &host_buffer.ptr); + host_buffer.buf->buf, 1024, &host_buffer.ptr, + NULL, NULL); if (n >= 0) goto got_it_already; } @@ -363,13 +438,14 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af, } n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, - 1024, &host_buffer.ptr); + 1024, &host_buffer.ptr, NULL, NULL); if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) { strcpy (qp, "ip6.int"); n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf, host_buffer.buf != orig_host_buffer - ? MAXPACKET : 1024, &host_buffer.ptr); + ? MAXPACKET : 1024, &host_buffer.ptr, + NULL, NULL); } if (n < 0) { @@ -555,7 +631,8 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, if (n > 0 && bp[0] == '.') bp[0] = '\0'; - if (n < 0 || (*name_ok) (bp) == 0) + if (__builtin_expect (n < 0 || ((*name_ok) (bp) == 0 && (errno = EBADMSG)), + 0)) { *errnop = errno; *h_errnop = NO_RECOVERY; @@ -629,7 +706,7 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, continue; /* XXX - had_error++ ? */ } - if ((qtype ==T_A || qtype == T_AAAA) && type == T_CNAME) + if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) { if (ap >= &host_data->aliases[MAX_NR_ALIASES - 1]) continue; @@ -857,3 +934,245 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype, 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 (__builtin_expect (qdcount != 1, 0)) + { + *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 (__builtin_expect (errno, 0) == EMSGSIZE) + { + too_small: + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + n = -1; + } + + if (__builtin_expect (n < 0 || (res_hnok (buffer) == 0 + && (errno = EBADMSG)), 0)) + { + *errnop = errno; + *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; + + 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 (__builtin_expect (errno, 0) == EMSGSIZE) + goto too_small; + + n = -1; + } + if (n < 0 || res_hnok (buffer) == 0) + { + ++had_error; + continue; + } + if (*firstp) + { + h_name = buffer; + buffer += h_namelen; + buflen -= h_namelen; + } + + cp += n; /* name */ + 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 (class != C_IN) + { + cp += n; + continue; + } + + if (type == T_CNAME) + { + char tbuf[MAXDNAME]; + n = dn_expand (answer->buf, end_of_message, cp, tbuf, sizeof tbuf); + if (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 (__builtin_expect (n > buflen, 0)) + goto too_small; + if (__builtin_expect (n >= MAXHOSTNAMELEN, 0)) + { + ++had_error; + continue; + } + + canon = buffer; + buffer = __mempcpy (buffer, tbuf, n); + buflen -= n; + h_namelen = 0; + } + continue; + } + if (__builtin_expect (type == T_SIG, 0) + || __builtin_expect (type == T_KEY, 0) + || __builtin_expect (type == T_NXT, 0) + || __builtin_expect (type == T_PTR, 0)) + { + /* We don't support DNSSEC yet. For now, ignore the record + and send a low priority message to syslog. + + We also don't expect T_PTR messages. */ + syslog (LOG_DEBUG | LOG_AUTH, + "getaddrinfo*.gaih_getanswer: got type \"%s\"", + p_type (type)); + cp += n; + continue; + } + if (type != T_A && type != T_AAAA) + abort (); + + if (*pat == NULL) + { + uintptr_t pad = (-(uintptr_t) buffer + % __alignof__ (struct gaih_addrtuple)); + buffer += pad; + buflen = buflen > pad ? buflen - pad : 0; + + if (__builtin_expect (buflen < sizeof (struct gaih_addrtuple), + 0)) + { + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; + return NSS_STATUS_TRYAGAIN; + } + + *pat = (struct gaih_addrtuple *) buffer; + buffer += sizeof (struct gaih_addrtuple); + buflen -= sizeof (struct gaih_addrtuple); + } + + (*pat)->name = NULL; + (*pat)->next = NULL; + + if (*firstp) + { + if (ttl != 0 && ttlp != NULL) + *ttlp = ttl; + + if (canon != NULL) + { + (*pat)->name = canon; + + /* Reclaim buffer space. */ + if (h_name + h_namelen == buffer) + { + buffer = h_name; + buflen += h_namelen; + } + } + else + (*pat)->name = 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. */ + return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND; +} + + +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 = gaih_getanswer_slice(answer1, anslen1, qname, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, + &first); + if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND) + && answer2 != NULL) + status = gaih_getanswer_slice(answer2, anslen2, qname, + &pat, &buffer, &buflen, + errnop, h_errnop, ttlp, &first); + + return status; +} diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c index 4552b5b678..40736fbe94 100644 --- a/resolv/nss_dns/dns-network.c +++ b/resolv/nss_dns/dns-network.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007 +/* Copyright (C) 1996, 1997, 1998, 1999, 2002, 2004, 2007, 2008 Free Software Foundation, Inc. This file is part of the GNU C Library. Extended from original form by Ulrich Drepper , 1996. @@ -130,7 +130,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result, net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024); anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf, - 1024, &net_buffer.ptr); + 1024, &net_buffer.ptr, NULL, NULL); if (anslen < 0) { /* Nothing found. */ @@ -206,7 +206,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result, 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); + 1024, &net_buffer.ptr, NULL, NULL); if (anslen < 0) { /* Nothing found. */ diff --git a/resolv/res_query.c b/resolv/res_query.c index 4371af5b05..a8e8d7b739 100644 --- a/resolv/res_query.c +++ b/resolv/res_query.c @@ -97,7 +97,7 @@ static const char rcsid[] = "$BINDId: res_query.c,v 8.20 2000/02/29 05:39:12 vix static int __libc_res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, u_char *answer, int anslen, - u_char **answerp); + u_char **answerp, u_char **answerp2, int *nanswerp2); /* * Formulate a normal query, send, and await answer. @@ -115,15 +115,20 @@ __libc_res_nquery(res_state statp, int class, int type, /* class and type of query */ u_char *answer, /* buffer to put answer */ int anslen, /* size of answer buffer */ - u_char **answerp) /* if buffer needs to be enlarged */ + u_char **answerp, /* if buffer needs to be enlarged */ + u_char **answerp2, + int *nanswerp2) { - u_char *buf; HEADER *hp = (HEADER *) answer; int n, use_malloc = 0; u_int oflags = statp->_flags; - size_t bufsize = QUERYSIZE; - buf = alloca (bufsize); + size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE; + u_char *buf = alloca (bufsize); + u_char *query1 = buf; + int nquery1 = -1; + u_char *query2 = NULL; + int nquery2 = 0; again: hp->rcode = NOERROR; /* default */ @@ -133,18 +138,47 @@ __libc_res_nquery(res_state statp, printf(";; res_query(%s, %d, %d)\n", name, class, type); #endif - n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, - buf, bufsize); - if (n > 0 - && (oflags & RES_F_EDNS0ERR) == 0 - && (statp->options & RES_USE_EDNS0) != 0) - n = __res_nopt(statp, n, buf, bufsize, anslen); + if (type == T_UNSPEC) + { + n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL, + query1, bufsize); + if (n > 0) + { + if ((oflags & RES_F_EDNS0ERR) == 0 + && (statp->options & RES_USE_EDNS0) != 0) + n = __res_nopt(statp, n, query1, bufsize, anslen / 2); + + nquery1 = n; + query2 = buf + nquery1; + n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0, + NULL, query2, bufsize - n); + if (n > 0 + && (oflags & RES_F_EDNS0ERR) == 0 + && (statp->options & RES_USE_EDNS0) != 0) + n = __res_nopt(statp, n, query2, bufsize - n, anslen / 2); + nquery2 = n; + } + } + else + { + n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, + query1, bufsize); + + if (n > 0 + && (oflags & RES_F_EDNS0ERR) == 0 + && (statp->options & RES_USE_EDNS0) != 0) + n = __res_nopt(statp, n, query1, bufsize, anslen); + + nquery1 = n; + } + if (__builtin_expect (n <= 0, 0) && !use_malloc) { /* Retry just in case res_nmkquery failed because of too short buffer. Shouldn't happen. */ - bufsize = MAXPACKET; + bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET; buf = malloc (bufsize); if (buf != NULL) { + query1 = buf; use_malloc = 1; goto again; } @@ -168,7 +202,8 @@ __libc_res_nquery(res_state statp, return (n); } assert (answerp == NULL || (void *) *answerp == (void *) answer); - n = __libc_res_nsend(statp, buf, n, answer, anslen, answerp); + n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer, + anslen, answerp, answerp2, nanswerp2); if (use_malloc) free (buf); if (n < 0) { @@ -184,20 +219,37 @@ __libc_res_nquery(res_state statp, /* __libc_res_nsend might have reallocated the buffer. */ hp = (HEADER *) *answerp; - if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { + /* We simplify the following tests by assigning HP to HP2. It + is easy to verify that this is the same as ignoring all + tests of HP2. */ + HEADER *hp2 = answerp2 ? (HEADER *) *answerp2 : hp; + + if ((hp->rcode != NOERROR || ntohs(hp->ancount) == 0) + && (hp2->rcode != NOERROR || ntohs(hp2->ancount) == 0)) { #ifdef DEBUG - if (statp->options & RES_DEBUG) + if (statp->options & RES_DEBUG) { printf(";; rcode = %d, ancount=%d\n", hp->rcode, ntohs(hp->ancount)); + if (hp != hp2) + printf(";; rcode2 = %d, ancount2=%d\n", hp2->rcode, + ntohs(hp2->ancount)); + } #endif - switch (hp->rcode) { + switch (hp->rcode == NOERROR ? hp2->rcode : hp->rcode) { case NXDOMAIN: + if ((hp->rcode == NOERROR && ntohs (hp->ancount) != 0) + || (hp2->rcode == NOERROR + && ntohs (hp2->ancount) != 0)) + goto success; RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); break; case SERVFAIL: RES_SET_H_ERRNO(statp, TRY_AGAIN); break; case NOERROR: + if (ntohs (hp->ancount) != 0 + || ntohs (hp2->ancount) != 0) + goto success; RES_SET_H_ERRNO(statp, NO_DATA); break; case FORMERR: @@ -209,6 +261,7 @@ __libc_res_nquery(res_state statp, } return (-1); } + success: return (n); } libresolv_hidden_def (__libc_res_nquery) @@ -221,7 +274,7 @@ res_nquery(res_state statp, int anslen) /* size of answer buffer */ { return __libc_res_nquery(statp, name, class, type, answer, anslen, - NULL); + NULL, NULL, NULL); } libresolv_hidden_def (res_nquery) @@ -233,11 +286,13 @@ libresolv_hidden_def (res_nquery) */ int __libc_res_nsearch(res_state statp, - const char *name, /* domain name */ - int class, int type, /* class and type of query */ - u_char *answer, /* buffer to put answer */ - int anslen, /* size of answer */ - u_char **answerp) + const char *name, /* domain name */ + int class, int type, /* class and type of query */ + u_char *answer, /* buffer to put answer */ + int anslen, /* size of answer */ + u_char **answerp, + u_char **answerp2, + int *nanswerp2) { const char *cp, * const *domain; HEADER *hp = (HEADER *) answer; @@ -260,7 +315,8 @@ __libc_res_nsearch(res_state statp, /* If there aren't any dots, it could be a user-level alias. */ if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) return (__libc_res_nquery(statp, cp, class, type, answer, - anslen, answerp)); + anslen, answerp, answerp2, + nanswerp2)); #ifdef DEBUG if (statp->options & RES_DEBUG) @@ -276,7 +332,8 @@ __libc_res_nsearch(res_state statp, saved_herrno = -1; if (dots >= statp->ndots || trailing_dot) { ret = __libc_res_nquerydomain(statp, name, NULL, class, type, - answer, anslen, answerp); + answer, anslen, answerp, + answerp2, nanswerp2); if (ret > 0 || trailing_dot) return (ret); saved_herrno = h_errno; @@ -285,6 +342,12 @@ __libc_res_nsearch(res_state statp, answer = *answerp; anslen = MAXPACKET; } + if (answerp2 + && (*answerp2 < answer || *answerp2 >= answer + anslen)) + { + free (*answerp2); + *answerp2 = NULL; + } } /* @@ -307,7 +370,8 @@ __libc_res_nsearch(res_state statp, ret = __libc_res_nquerydomain(statp, name, *domain, class, type, - answer, anslen, answerp); + answer, anslen, answerp, + answerp2, nanswerp2); if (ret > 0) return (ret); @@ -315,6 +379,13 @@ __libc_res_nsearch(res_state statp, answer = *answerp; anslen = MAXPACKET; } + if (answerp2 + && (*answerp2 < answer + || *answerp2 >= answer + anslen)) + { + free (*answerp2); + *answerp2 = NULL; + } /* * If no server present, give up. @@ -368,7 +439,8 @@ __libc_res_nsearch(res_state statp, */ if (dots && !(tried_as_is || root_on_list)) { ret = __libc_res_nquerydomain(statp, name, NULL, class, type, - answer, anslen, answerp); + answer, anslen, answerp, + answerp2, nanswerp2); if (ret > 0) return (ret); } @@ -380,6 +452,11 @@ __libc_res_nsearch(res_state statp, * else send back meaningless H_ERRNO, that being the one from * the last DNSRCH we did. */ + if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen)) + { + free (*answerp2); + *answerp2 = NULL; + } if (saved_herrno != -1) RES_SET_H_ERRNO(statp, saved_herrno); else if (got_nodata) @@ -398,7 +475,7 @@ res_nsearch(res_state statp, int anslen) /* size of answer */ { return __libc_res_nsearch(statp, name, class, type, answer, - anslen, NULL); + anslen, NULL, NULL, NULL); } libresolv_hidden_def (res_nsearch) @@ -408,12 +485,14 @@ libresolv_hidden_def (res_nsearch) */ static int __libc_res_nquerydomain(res_state statp, - const char *name, - const char *domain, - int class, int type, /* class and type of query */ - u_char *answer, /* buffer to put answer */ - int anslen, /* size of answer */ - u_char **answerp) + const char *name, + const char *domain, + int class, int type, /* class and type of query */ + u_char *answer, /* buffer to put answer */ + int anslen, /* size of answer */ + u_char **answerp, + u_char **answerp2, + int *nanswerp2) { char nbuf[MAXDNAME]; const char *longname = nbuf; @@ -450,7 +529,7 @@ __libc_res_nquerydomain(res_state statp, sprintf(nbuf, "%s.%s", name, domain); } return (__libc_res_nquery(statp, longname, class, type, answer, - anslen, answerp)); + anslen, answerp, answerp2, nanswerp2)); } int @@ -462,7 +541,7 @@ res_nquerydomain(res_state statp, int anslen) /* size of answer */ { return __libc_res_nquerydomain(statp, name, domain, class, type, - answer, anslen, NULL); + answer, anslen, NULL, NULL, NULL); } libresolv_hidden_def (res_nquerydomain) diff --git a/resolv/res_send.c b/resolv/res_send.c index 23306a2fb4..b3dbd702a2 100644 --- a/resolv/res_send.c +++ b/resolv/res_send.c @@ -176,10 +176,14 @@ evNowTime(struct timespec *res) { /* Forward. */ static int send_vc(res_state, const u_char *, int, - u_char **, int *, int *, int, u_char **); + const u_char *, int, + u_char **, int *, int *, int, u_char **, + u_char **, int *, int *); static int send_dg(res_state, const u_char *, int, + const u_char *, int, u_char **, int *, int *, int, - int *, int *, u_char **); + int *, int *, u_char **, + u_char **, int *, int *); #ifdef DEBUG static void Aerror(const res_state, FILE *, const char *, int, const struct sockaddr *); @@ -334,33 +338,41 @@ libresolv_hidden_def (res_queriesmatch) int __libc_res_nsend(res_state statp, const u_char *buf, int buflen, - u_char *ans, int anssiz, u_char **ansp) + const u_char *buf2, int buflen2, + u_char *ans, int anssiz, u_char **ansp, u_char **ansp2, + int *nansp2) { - int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; + int gotsomewhere, terrno, try, v_circuit, resplen, resplen2, ns, n; if (statp->nscount == 0) { __set_errno (ESRCH); return (-1); } - if (anssiz < HFIXEDSZ) { + if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) { __set_errno (EINVAL); return (-1); } - if ((statp->qhook || statp->rhook) && anssiz < MAXPACKET && ansp) { - u_char *buf = malloc (MAXPACKET); - if (buf == NULL) - return (-1); - memcpy (buf, ans, HFIXEDSZ); - *ansp = buf; - ans = buf; - anssiz = MAXPACKET; +#ifdef USE_HOOKS + if (__builtin_expect (statp->qhook || statp->rhook, 0)) { + if (anssiz < MAXPACKET && ansp) { + u_char *buf = malloc (MAXPACKET); + if (buf == NULL) + return (-1); + memcpy (buf, ans, HFIXEDSZ); + *ansp = buf; + ans = buf; + anssiz = MAXPACKET; + } } +#endif DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), (stdout, ";; res_send()\n"), buf, buflen); - v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ; + v_circuit = ((statp->options & RES_USEVC) + || buflen > PACKETSZ + || buflen2 > PACKETSZ); gotsomewhere = 0; terrno = ETIMEDOUT; @@ -442,7 +454,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, * Some resolvers want to even out the load on their nameservers. * Note that RES_BLAST overrides RES_ROTATE. */ - if ((statp->options & RES_ROTATE) != 0 && + if (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) && (statp->options & RES_BLAST) == 0) { struct sockaddr_in6 *ina; unsigned int map; @@ -479,8 +491,9 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, if (nsap == NULL) goto next_ns; - same_ns: - if (statp->qhook) { + same_ns: +#ifdef USE_HOOKS + if (__builtin_expect (statp->qhook != NULL, 0)) { int done = 0, loops = 0; do { @@ -512,6 +525,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, } } while (!done); } +#endif #ifdef DEBUG char tmpbuf[40]; @@ -521,29 +535,34 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, ns + 1, inet_ntop(AF_INET6, &nsap->sin6_addr, tmpbuf, sizeof (tmpbuf)))); - if (v_circuit) { + if (__builtin_expect (v_circuit, 0)) { /* Use VC; at most one attempt per server. */ try = statp->retry; - n = send_vc(statp, buf, buflen, &ans, &anssiz, &terrno, - ns, ansp); + n = send_vc(statp, buf, buflen, buf2, buflen2, + &ans, &anssiz, &terrno, + ns, ansp, ansp2, nansp2, &resplen2); if (n < 0) return (-1); if (n == 0) goto next_ns; - resplen = n; } else { /* Use datagrams. */ - n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno, - ns, &v_circuit, &gotsomewhere, ansp); + n = send_dg(statp, buf, buflen, buf2, buflen2, + &ans, &anssiz, &terrno, + ns, &v_circuit, &gotsomewhere, ansp, + ansp2, nansp2, &resplen2); if (n < 0) return (-1); if (n == 0) goto next_ns; if (v_circuit) + // XXX Check whether both requests failed or + // XXX whether one have been answered successfully goto same_ns; - resplen = n; } + resplen = n; + Dprint((statp->options & RES_DEBUG) || ((statp->pfcode & RES_PRF_REPLY) && (statp->pfcode & RES_PRF_HEAD1)), @@ -553,6 +572,11 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, (statp->pfcode & RES_PRF_REPLY), (stdout, "%s", ""), ans, (resplen > anssiz) ? anssiz : resplen); + if (buf2 != NULL) + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, "%s", ""), + *ansp2, (resplen2 > *nansp2) ? *nansp2 : resplen2); /* * If we have temporarily opened a virtual circuit, @@ -563,7 +587,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, (statp->options & RES_STAYOPEN) == 0) { __res_iclose(statp, false); } - if (statp->rhook) { +#ifdef USE_HOOKS + if (__builtin_expect (statp->rhook, 0)) { int done = 0, loops = 0; do { @@ -593,6 +618,7 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen, } while (!done); } +#endif return (resplen); next_ns: ; } /*foreach ns*/ @@ -612,7 +638,8 @@ int res_nsend(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz) { - return __libc_res_nsend(statp, buf, buflen, ans, anssiz, NULL); + return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz, + NULL, NULL, NULL); } libresolv_hidden_def (res_nsend) @@ -620,17 +647,23 @@ libresolv_hidden_def (res_nsend) static int send_vc(res_state statp, - const u_char *buf, int buflen, u_char **ansp, int *anssizp, - int *terrno, int ns, u_char **anscp) + const u_char *buf, int buflen, const u_char *buf2, int buflen2, + u_char **ansp, int *anssizp, + int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2, + int *resplen2) { const HEADER *hp = (HEADER *) buf; + const HEADER *hp2 = (HEADER *) buf2; u_char *ans = *ansp; - int anssiz = *anssizp; + int orig_anssizp = *anssizp; + // XXX REMOVE + // int anssiz = *anssizp; HEADER *anhp = (HEADER *) ans; struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; int truncating, connreset, resplen, n; - struct iovec iov[2]; + struct iovec iov[4]; u_short len; + u_short len2; u_char *cp; connreset = 0; @@ -677,11 +710,19 @@ send_vc(res_state statp, /* * Send length & message */ - ns_put16((u_short)buflen, (u_char*)&len); + len = htons ((u_short) buflen); evConsIovec(&len, INT16SZ, &iov[0]); evConsIovec((void*)buf, buflen, &iov[1]); - if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, 2)) - != (INT16SZ + buflen)) { + int niov = 2; + ssize_t explen = INT16SZ + buflen; + if (buf2 != NULL) { + len2 = htons ((u_short) buflen2); + evConsIovec(&len2, INT16SZ, &iov[2]); + evConsIovec((void*)buf2, buflen2, &iov[3]); + niov = 4; + explen += INT16SZ + buflen2; + } + if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, niov)) != explen) { *terrno = errno; Perror(statp, stderr, "write failed", errno); __res_iclose(statp, false); @@ -690,6 +731,8 @@ send_vc(res_state statp, /* * Receive length & response */ + int recvresp1 = 0; + int recvresp2 = buf2 == NULL; read_len: cp = ans; len = INT16SZ; @@ -718,30 +761,66 @@ send_vc(res_state statp, } return (0); } +#ifdef _STRING_ARCH_unaligned + resplen = ntohs (*(uint16_t *) ans); +#else resplen = ns_get16(ans); - if (resplen > anssiz) { +#endif + + int *thisanssizp; + u_char **thisansp; + int *thisresplenp; + if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) { + thisanssizp = anssizp; + thisansp = anscp ?: ansp; + assert (anscp != NULL || ansp2 == NULL); + thisresplenp = &resplen; + } else { + if (*anssizp != MAXPACKET) { + /* No buffer allocated for the first + reply. We can try to use the rest + of the user-provided buffer. */ + *anssizp2 = orig_anssizp - resplen; + *ansp2 = *ansp + resplen; + } else { + /* The first reply did not fit into the + user-provided buffer. Maybe the second + answer will. */ + *anssizp2 = orig_anssizp; + *ansp2 = *ansp; + } + + thisanssizp = anssizp2; + thisansp = ansp2; + thisresplenp = resplen2; + } + anhp = (HEADER *) *thisansp; + + *thisresplenp = resplen; + if (resplen > *thisanssizp) { + /* Yes, we test ANSCP here. If we have two buffers + both will be allocatable. */ if (anscp) { - ans = malloc (MAXPACKET); - if (ans == NULL) { + u_char *newp = malloc (MAXPACKET); + if (newp == NULL) { *terrno = ENOMEM; __res_iclose(statp, false); return (0); } - anssiz = MAXPACKET; - *anssizp = MAXPACKET; - *ansp = ans; - *anscp = ans; - anhp = (HEADER *) ans; + *thisanssizp = MAXPACKET; + *thisansp = newp; + anhp = (HEADER *) newp; len = resplen; } else { Dprint(statp->options & RES_DEBUG, (stdout, ";; response truncated\n") ); truncating = 1; - len = anssiz; + len = *thisanssizp; } } else len = resplen; + if (len < HFIXEDSZ) { /* * Undersized message. @@ -752,7 +831,8 @@ send_vc(res_state statp, __res_iclose(statp, false); return (0); } - cp = ans; + + cp = *thisansp; while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){ cp += n; len -= n; @@ -768,7 +848,7 @@ send_vc(res_state statp, * Flush rest of answer so connection stays in synch. */ anhp->tc = 1; - len = resplen - anssiz; + len = resplen - *thisanssizp; while (len != 0) { char junk[PACKETSZ]; @@ -787,14 +867,25 @@ send_vc(res_state statp, * itself confused, then drop the packet and * wait for the correct one. */ - if (hp->id != anhp->id) { + if ((recvresp1 || hp->id != anhp->id) + && (recvresp2 || hp2->id != anhp->id)) { DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer (unexpected):\n"), - ans, (resplen > anssiz) ? anssiz: resplen); + *thisansp, + (resplen > *thisanssiz) ? *thisanssiz: resplen); goto read_len; } + /* Mark which reply we received. */ + if (recvresp1 == 0 && hp->id == anhp->id) + recvresp1 = 1; + else + recvresp2 = 1; + /* Repeat waiting if we have a second answer to arrive. */ + if ((recvresp1 & recvresp2) == 0) + goto read_len; + /* * All is well, or the error is fatal. Signal that the * next nameserver ought not be tried. @@ -804,19 +895,20 @@ send_vc(res_state statp, static int send_dg(res_state statp, - const u_char *buf, int buflen, u_char **ansp, int *anssizp, - int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp) + const u_char *buf, int buflen, const u_char *buf2, int buflen2, + u_char **ansp, int *anssizp, + int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp, + u_char **ansp2, int *anssizp2, int *resplen2) { const HEADER *hp = (HEADER *) buf; + const HEADER *hp2 = (HEADER *) buf2; u_char *ans = *ansp; - int anssiz = *anssizp; - HEADER *anhp = (HEADER *) ans; + int orig_anssizp = *anssizp; struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; struct timespec now, timeout, finish; struct pollfd pfd[1]; int ptimeout; struct sockaddr_in6 from; - socklen_t fromlen; int resplen, seconds, n; if (EXT(statp).nssocks[ns] == -1) { @@ -879,6 +971,8 @@ send_dg(res_state statp, evAddTime(&finish, &now, &timeout); int need_recompute = 0; int nwritten = 0; + int recvresp1 = 0; + int recvresp2 = buf2 == NULL; pfd[0].fd = EXT(statp).nssocks[ns]; pfd[0].events = POLLOUT; wait: @@ -918,35 +1012,73 @@ send_dg(res_state statp, } __set_errno (0); if (pfd[0].revents & POLLOUT) { - if (send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL) != buflen) { + ssize_t sr; + if (nwritten != 0) + sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL); + else + sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL); + + if (sr != buflen) { if (errno == EINTR || errno == EAGAIN) goto recompute_resend; Perror(statp, stderr, "send", errno); goto err_out; } - pfd[0].events = POLLIN; + if (nwritten != 0 || buf2 == NULL) + pfd[0].events = POLLIN; + else + pfd[0].events = POLLIN | POLLOUT; ++nwritten; goto wait; } else if (pfd[0].revents & POLLIN) { - fromlen = sizeof(struct sockaddr_in6); - if (anssiz < MAXPACKET + int *thisanssizp; + u_char **thisansp; + int *thisresplenp; + + if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) { + thisanssizp = anssizp; + thisansp = anscp ?: ansp; + assert (anscp != NULL || ansp2 == NULL); + thisresplenp = &resplen; + } else { + if (*anssizp != MAXPACKET) { + /* No buffer allocated for the first + reply. We can try to use the rest + of the user-provided buffer. */ + *anssizp2 = orig_anssizp - resplen; + *ansp2 = *ansp + resplen; + } else { + /* The first reply did not fit into the + user-provided buffer. Maybe the second + answer will. */ + *anssizp2 = orig_anssizp; + *ansp2 = *ansp; + } + + thisanssizp = anssizp2; + thisansp = ansp2; + thisresplenp = resplen2; + } + + if (*thisanssizp < MAXPACKET + /* Yes, we test ANSCP here. If we have two buffers + both will be allocatable. */ && anscp - && (ioctl (pfd[0].fd, FIONREAD, &resplen) < 0 - || anssiz < resplen)) { - ans = malloc (MAXPACKET); - if (ans == NULL) - ans = *ansp; - else { - anssiz = MAXPACKET; + && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0 + || *thisanssizp < *thisresplenp)) { + u_char *newp = malloc (MAXPACKET); + if (newp != NULL) { *anssizp = MAXPACKET; - *ansp = ans; - *anscp = ans; - anhp = (HEADER *) ans; + *thisansp = ans = newp; } } - resplen = recvfrom(pfd[0].fd, (char*)ans, anssiz,0, - (struct sockaddr *)&from, &fromlen); - if (resplen <= 0) { + HEADER *anhp = (HEADER *) *thisansp; + socklen_t fromlen = sizeof(struct sockaddr_in6); + assert (sizeof(from) <= fromlen); + *thisresplenp = recvfrom(pfd[0].fd, (char*)*thisansp, + *thisanssizp, 0, + (struct sockaddr *)&from, &fromlen); + if (*thisresplenp <= 0) { if (errno == EINTR || errno == EAGAIN) { need_recompute = 1; goto wait; @@ -955,17 +1087,18 @@ send_dg(res_state statp, goto err_out; } *gotsomewhere = 1; - if (resplen < HFIXEDSZ) { + if (*thisresplenp < HFIXEDSZ) { /* * Undersized message. */ Dprint(statp->options & RES_DEBUG, (stdout, ";; undersized: %d\n", - resplen)); + *thisresplen)); *terrno = EMSGSIZE; goto err_out; } - if (hp->id != anhp->id) { + if ((recvresp1 || hp->id != anhp->id) + && (recvresp2 || hp2->id != anhp->id)) { /* * response from old query, ignore it. * XXX - potential security hazard could @@ -974,7 +1107,9 @@ send_dg(res_state statp, DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto wait; } if (!(statp->options & RES_INSECURE1) && @@ -987,14 +1122,16 @@ send_dg(res_state statp, DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; not our server:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto wait; } #ifdef RES_USE_EDNS0 if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) { /* - * Do not retry if the server do not understand + * Do not retry if the server does not understand * EDNS0. The case has to be captured here, as * FORMERR packet do not carry query section, hence * res_queriesmatch() returns 0. @@ -1002,15 +1139,23 @@ send_dg(res_state statp, DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query with EDNS0:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisans, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); /* record the error */ statp->_flags |= RES_F_EDNS0ERR; goto err_out; } #endif - if (!(statp->options & RES_INSECURE2) && - !res_queriesmatch(buf, buf + buflen, - ans, ans + anssiz)) { + if (!(statp->options & RES_INSECURE2) + && (recvresp1 || !res_queriesmatch(buf, buf + buflen, + *thisansp, + *thisansp + + *thisanssizp)) + && (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2, + *thisansp, + *thisansp + + *thisanssizp))) { /* * response contains wrong query? ignore it. * XXX - potential security hazard could @@ -1019,7 +1164,9 @@ send_dg(res_state statp, DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; wrong query name:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto wait; } if (anhp->rcode == SERVFAIL || @@ -1027,7 +1174,9 @@ send_dg(res_state statp, anhp->rcode == REFUSED) { DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); next_ns: __res_iclose(statp, false); /* don't retry if called from dig */ @@ -1038,7 +1187,9 @@ send_dg(res_state statp, && anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) { DprintQ(statp->options & RES_DEBUG, (stdout, "referred query:\n"), - ans, (resplen > anssiz) ? anssiz : resplen); + thisansp, + (*thisresplen > *thisanssiz) + ? *thisanssiz : *thisresplen); goto next_ns; } if (!(statp->options & RES_IGNTC) && anhp->tc) { @@ -1050,8 +1201,18 @@ send_dg(res_state statp, (stdout, ";; truncated answer\n")); *v_circuit = 1; __res_iclose(statp, false); + // XXX if we have received one reply we could + // XXX use it and not repeat it over TCP... return (1); } + /* Mark which reply we received. */ + if (recvresp1 == 0 && hp->id == anhp->id) + recvresp1 = 1; + else + recvresp2 = 1; + /* Repeat waiting if we have a second answer to arrive. */ + if ((recvresp1 & recvresp2) == 0) + goto wait; /* * All is well, or the error is fatal. Signal that the * next nameserver ought not be tried. -- cgit v1.2.3