aboutsummaryrefslogtreecommitdiff
path: root/resolv/res_send.c
diff options
context:
space:
mode:
Diffstat (limited to 'resolv/res_send.c')
-rw-r--r--resolv/res_send.c1502
1 files changed, 0 insertions, 1502 deletions
diff --git a/resolv/res_send.c b/resolv/res_send.c
deleted file mode 100644
index b7b8ecdfc4..0000000000
--- a/resolv/res_send.c
+++ /dev/null
@@ -1,1502 +0,0 @@
-/* Copyright (C) 2016-2017 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <http://www.gnu.org/licenses/>. */
-
-/*
- * Copyright (c) 1985, 1989, 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.
- */
-
-/*
- * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
- *
- * 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.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM 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.
- */
-
-/*
- * Send query to name server and wait for reply.
- */
-
-#include <assert.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/poll.h>
-
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <arpa/inet.h>
-#include <sys/ioctl.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <netdb.h>
-#include <resolv/resolv-internal.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <kernel-features.h>
-#include <libc-diag.h>
-
-#if PACKETSZ > 65536
-#define MAXPACKET PACKETSZ
-#else
-#define MAXPACKET 65536
-#endif
-
-/* From ev_streams.c. */
-
-static inline void
-__attribute ((always_inline))
-evConsIovec(void *buf, size_t cnt, struct iovec *vec) {
- memset(vec, 0xf5, sizeof (*vec));
- vec->iov_base = buf;
- vec->iov_len = cnt;
-}
-
-/* From ev_timers.c. */
-
-#define BILLION 1000000000
-
-static inline void
-evConsTime(struct timespec *res, time_t sec, long nsec) {
- res->tv_sec = sec;
- res->tv_nsec = nsec;
-}
-
-static inline void
-evAddTime(struct timespec *res, const struct timespec *addend1,
- const struct timespec *addend2) {
- res->tv_sec = addend1->tv_sec + addend2->tv_sec;
- res->tv_nsec = addend1->tv_nsec + addend2->tv_nsec;
- if (res->tv_nsec >= BILLION) {
- res->tv_sec++;
- res->tv_nsec -= BILLION;
- }
-}
-
-static inline void
-evSubTime(struct timespec *res, const struct timespec *minuend,
- const struct timespec *subtrahend) {
- res->tv_sec = minuend->tv_sec - subtrahend->tv_sec;
- if (minuend->tv_nsec >= subtrahend->tv_nsec)
- res->tv_nsec = minuend->tv_nsec - subtrahend->tv_nsec;
- else {
- res->tv_nsec = (BILLION
- - subtrahend->tv_nsec + minuend->tv_nsec);
- res->tv_sec--;
- }
-}
-
-static int
-evCmpTime(struct timespec a, struct timespec b) {
- long x = a.tv_sec - b.tv_sec;
-
- if (x == 0L)
- x = a.tv_nsec - b.tv_nsec;
- return (x < 0L ? (-1) : x > 0L ? (1) : (0));
-}
-
-static void
-evNowTime(struct timespec *res) {
- struct timeval now;
-
- if (gettimeofday(&now, NULL) < 0)
- evConsTime(res, 0, 0);
- else
- TIMEVAL_TO_TIMESPEC (&now, res);
-}
-
-
-/* Options. Leave them on. */
-/* #undef DEBUG */
-#include "res_debug.h"
-
-#define EXT(res) ((res)->_u._ext)
-
-/* Forward. */
-
-static struct sockaddr *get_nsaddr (res_state, int);
-static int send_vc(res_state, const u_char *, int,
- const u_char *, int,
- u_char **, int *, int *, int, u_char **,
- u_char **, int *, 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 **,
- u_char **, int *, int *, int *);
-#ifdef DEBUG
-static void Aerror(const res_state, FILE *, const char *, int,
- const struct sockaddr *);
-static void Perror(const res_state, FILE *, const char *, int);
-#endif
-static int sock_eq(struct sockaddr_in6 *, struct sockaddr_in6 *);
-
-/* Public. */
-
-/* int
- * res_isourserver(ina)
- * looks up "ina" in _res.ns_addr_list[]
- * returns:
- * 0 : not found
- * >0 : found
- * author:
- * paul vixie, 29may94
- */
-int
-res_ourserver_p(const res_state statp, const struct sockaddr_in6 *inp)
-{
- int ns;
-
- if (inp->sin6_family == AF_INET) {
- struct sockaddr_in *in4p = (struct sockaddr_in *) inp;
- in_port_t port = in4p->sin_port;
- in_addr_t addr = in4p->sin_addr.s_addr;
-
- for (ns = 0; ns < statp->nscount; ns++) {
- const struct sockaddr_in *srv =
- (struct sockaddr_in *) get_nsaddr (statp, ns);
-
- if ((srv->sin_family == AF_INET) &&
- (srv->sin_port == port) &&
- (srv->sin_addr.s_addr == INADDR_ANY ||
- srv->sin_addr.s_addr == addr))
- return (1);
- }
- } else if (inp->sin6_family == AF_INET6) {
- for (ns = 0; ns < statp->nscount; ns++) {
- const struct sockaddr_in6 *srv
- = (struct sockaddr_in6 *) get_nsaddr (statp, ns);
- if ((srv->sin6_family == AF_INET6) &&
- (srv->sin6_port == inp->sin6_port) &&
- !(memcmp(&srv->sin6_addr, &in6addr_any,
- sizeof (struct in6_addr)) &&
- memcmp(&srv->sin6_addr, &inp->sin6_addr,
- sizeof (struct in6_addr))))
- return (1);
- }
- }
- return (0);
-}
-
-/* int
- * res_nameinquery(name, type, class, buf, eom)
- * look for (name,type,class) in the query section of packet (buf,eom)
- * requires:
- * buf + HFIXEDSZ <= eom
- * returns:
- * -1 : format error
- * 0 : not found
- * >0 : found
- * author:
- * paul vixie, 29may94
- */
-int
-res_nameinquery(const char *name, int type, int class,
- const u_char *buf, const u_char *eom)
-{
- const u_char *cp = buf + HFIXEDSZ;
- int qdcount = ntohs(((HEADER*)buf)->qdcount);
-
- while (qdcount-- > 0) {
- char tname[MAXDNAME+1];
- int n, ttype, tclass;
-
- n = dn_expand(buf, eom, cp, tname, sizeof tname);
- if (n < 0)
- return (-1);
- cp += n;
- if (cp + 2 * INT16SZ > eom)
- return (-1);
- NS_GET16(ttype, cp);
- NS_GET16(tclass, cp);
- if (ttype == type && tclass == class &&
- ns_samename(tname, name) == 1)
- return (1);
- }
- return (0);
-}
-libresolv_hidden_def (res_nameinquery)
-
-/* int
- * res_queriesmatch(buf1, eom1, buf2, eom2)
- * is there a 1:1 mapping of (name,type,class)
- * in (buf1,eom1) and (buf2,eom2)?
- * returns:
- * -1 : format error
- * 0 : not a 1:1 mapping
- * >0 : is a 1:1 mapping
- * author:
- * paul vixie, 29may94
- */
-int
-res_queriesmatch(const u_char *buf1, const u_char *eom1,
- const u_char *buf2, const u_char *eom2)
-{
- if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2)
- return (-1);
-
- /*
- * Only header section present in replies to
- * dynamic update packets.
- */
- if ((((HEADER *)buf1)->opcode == ns_o_update) &&
- (((HEADER *)buf2)->opcode == ns_o_update))
- return (1);
-
- /* Note that we initially do not convert QDCOUNT to the host byte
- order. We can compare it with the second buffer's QDCOUNT
- value without doing this. */
- int qdcount = ((HEADER*)buf1)->qdcount;
- if (qdcount != ((HEADER*)buf2)->qdcount)
- return (0);
-
- qdcount = htons (qdcount);
- const u_char *cp = buf1 + HFIXEDSZ;
-
- while (qdcount-- > 0) {
- char tname[MAXDNAME+1];
- int n, ttype, tclass;
-
- n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
- if (n < 0)
- return (-1);
- cp += n;
- if (cp + 2 * INT16SZ > eom1)
- return (-1);
- NS_GET16(ttype, cp);
- NS_GET16(tclass, cp);
- if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
- return (0);
- }
- return (1);
-}
-libresolv_hidden_def (res_queriesmatch)
-
-int
-__libc_res_nsend(res_state statp, const u_char *buf, int buflen,
- const u_char *buf2, int buflen2,
- u_char *ans, int anssiz, u_char **ansp, u_char **ansp2,
- int *nansp2, int *resplen2, int *ansp2_malloced)
-{
- int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
-
- if (statp->nscount == 0) {
- __set_errno (ESRCH);
- return (-1);
- }
-
- if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) {
- __set_errno (EINVAL);
- return (-1);
- }
-
- DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
- (stdout, ";; res_send()\n"), buf, buflen);
- v_circuit = ((statp->options & RES_USEVC)
- || buflen > PACKETSZ
- || buflen2 > PACKETSZ);
- gotsomewhere = 0;
- terrno = ETIMEDOUT;
-
- /*
- * If the ns_addr_list in the resolver context has changed, then
- * invalidate our cached copy and the associated timing data.
- */
- if (EXT(statp).nscount != 0) {
- int needclose = 0;
-
- if (EXT(statp).nscount != statp->nscount)
- needclose++;
- else
- for (ns = 0; ns < statp->nscount; ns++) {
- if (statp->nsaddr_list[ns].sin_family != 0
- && !sock_eq((struct sockaddr_in6 *)
- &statp->nsaddr_list[ns],
- EXT(statp).nsaddrs[ns]))
- {
- needclose++;
- break;
- }
- }
- if (needclose) {
- __res_iclose(statp, false);
- EXT(statp).nscount = 0;
- }
- }
-
- /*
- * Maybe initialize our private copy of the ns_addr_list.
- */
- if (EXT(statp).nscount == 0) {
- for (ns = 0; ns < statp->nscount; ns++) {
- EXT(statp).nssocks[ns] = -1;
- if (statp->nsaddr_list[ns].sin_family == 0)
- continue;
- if (EXT(statp).nsaddrs[ns] == NULL)
- EXT(statp).nsaddrs[ns] =
- malloc(sizeof (struct sockaddr_in6));
- if (EXT(statp).nsaddrs[ns] != NULL)
- memset (mempcpy(EXT(statp).nsaddrs[ns],
- &statp->nsaddr_list[ns],
- sizeof (struct sockaddr_in)),
- '\0',
- sizeof (struct sockaddr_in6)
- - sizeof (struct sockaddr_in));
- }
- EXT(statp).nscount = statp->nscount;
- }
-
- /*
- * Some resolvers want to even out the load on their nameservers.
- * Note that RES_BLAST overrides RES_ROTATE.
- */
- if (__glibc_unlikely ((statp->options & RES_ROTATE) != 0)) {
- struct sockaddr_in ina;
- struct sockaddr_in6 *inp;
- int lastns = statp->nscount - 1;
- int fd;
-
- inp = EXT(statp).nsaddrs[0];
- ina = statp->nsaddr_list[0];
- fd = EXT(statp).nssocks[0];
- for (ns = 0; ns < lastns; ns++) {
- EXT(statp).nsaddrs[ns] = EXT(statp).nsaddrs[ns + 1];
- statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1];
- EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1];
- }
- EXT(statp).nsaddrs[lastns] = inp;
- statp->nsaddr_list[lastns] = ina;
- EXT(statp).nssocks[lastns] = fd;
- }
-
- /*
- * Send request, RETRY times, or until successful.
- */
- for (try = 0; try < statp->retry; try++) {
- for (ns = 0; ns < statp->nscount; ns++)
- {
-#ifdef DEBUG
- char tmpbuf[40];
- struct sockaddr *nsap = get_nsaddr (statp, ns);
-#endif
-
- same_ns:
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; Querying server (# %d) address = %s\n",
- ns + 1, inet_ntop(nsap->sa_family,
- (nsap->sa_family == AF_INET6
- ? (void *) &((struct sockaddr_in6 *) nsap)->sin6_addr
- : (void *) &((struct sockaddr_in *) nsap)->sin_addr),
- tmpbuf, sizeof (tmpbuf))));
-
- if (__glibc_unlikely (v_circuit)) {
- /* Use VC; at most one attempt per server. */
- try = statp->retry;
- n = send_vc(statp, buf, buflen, buf2, buflen2,
- &ans, &anssiz, &terrno,
- ns, ansp, ansp2, nansp2, resplen2,
- ansp2_malloced);
- if (n < 0)
- return (-1);
- if (n == 0 && (buf2 == NULL || *resplen2 == 0))
- goto next_ns;
- } else {
- /* Use datagrams. */
- n = send_dg(statp, buf, buflen, buf2, buflen2,
- &ans, &anssiz, &terrno,
- ns, &v_circuit, &gotsomewhere, ansp,
- ansp2, nansp2, resplen2, ansp2_malloced);
- if (n < 0)
- return (-1);
- if (n == 0 && (buf2 == NULL || *resplen2 == 0))
- goto next_ns;
- if (v_circuit)
- // XXX Check whether both requests failed or
- // XXX whether one has been answered successfully
- goto same_ns;
- }
-
- resplen = n;
-
- Dprint((statp->options & RES_DEBUG) ||
- ((statp->pfcode & RES_PRF_REPLY) &&
- (statp->pfcode & RES_PRF_HEAD1)),
- (stdout, ";; got answer:\n"));
-
- DprintQ((statp->options & RES_DEBUG) ||
- (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,
- * or if we haven't been asked to keep a socket open,
- * close the socket.
- */
- if ((v_circuit && (statp->options & RES_USEVC) == 0) ||
- (statp->options & RES_STAYOPEN) == 0) {
- __res_iclose(statp, false);
- }
- return (resplen);
- next_ns: ;
- } /*foreach ns*/
- } /*foreach retry*/
- __res_iclose(statp, false);
- if (!v_circuit) {
- if (!gotsomewhere)
- __set_errno (ECONNREFUSED); /* no nameservers found */
- else
- __set_errno (ETIMEDOUT); /* no answer obtained */
- } else
- __set_errno (terrno);
- return (-1);
-}
-
-int
-res_nsend(res_state statp,
- const u_char *buf, int buflen, u_char *ans, int anssiz)
-{
- return __libc_res_nsend(statp, buf, buflen, NULL, 0, ans, anssiz,
- NULL, NULL, NULL, NULL, NULL);
-}
-libresolv_hidden_def (res_nsend)
-
-/* Private */
-
-static struct sockaddr *
-get_nsaddr (res_state statp, int n)
-{
-
- if (statp->nsaddr_list[n].sin_family == 0 && EXT(statp).nsaddrs[n] != NULL)
- /* EXT(statp).nsaddrs[n] holds an address that is larger than
- struct sockaddr, and user code did not update
- statp->nsaddr_list[n]. */
- return (struct sockaddr *) EXT(statp).nsaddrs[n];
- else
- /* User code updated statp->nsaddr_list[n], or statp->nsaddr_list[n]
- has the same content as EXT(statp).nsaddrs[n]. */
- return (struct sockaddr *) (void *) &statp->nsaddr_list[n];
-}
-
-/* Close the resolver structure, assign zero to *RESPLEN2 if RESPLEN2
- is not NULL, and return zero. */
-static int
-__attribute__ ((warn_unused_result))
-close_and_return_error (res_state statp, int *resplen2)
-{
- __res_iclose(statp, false);
- if (resplen2 != NULL)
- *resplen2 = 0;
- return 0;
-}
-
-/* The send_vc function is responsible for sending a DNS query over TCP
- to the nameserver numbered NS from the res_state STATP i.e.
- EXT(statp).nssocks[ns]. The function supports sending both IPv4 and
- IPv6 queries at the same serially on the same socket.
-
- Please note that for TCP there is no way to disable sending both
- queries, unlike UDP, which honours RES_SNGLKUP and RES_SNGLKUPREOP
- and sends the queries serially and waits for the result after each
- sent query. This implementation should be corrected to honour these
- options.
-
- Please also note that for TCP we send both queries over the same
- socket one after another. This technically violates best practice
- since the server is allowed to read the first query, respond, and
- then close the socket (to service another client). If the server
- does this, then the remaining second query in the socket data buffer
- will cause the server to send the client an RST which will arrive
- asynchronously and the client's OS will likely tear down the socket
- receive buffer resulting in a potentially short read and lost
- response data. This will force the client to retry the query again,
- and this process may repeat until all servers and connection resets
- are exhausted and then the query will fail. It's not known if this
- happens with any frequency in real DNS server implementations. This
- implementation should be corrected to use two sockets by default for
- parallel queries.
-
- The query stored in BUF of BUFLEN length is sent first followed by
- the query stored in BUF2 of BUFLEN2 length. Queries are sent
- serially on the same socket.
-
- Answers to the query are stored firstly in *ANSP up to a max of
- *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP
- is non-NULL (to indicate that modifying the answer buffer is allowed)
- then malloc is used to allocate a new response buffer and ANSCP and
- ANSP will both point to the new buffer. If more than *ANSSIZP bytes
- are needed but ANSCP is NULL, then as much of the response as
- possible is read into the buffer, but the results will be truncated.
- When truncation happens because of a small answer buffer the DNS
- packets header field TC will bet set to 1, indicating a truncated
- message and the rest of the socket data will be read and discarded.
-
- Answers to the query are stored secondly in *ANSP2 up to a max of
- *ANSSIZP2 bytes, with the actual response length stored in
- *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2
- is non-NULL (required for a second query) then malloc is used to
- allocate a new response buffer, *ANSSIZP2 is set to the new buffer
- size and *ANSP2_MALLOCED is set to 1.
-
- The ANSP2_MALLOCED argument will eventually be removed as the
- change in buffer pointer can be used to detect the buffer has
- changed and that the caller should use free on the new buffer.
-
- Note that the answers may arrive in any order from the server and
- therefore the first and second answer buffers may not correspond to
- the first and second queries.
-
- It is not supported to call this function with a non-NULL ANSP2
- but a NULL ANSCP. Put another way, you can call send_vc with a
- single unmodifiable buffer or two modifiable buffers, but no other
- combination is supported.
-
- It is the caller's responsibility to free the malloc allocated
- buffers by detecting that the pointers have changed from their
- original values i.e. *ANSCP or *ANSP2 has changed.
-
- If errors are encountered then *TERRNO is set to an appropriate
- errno value and a zero result is returned for a recoverable error,
- and a less-than zero result is returned for a non-recoverable error.
-
- If no errors are encountered then *TERRNO is left unmodified and
- a the length of the first response in bytes is returned. */
-static int
-send_vc(res_state statp,
- 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, int *ansp2_malloced)
-{
- const HEADER *hp = (HEADER *) buf;
- const HEADER *hp2 = (HEADER *) buf2;
- HEADER *anhp = (HEADER *) *ansp;
- struct sockaddr *nsap = get_nsaddr (statp, ns);
- int truncating, connreset, n;
- /* On some architectures compiler might emit a warning indicating
- 'resplen' may be used uninitialized. However if buf2 == NULL
- then this code won't be executed; if buf2 != NULL, then first
- time round the loop recvresp1 and recvresp2 will be 0 so this
- code won't be executed but "thisresplenp = &resplen;" followed
- by "*thisresplenp = rlen;" will be executed so that subsequent
- times round the loop resplen has been initialized. So this is
- a false-positive.
- */
- DIAG_PUSH_NEEDS_COMMENT;
- DIAG_IGNORE_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
- int resplen;
- DIAG_POP_NEEDS_COMMENT;
- struct iovec iov[4];
- u_short len;
- u_short len2;
- u_char *cp;
-
- connreset = 0;
- same_ns:
- truncating = 0;
-
- /* Are we still talking to whom we want to talk to? */
- if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
- struct sockaddr_in6 peer;
- socklen_t size = sizeof peer;
-
- if (getpeername(statp->_vcsock,
- (struct sockaddr *)&peer, &size) < 0 ||
- !sock_eq(&peer, (struct sockaddr_in6 *) nsap)) {
- __res_iclose(statp, false);
- statp->_flags &= ~RES_F_VC;
- }
- }
-
- if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
- if (statp->_vcsock >= 0)
- __res_iclose(statp, false);
-
- statp->_vcsock = socket
- (nsap->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
- if (statp->_vcsock < 0) {
- *terrno = errno;
- Perror(statp, stderr, "socket(vc)", errno);
- if (resplen2 != NULL)
- *resplen2 = 0;
- return (-1);
- }
- __set_errno (0);
- if (connect(statp->_vcsock, nsap,
- nsap->sa_family == AF_INET
- ? sizeof (struct sockaddr_in)
- : sizeof (struct sockaddr_in6)) < 0) {
- *terrno = errno;
- Aerror(statp, stderr, "connect/vc", errno, nsap);
- return close_and_return_error (statp, resplen2);
- }
- statp->_flags |= RES_F_VC;
- }
-
- /*
- * Send length & message
- */
- len = htons ((u_short) buflen);
- evConsIovec(&len, INT16SZ, &iov[0]);
- evConsIovec((void*)buf, buflen, &iov[1]);
- 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);
- return close_and_return_error (statp, resplen2);
- }
- /*
- * Receive length & response
- */
- int recvresp1 = 0;
- /* Skip the second response if there is no second query.
- To do that we mark the second response as received. */
- int recvresp2 = buf2 == NULL;
- uint16_t rlen16;
- read_len:
- cp = (u_char *)&rlen16;
- len = sizeof(rlen16);
- while ((n = TEMP_FAILURE_RETRY (read(statp->_vcsock, cp,
- (int)len))) > 0) {
- cp += n;
- if ((len -= n) <= 0)
- break;
- }
- if (n <= 0) {
- *terrno = errno;
- Perror(statp, stderr, "read failed", errno);
- /*
- * A long running process might get its TCP
- * connection reset if the remote server was
- * restarted. Requery the server instead of
- * trying a new one. When there is only one
- * server, this means that a query might work
- * instead of failing. We only allow one reset
- * per query to prevent looping.
- */
- if (*terrno == ECONNRESET && !connreset)
- {
- __res_iclose (statp, false);
- connreset = 1;
- goto same_ns;
- }
- return close_and_return_error (statp, resplen2);
- }
- int rlen = ntohs (rlen16);
-
- int *thisanssizp;
- u_char **thisansp;
- int *thisresplenp;
- if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
- /* We have not received any responses
- yet or we only have one response to
- receive. */
- thisanssizp = anssizp;
- thisansp = anscp ?: ansp;
- assert (anscp != NULL || ansp2 == NULL);
- thisresplenp = &resplen;
- } else {
- thisanssizp = anssizp2;
- thisansp = ansp2;
- thisresplenp = resplen2;
- }
- anhp = (HEADER *) *thisansp;
-
- *thisresplenp = rlen;
- /* Is the answer buffer too small? */
- if (*thisanssizp < rlen) {
- /* If the current buffer is not the the static
- user-supplied buffer then we can reallocate
- it. */
- if (thisansp != NULL && thisansp != ansp) {
- /* Always allocate MAXPACKET, callers expect
- this specific size. */
- u_char *newp = malloc (MAXPACKET);
- if (newp == NULL)
- {
- *terrno = ENOMEM;
- return close_and_return_error (statp, resplen2);
- }
- *thisanssizp = MAXPACKET;
- *thisansp = newp;
- if (thisansp == ansp2)
- *ansp2_malloced = 1;
- anhp = (HEADER *) newp;
- /* A uint16_t can't be larger than MAXPACKET
- thus it's safe to allocate MAXPACKET but
- read RLEN bytes instead. */
- len = rlen;
- } else {
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; response truncated\n")
- );
- truncating = 1;
- len = *thisanssizp;
- }
- } else
- len = rlen;
-
- if (__glibc_unlikely (len < HFIXEDSZ)) {
- /*
- * Undersized message.
- */
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; undersized: %d\n", len));
- *terrno = EMSGSIZE;
- return close_and_return_error (statp, resplen2);
- }
-
- cp = *thisansp;
- while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
- cp += n;
- len -= n;
- }
- if (__glibc_unlikely (n <= 0)) {
- *terrno = errno;
- Perror(statp, stderr, "read(vc)", errno);
- return close_and_return_error (statp, resplen2);
- }
- if (__glibc_unlikely (truncating)) {
- /*
- * Flush rest of answer so connection stays in synch.
- */
- anhp->tc = 1;
- len = rlen - *thisanssizp;
- while (len != 0) {
- char junk[PACKETSZ];
-
- n = read(statp->_vcsock, junk,
- (len > sizeof junk) ? sizeof junk : len);
- if (n > 0)
- len -= n;
- else
- break;
- }
- }
- /*
- * If the calling application has bailed out of
- * a previous call and failed to arrange to have
- * the circuit closed or the server has got
- * itself confused, then drop the packet and
- * wait for the correct one.
- */
- 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"),
- *thisansp,
- (rlen > *thisanssizp) ? *thisanssizp: rlen);
- 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.
- */
- return resplen;
-}
-
-static int
-reopen (res_state statp, int *terrno, int ns)
-{
- if (EXT(statp).nssocks[ns] == -1) {
- struct sockaddr *nsap = get_nsaddr (statp, ns);
- socklen_t slen;
-
- /* only try IPv6 if IPv6 NS and if not failed before */
- if (nsap->sa_family == AF_INET6 && !statp->ipv6_unavail) {
- EXT(statp).nssocks[ns] = socket
- (PF_INET6,
- SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
- if (EXT(statp).nssocks[ns] < 0)
- statp->ipv6_unavail = errno == EAFNOSUPPORT;
- slen = sizeof (struct sockaddr_in6);
- } else if (nsap->sa_family == AF_INET) {
- EXT(statp).nssocks[ns] = socket
- (PF_INET,
- SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
- slen = sizeof (struct sockaddr_in);
- }
- if (EXT(statp).nssocks[ns] < 0) {
- *terrno = errno;
- Perror(statp, stderr, "socket(dg)", errno);
- return (-1);
- }
-
- /*
- * On a 4.3BSD+ machine (client and server,
- * actually), sending to a nameserver datagram
- * port with no nameserver will cause an
- * ICMP port unreachable message to be returned.
- * If our datagram socket is "connected" to the
- * server, we get an ECONNREFUSED error on the next
- * socket operation, and select returns if the
- * error message is received. We can thus detect
- * the absence of a nameserver without timing out.
- */
- /* With GCC 5.3 when compiling with -Os the compiler
- emits a warning that slen may be used uninitialized,
- but that is never true. Both slen and
- EXT(statp).nssocks[ns] are initialized together or
- the function return -1 before control flow reaches
- the call to connect with slen. */
- DIAG_PUSH_NEEDS_COMMENT;
- DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
- if (connect(EXT(statp).nssocks[ns], nsap, slen) < 0) {
- DIAG_POP_NEEDS_COMMENT;
- Aerror(statp, stderr, "connect(dg)", errno, nsap);
- __res_iclose(statp, false);
- return (0);
- }
- }
-
- return 1;
-}
-
-/* The send_dg function is responsible for sending a DNS query over UDP
- to the nameserver numbered NS from the res_state STATP i.e.
- EXT(statp).nssocks[ns]. The function supports IPv4 and IPv6 queries
- along with the ability to send the query in parallel for both stacks
- (default) or serially (RES_SINGLKUP). It also supports serial lookup
- with a close and reopen of the socket used to talk to the server
- (RES_SNGLKUPREOP) to work around broken name servers.
-
- The query stored in BUF of BUFLEN length is sent first followed by
- the query stored in BUF2 of BUFLEN2 length. Queries are sent
- in parallel (default) or serially (RES_SINGLKUP or RES_SNGLKUPREOP).
-
- Answers to the query are stored firstly in *ANSP up to a max of
- *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP
- is non-NULL (to indicate that modifying the answer buffer is allowed)
- then malloc is used to allocate a new response buffer and ANSCP and
- ANSP will both point to the new buffer. If more than *ANSSIZP bytes
- are needed but ANSCP is NULL, then as much of the response as
- possible is read into the buffer, but the results will be truncated.
- When truncation happens because of a small answer buffer the DNS
- packets header field TC will bet set to 1, indicating a truncated
- message, while the rest of the UDP packet is discarded.
-
- Answers to the query are stored secondly in *ANSP2 up to a max of
- *ANSSIZP2 bytes, with the actual response length stored in
- *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2
- is non-NULL (required for a second query) then malloc is used to
- allocate a new response buffer, *ANSSIZP2 is set to the new buffer
- size and *ANSP2_MALLOCED is set to 1.
-
- The ANSP2_MALLOCED argument will eventually be removed as the
- change in buffer pointer can be used to detect the buffer has
- changed and that the caller should use free on the new buffer.
-
- Note that the answers may arrive in any order from the server and
- therefore the first and second answer buffers may not correspond to
- the first and second queries.
-
- It is not supported to call this function with a non-NULL ANSP2
- but a NULL ANSCP. Put another way, you can call send_vc with a
- single unmodifiable buffer or two modifiable buffers, but no other
- combination is supported.
-
- It is the caller's responsibility to free the malloc allocated
- buffers by detecting that the pointers have changed from their
- original values i.e. *ANSCP or *ANSP2 has changed.
-
- If an answer is truncated because of UDP datagram DNS limits then
- *V_CIRCUIT is set to 1 and the return value non-zero to indicate to
- the caller to retry with TCP. The value *GOTSOMEWHERE is set to 1
- if any progress was made reading a response from the nameserver and
- is used by the caller to distinguish between ECONNREFUSED and
- ETIMEDOUT (the latter if *GOTSOMEWHERE is 1).
-
- If errors are encountered then *TERRNO is set to an appropriate
- errno value and a zero result is returned for a recoverable error,
- and a less-than zero result is returned for a non-recoverable error.
-
- If no errors are encountered then *TERRNO is left unmodified and
- a the length of the first response in bytes is returned. */
-static int
-send_dg(res_state statp,
- 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, int *ansp2_malloced)
-{
- const HEADER *hp = (HEADER *) buf;
- const HEADER *hp2 = (HEADER *) buf2;
- struct timespec now, timeout, finish;
- struct pollfd pfd[1];
- int ptimeout;
- struct sockaddr_in6 from;
- int resplen = 0;
- int n;
-
- /*
- * Compute time for the total operation.
- */
- int seconds = (statp->retrans << ns);
- if (ns > 0)
- seconds /= statp->nscount;
- if (seconds <= 0)
- seconds = 1;
- bool single_request_reopen = (statp->options & RES_SNGLKUPREOP) != 0;
- bool single_request = (((statp->options & RES_SNGLKUP) != 0)
- | single_request_reopen);
- int save_gotsomewhere = *gotsomewhere;
-
- int retval;
- retry_reopen:
- retval = reopen (statp, terrno, ns);
- if (retval <= 0)
- {
- if (resplen2 != NULL)
- *resplen2 = 0;
- return retval;
- }
- retry:
- evNowTime(&now);
- evConsTime(&timeout, seconds, 0);
- evAddTime(&finish, &now, &timeout);
- int need_recompute = 0;
- int nwritten = 0;
- int recvresp1 = 0;
- /* Skip the second response if there is no second query.
- To do that we mark the second response as received. */
- int recvresp2 = buf2 == NULL;
- pfd[0].fd = EXT(statp).nssocks[ns];
- pfd[0].events = POLLOUT;
- wait:
- if (need_recompute) {
- recompute_resend:
- evNowTime(&now);
- if (evCmpTime(finish, now) <= 0) {
- poll_err_out:
- Perror(statp, stderr, "poll", errno);
- return close_and_return_error (statp, resplen2);
- }
- evSubTime(&timeout, &finish, &now);
- need_recompute = 0;
- }
- /* Convert struct timespec in milliseconds. */
- ptimeout = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000;
-
- n = 0;
- if (nwritten == 0)
- n = __poll (pfd, 1, 0);
- if (__glibc_unlikely (n == 0)) {
- n = __poll (pfd, 1, ptimeout);
- need_recompute = 1;
- }
- if (n == 0) {
- Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
- if (resplen > 1 && (recvresp1 || (buf2 != NULL && recvresp2)))
- {
- /* There are quite a few broken name servers out
- there which don't handle two outstanding
- requests from the same source. There are also
- broken firewall settings. If we time out after
- having received one answer switch to the mode
- where we send the second request only once we
- have received the first answer. */
- if (!single_request)
- {
- statp->options |= RES_SNGLKUP;
- single_request = true;
- *gotsomewhere = save_gotsomewhere;
- goto retry;
- }
- else if (!single_request_reopen)
- {
- statp->options |= RES_SNGLKUPREOP;
- single_request_reopen = true;
- *gotsomewhere = save_gotsomewhere;
- __res_iclose (statp, false);
- goto retry_reopen;
- }
-
- *resplen2 = 1;
- return resplen;
- }
-
- *gotsomewhere = 1;
- if (resplen2 != NULL)
- *resplen2 = 0;
- return 0;
- }
- if (n < 0) {
- if (errno == EINTR)
- goto recompute_resend;
-
- goto poll_err_out;
- }
- __set_errno (0);
- if (pfd[0].revents & POLLOUT) {
-#ifndef __ASSUME_SENDMMSG
- static int have_sendmmsg;
-#else
-# define have_sendmmsg 1
-#endif
- if (have_sendmmsg >= 0 && nwritten == 0 && buf2 != NULL
- && !single_request)
- {
- struct iovec iov[2];
- struct mmsghdr reqs[2];
- reqs[0].msg_hdr.msg_name = NULL;
- reqs[0].msg_hdr.msg_namelen = 0;
- reqs[0].msg_hdr.msg_iov = &iov[0];
- reqs[0].msg_hdr.msg_iovlen = 1;
- iov[0].iov_base = (void *) buf;
- iov[0].iov_len = buflen;
- reqs[0].msg_hdr.msg_control = NULL;
- reqs[0].msg_hdr.msg_controllen = 0;
-
- reqs[1].msg_hdr.msg_name = NULL;
- reqs[1].msg_hdr.msg_namelen = 0;
- reqs[1].msg_hdr.msg_iov = &iov[1];
- reqs[1].msg_hdr.msg_iovlen = 1;
- iov[1].iov_base = (void *) buf2;
- iov[1].iov_len = buflen2;
- reqs[1].msg_hdr.msg_control = NULL;
- reqs[1].msg_hdr.msg_controllen = 0;
-
- int ndg = __sendmmsg (pfd[0].fd, reqs, 2, MSG_NOSIGNAL);
- if (__glibc_likely (ndg == 2))
- {
- if (reqs[0].msg_len != buflen
- || reqs[1].msg_len != buflen2)
- goto fail_sendmmsg;
-
- pfd[0].events = POLLIN;
- nwritten += 2;
- }
- else if (ndg == 1 && reqs[0].msg_len == buflen)
- goto just_one;
- else if (ndg < 0 && (errno == EINTR || errno == EAGAIN))
- goto recompute_resend;
- else
- {
-#ifndef __ASSUME_SENDMMSG
- if (__glibc_unlikely (have_sendmmsg == 0))
- {
- if (ndg < 0 && errno == ENOSYS)
- {
- have_sendmmsg = -1;
- goto try_send;
- }
- have_sendmmsg = 1;
- }
-#endif
-
- fail_sendmmsg:
- Perror(statp, stderr, "sendmmsg", errno);
- return close_and_return_error (statp, resplen2);
- }
- }
- else
- {
- ssize_t sr;
-#ifndef __ASSUME_SENDMMSG
- try_send:
-#endif
- if (nwritten != 0)
- sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL);
- else
- sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);
-
- if (sr != (nwritten != 0 ? buflen2 : buflen)) {
- if (errno == EINTR || errno == EAGAIN)
- goto recompute_resend;
- Perror(statp, stderr, "send", errno);
- return close_and_return_error (statp, resplen2);
- }
- just_one:
- if (nwritten != 0 || buf2 == NULL || single_request)
- pfd[0].events = POLLIN;
- else
- pfd[0].events = POLLIN | POLLOUT;
- ++nwritten;
- }
- goto wait;
- } else if (pfd[0].revents & POLLIN) {
- int *thisanssizp;
- u_char **thisansp;
- int *thisresplenp;
-
- if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
- /* We have not received any responses
- yet or we only have one response to
- receive. */
- thisanssizp = anssizp;
- thisansp = anscp ?: ansp;
- assert (anscp != NULL || ansp2 == NULL);
- thisresplenp = &resplen;
- } else {
- thisanssizp = anssizp2;
- thisansp = ansp2;
- thisresplenp = resplen2;
- }
-
- if (*thisanssizp < MAXPACKET
- /* If the current buffer is not the the static
- user-supplied buffer then we can reallocate
- it. */
- && (thisansp != NULL && thisansp != ansp)
-#ifdef FIONREAD
- /* Is the size too small? */
- && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
- || *thisanssizp < *thisresplenp)
-#endif
- ) {
- /* Always allocate MAXPACKET, callers expect
- this specific size. */
- u_char *newp = malloc (MAXPACKET);
- if (newp != NULL) {
- *thisanssizp = MAXPACKET;
- *thisansp = newp;
- if (thisansp == ansp2)
- *ansp2_malloced = 1;
- }
- }
- /* We could end up with truncation if anscp was NULL
- (not allowed to change caller's buffer) and the
- response buffer size is too small. This isn't a
- reliable way to detect truncation because the ioctl
- may be an inaccurate report of the UDP message size.
- Therefore we use this only to issue debug output.
- To do truncation accurately with UDP we need
- MSG_TRUNC which is only available on Linux. We
- can abstract out the Linux-specific feature in the
- future to detect truncation. */
- if (__glibc_unlikely (*thisanssizp < *thisresplenp)) {
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; response may be truncated (UDP)\n")
- );
- }
-
- 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 (__glibc_unlikely (*thisresplenp <= 0)) {
- if (errno == EINTR || errno == EAGAIN) {
- need_recompute = 1;
- goto wait;
- }
- Perror(statp, stderr, "recvfrom", errno);
- return close_and_return_error (statp, resplen2);
- }
- *gotsomewhere = 1;
- if (__glibc_unlikely (*thisresplenp < HFIXEDSZ)) {
- /*
- * Undersized message.
- */
- Dprint(statp->options & RES_DEBUG,
- (stdout, ";; undersized: %d\n",
- *thisresplenp));
- *terrno = EMSGSIZE;
- return close_and_return_error (statp, resplen2);
- }
- if ((recvresp1 || hp->id != anhp->id)
- && (recvresp2 || hp2->id != anhp->id)) {
- /*
- * response from old query, ignore it.
- * XXX - potential security hazard could
- * be detected here.
- */
- DprintQ((statp->options & RES_DEBUG) ||
- (statp->pfcode & RES_PRF_REPLY),
- (stdout, ";; old answer:\n"),
- *thisansp,
- (*thisresplenp > *thisanssizp)
- ? *thisanssizp : *thisresplenp);
- goto wait;
- }
- if (!(statp->options & RES_INSECURE1) &&
- !res_ourserver_p(statp, &from)) {
- /*
- * response from wrong server? ignore it.
- * XXX - potential security hazard could
- * be detected here.
- */
- DprintQ((statp->options & RES_DEBUG) ||
- (statp->pfcode & RES_PRF_REPLY),
- (stdout, ";; not our server:\n"),
- *thisansp,
- (*thisresplenp > *thisanssizp)
- ? *thisanssizp : *thisresplenp);
- goto wait;
- }
- 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
- * be detected here.
- */
- DprintQ((statp->options & RES_DEBUG) ||
- (statp->pfcode & RES_PRF_REPLY),
- (stdout, ";; wrong query name:\n"),
- *thisansp,
- (*thisresplenp > *thisanssizp)
- ? *thisanssizp : *thisresplenp);
- goto wait;
- }
- if (anhp->rcode == SERVFAIL ||
- anhp->rcode == NOTIMP ||
- anhp->rcode == REFUSED) {
- DprintQ(statp->options & RES_DEBUG,
- (stdout, "server rejected query:\n"),
- *thisansp,
- (*thisresplenp > *thisanssizp)
- ? *thisanssizp : *thisresplenp);
-
- next_ns:
- if (recvresp1 || (buf2 != NULL && recvresp2)) {
- *resplen2 = 0;
- return resplen;
- }
- if (buf2 != NULL)
- {
- /* No data from the first reply. */
- resplen = 0;
- /* We are waiting for a possible second reply. */
- if (hp->id == anhp->id)
- recvresp1 = 1;
- else
- recvresp2 = 1;
-
- goto wait;
- }
-
- /* don't retry if called from dig */
- if (!statp->pfcode)
- return close_and_return_error (statp, resplen2);
- __res_iclose(statp, false);
- }
- if (anhp->rcode == NOERROR && anhp->ancount == 0
- && anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) {
- DprintQ(statp->options & RES_DEBUG,
- (stdout, "referred query:\n"),
- *thisansp,
- (*thisresplenp > *thisanssizp)
- ? *thisanssizp : *thisresplenp);
- goto next_ns;
- }
- if (!(statp->options & RES_IGNTC) && anhp->tc) {
- /*
- * To get the rest of answer,
- * use TCP with same server.
- */
- Dprint(statp->options & RES_DEBUG,
- (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...
- if (resplen2 != NULL)
- *resplen2 = 0;
- 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) {
- if (single_request) {
- pfd[0].events = POLLOUT;
- if (single_request_reopen) {
- __res_iclose (statp, false);
- retval = reopen (statp, terrno, ns);
- if (retval <= 0)
- {
- if (resplen2 != NULL)
- *resplen2 = 0;
- return retval;
- }
- pfd[0].fd = EXT(statp).nssocks[ns];
- }
- }
- goto wait;
- }
- /* All is well. We have received both responses (if
- two responses were requested). */
- return (resplen);
- } else if (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))
- /* Something went wrong. We can stop trying. */
- return close_and_return_error (statp, resplen2);
- else {
- /* poll should not have returned > 0 in this case. */
- abort ();
- }
-}
-
-#ifdef DEBUG
-static void
-Aerror(const res_state statp, FILE *file, const char *string, int error,
- const struct sockaddr *address)
-{
- int save = errno;
-
- if ((statp->options & RES_DEBUG) != 0) {
- char tmp[sizeof "xxxx.xxxx.xxxx.255.255.255.255"];
-
- fprintf(file, "res_send: %s ([%s].%u): %s\n",
- string,
- (address->sa_family == AF_INET
- ? inet_ntop(address->sa_family,
- &((const struct sockaddr_in *) address)->sin_addr,
- tmp, sizeof tmp)
- : inet_ntop(address->sa_family,
- &((const struct sockaddr_in6 *) address)->sin6_addr,
- tmp, sizeof tmp)),
- (address->sa_family == AF_INET
- ? ntohs(((struct sockaddr_in *) address)->sin_port)
- : address->sa_family == AF_INET6
- ? ntohs(((struct sockaddr_in6 *) address)->sin6_port)
- : 0),
- strerror(error));
- }
- __set_errno (save);
-}
-
-static void
-Perror(const res_state statp, FILE *file, const char *string, int error) {
- int save = errno;
-
- if ((statp->options & RES_DEBUG) != 0)
- fprintf(file, "res_send: %s: %s\n",
- string, strerror(error));
- __set_errno (save);
-}
-#endif
-
-static int
-sock_eq(struct sockaddr_in6 *a1, struct sockaddr_in6 *a2) {
- if (a1->sin6_family == a2->sin6_family) {
- if (a1->sin6_family == AF_INET)
- return ((((struct sockaddr_in *)a1)->sin_port ==
- ((struct sockaddr_in *)a2)->sin_port) &&
- (((struct sockaddr_in *)a1)->sin_addr.s_addr ==
- ((struct sockaddr_in *)a2)->sin_addr.s_addr));
- else
- return ((a1->sin6_port == a2->sin6_port) &&
- !memcmp(&a1->sin6_addr, &a2->sin6_addr,
- sizeof (struct in6_addr)));
- }
- if (a1->sin6_family == AF_INET) {
- struct sockaddr_in6 *sap = a1;
- a1 = a2;
- a2 = sap;
- } /* assumes that AF_INET and AF_INET6 are the only possibilities */
- return ((a1->sin6_port == ((struct sockaddr_in *)a2)->sin_port) &&
- IN6_IS_ADDR_V4MAPPED(&a1->sin6_addr) &&
- (a1->sin6_addr.s6_addr32[3] ==
- ((struct sockaddr_in *)a2)->sin_addr.s_addr));
-}