summaryrefslogtreecommitdiff
path: root/REORG.TODO/resolv/res_send.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/resolv/res_send.c')
-rw-r--r--REORG.TODO/resolv/res_send.c1502
1 files changed, 1502 insertions, 0 deletions
diff --git a/REORG.TODO/resolv/res_send.c b/REORG.TODO/resolv/res_send.c
new file mode 100644
index 0000000000..b7b8ecdfc4
--- /dev/null
+++ b/REORG.TODO/resolv/res_send.c
@@ -0,0 +1,1502 @@
+/* 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));
+}