Blame resolv/res_send.c

Packit 6c4009
/* Copyright (C) 2016-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Copyright (c) 1985, 1989, 1993
Packit 6c4009
 *    The Regents of the University of California.  All rights reserved.
Packit 6c4009
 *
Packit 6c4009
 * Redistribution and use in source and binary forms, with or without
Packit 6c4009
 * modification, are permitted provided that the following conditions
Packit 6c4009
 * are met:
Packit 6c4009
 * 1. Redistributions of source code must retain the above copyright
Packit 6c4009
 *    notice, this list of conditions and the following disclaimer.
Packit 6c4009
 * 2. Redistributions in binary form must reproduce the above copyright
Packit 6c4009
 *    notice, this list of conditions and the following disclaimer in the
Packit 6c4009
 *    documentation and/or other materials provided with the distribution.
Packit 6c4009
 * 4. Neither the name of the University nor the names of its contributors
Packit 6c4009
 *    may be used to endorse or promote products derived from this software
Packit 6c4009
 *    without specific prior written permission.
Packit 6c4009
 *
Packit 6c4009
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
Packit 6c4009
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
Packit 6c4009
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit 6c4009
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
Packit 6c4009
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
Packit 6c4009
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
Packit 6c4009
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
Packit 6c4009
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
Packit 6c4009
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
Packit 6c4009
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
Packit 6c4009
 * SUCH DAMAGE.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
Packit 6c4009
 *
Packit 6c4009
 * Permission to use, copy, modify, and distribute this software for any
Packit 6c4009
 * purpose with or without fee is hereby granted, provided that the above
Packit 6c4009
 * copyright notice and this permission notice appear in all copies, and that
Packit 6c4009
 * the name of Digital Equipment Corporation not be used in advertising or
Packit 6c4009
 * publicity pertaining to distribution of the document or software without
Packit 6c4009
 * specific, written prior permission.
Packit 6c4009
 *
Packit 6c4009
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
Packit 6c4009
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
Packit 6c4009
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
Packit 6c4009
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
Packit 6c4009
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
Packit 6c4009
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
Packit 6c4009
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
Packit 6c4009
 * SOFTWARE.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
Packit 6c4009
 *
Packit 6c4009
 * Permission to use, copy, modify, and distribute this software for any
Packit 6c4009
 * purpose with or without fee is hereby granted, provided that the above
Packit 6c4009
 * copyright notice and this permission notice appear in all copies.
Packit 6c4009
 *
Packit 6c4009
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
Packit 6c4009
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
Packit 6c4009
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
Packit 6c4009
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
Packit 6c4009
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
Packit 6c4009
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
Packit 6c4009
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
Packit 6c4009
 * SOFTWARE.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Send query to name server and wait for reply.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/time.h>
Packit 6c4009
#include <sys/socket.h>
Packit 6c4009
#include <sys/uio.h>
Packit 6c4009
#include <sys/poll.h>
Packit 6c4009
Packit 6c4009
#include <netinet/in.h>
Packit 6c4009
#include <arpa/nameser.h>
Packit 6c4009
#include <arpa/inet.h>
Packit 6c4009
#include <sys/ioctl.h>
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <netdb.h>
Packit 6c4009
#include <resolv/resolv-internal.h>
Packit 6c4009
#include <resolv/resolv_context.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <kernel-features.h>
Packit 6c4009
#include <libc-diag.h>
Packit 6c4009
#include <hp-timing.h>
Packit 6c4009
Packit 6c4009
#if PACKETSZ > 65536
Packit 6c4009
#define MAXPACKET       PACKETSZ
Packit 6c4009
#else
Packit 6c4009
#define MAXPACKET       65536
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* From ev_streams.c.  */
Packit 6c4009
Packit 6c4009
static inline void
Packit 6c4009
__attribute ((always_inline))
Packit 6c4009
evConsIovec(void *buf, size_t cnt, struct iovec *vec) {
Packit 6c4009
	memset(vec, 0xf5, sizeof (*vec));
Packit 6c4009
	vec->iov_base = buf;
Packit 6c4009
	vec->iov_len = cnt;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* From ev_timers.c.  */
Packit 6c4009
Packit 6c4009
#define BILLION 1000000000
Packit 6c4009
Packit 6c4009
static inline void
Packit 6c4009
evConsTime(struct timespec *res, time_t sec, long nsec) {
Packit 6c4009
	res->tv_sec = sec;
Packit 6c4009
	res->tv_nsec = nsec;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline void
Packit 6c4009
evAddTime(struct timespec *res, const struct timespec *addend1,
Packit 6c4009
	  const struct timespec *addend2) {
Packit 6c4009
	res->tv_sec = addend1->tv_sec + addend2->tv_sec;
Packit 6c4009
	res->tv_nsec = addend1->tv_nsec + addend2->tv_nsec;
Packit 6c4009
	if (res->tv_nsec >= BILLION) {
Packit 6c4009
		res->tv_sec++;
Packit 6c4009
		res->tv_nsec -= BILLION;
Packit 6c4009
	}
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static inline void
Packit 6c4009
evSubTime(struct timespec *res, const struct timespec *minuend,
Packit 6c4009
	  const struct timespec *subtrahend) {
Packit 6c4009
       res->tv_sec = minuend->tv_sec - subtrahend->tv_sec;
Packit 6c4009
	if (minuend->tv_nsec >= subtrahend->tv_nsec)
Packit 6c4009
		res->tv_nsec = minuend->tv_nsec - subtrahend->tv_nsec;
Packit 6c4009
	else {
Packit 6c4009
		res->tv_nsec = (BILLION
Packit 6c4009
				- subtrahend->tv_nsec + minuend->tv_nsec);
Packit 6c4009
		res->tv_sec--;
Packit 6c4009
	}
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
evCmpTime(struct timespec a, struct timespec b) {
Packit 6c4009
	long x = a.tv_sec - b.tv_sec;
Packit 6c4009
Packit 6c4009
	if (x == 0L)
Packit 6c4009
		x = a.tv_nsec - b.tv_nsec;
Packit 6c4009
	return (x < 0L ? (-1) : x > 0L ? (1) : (0));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
evNowTime(struct timespec *res) {
Packit 6c4009
	struct timeval now;
Packit 6c4009
Packit 6c4009
	if (gettimeofday(&now, NULL) < 0)
Packit 6c4009
		evConsTime(res, 0, 0);
Packit 6c4009
	else
Packit 6c4009
		TIMEVAL_TO_TIMESPEC (&now, res);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#define EXT(res) ((res)->_u._ext)
Packit 6c4009
Packit 6c4009
/* Forward. */
Packit 6c4009
Packit 6c4009
static struct sockaddr *get_nsaddr (res_state, unsigned int);
Packit 6c4009
static int		send_vc(res_state, const u_char *, int,
Packit 6c4009
				const u_char *, int,
Packit 6c4009
				u_char **, int *, int *, int, u_char **,
Packit 6c4009
				u_char **, int *, int *, int *);
Packit 6c4009
static int		send_dg(res_state, const u_char *, int,
Packit 6c4009
				const u_char *, int,
Packit 6c4009
				u_char **, int *, int *, int,
Packit 6c4009
				int *, int *, u_char **,
Packit 6c4009
				u_char **, int *, int *, int *);
Packit 6c4009
static int		sock_eq(struct sockaddr_in6 *, struct sockaddr_in6 *);
Packit 6c4009
Packit 6c4009
/* Public. */
Packit 6c4009
Packit 6c4009
/* int
Packit 6c4009
 * res_isourserver(ina)
Packit 6c4009
 *	looks up "ina" in _res.ns_addr_list[]
Packit 6c4009
 * returns:
Packit 6c4009
 *	0  : not found
Packit 6c4009
 *	>0 : found
Packit 6c4009
 * author:
Packit 6c4009
 *	paul vixie, 29may94
Packit 6c4009
 */
Packit 6c4009
int
Packit 6c4009
res_ourserver_p(const res_state statp, const struct sockaddr_in6 *inp)
Packit 6c4009
{
Packit 6c4009
	int ns;
Packit 6c4009
Packit 6c4009
	if (inp->sin6_family == AF_INET) {
Packit 6c4009
	    struct sockaddr_in *in4p = (struct sockaddr_in *) inp;
Packit 6c4009
	    in_port_t port = in4p->sin_port;
Packit 6c4009
	    in_addr_t addr = in4p->sin_addr.s_addr;
Packit 6c4009
Packit 6c4009
	    for (ns = 0;  ns < statp->nscount;  ns++) {
Packit 6c4009
		const struct sockaddr_in *srv =
Packit 6c4009
		    (struct sockaddr_in *) get_nsaddr (statp, ns);
Packit 6c4009
Packit 6c4009
		if ((srv->sin_family == AF_INET) &&
Packit 6c4009
		    (srv->sin_port == port) &&
Packit 6c4009
		    (srv->sin_addr.s_addr == INADDR_ANY ||
Packit 6c4009
		     srv->sin_addr.s_addr == addr))
Packit 6c4009
		    return (1);
Packit 6c4009
	    }
Packit 6c4009
	} else if (inp->sin6_family == AF_INET6) {
Packit 6c4009
	    for (ns = 0;  ns < statp->nscount;  ns++) {
Packit 6c4009
		const struct sockaddr_in6 *srv
Packit 6c4009
		  = (struct sockaddr_in6 *) get_nsaddr (statp, ns);
Packit 6c4009
		if ((srv->sin6_family == AF_INET6) &&
Packit 6c4009
		    (srv->sin6_port == inp->sin6_port) &&
Packit 6c4009
		    !(memcmp(&srv->sin6_addr, &in6addr_any,
Packit 6c4009
			     sizeof (struct in6_addr)) &&
Packit 6c4009
		      memcmp(&srv->sin6_addr, &inp->sin6_addr,
Packit 6c4009
			     sizeof (struct in6_addr))))
Packit 6c4009
		    return (1);
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
	return (0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
res_isourserver (const struct sockaddr_in *inp)
Packit 6c4009
{
Packit 6c4009
  return res_ourserver_p (&_res, (const struct sockaddr_in6 *) inp);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* int
Packit 6c4009
 * res_nameinquery(name, type, class, buf, eom)
Packit 6c4009
 *	look for (name,type,class) in the query section of packet (buf,eom)
Packit 6c4009
 * requires:
Packit 6c4009
 *	buf + HFIXEDSZ <= eom
Packit 6c4009
 * returns:
Packit 6c4009
 *	-1 : format error
Packit 6c4009
 *	0  : not found
Packit 6c4009
 *	>0 : found
Packit 6c4009
 * author:
Packit 6c4009
 *	paul vixie, 29may94
Packit 6c4009
 */
Packit 6c4009
int
Packit 6c4009
res_nameinquery(const char *name, int type, int class,
Packit 6c4009
		const u_char *buf, const u_char *eom)
Packit 6c4009
{
Packit 6c4009
	const u_char *cp = buf + HFIXEDSZ;
Packit 6c4009
	int qdcount = ntohs(((HEADER*)buf)->qdcount);
Packit 6c4009
Packit 6c4009
	while (qdcount-- > 0) {
Packit 6c4009
		char tname[MAXDNAME+1];
Packit 6c4009
		int n, ttype, tclass;
Packit 6c4009
Packit 6c4009
		n = dn_expand(buf, eom, cp, tname, sizeof tname);
Packit 6c4009
		if (n < 0)
Packit 6c4009
			return (-1);
Packit 6c4009
		cp += n;
Packit 6c4009
		if (cp + 2 * INT16SZ > eom)
Packit 6c4009
			return (-1);
Packit 6c4009
		NS_GET16(ttype, cp);
Packit 6c4009
		NS_GET16(tclass, cp);
Packit 6c4009
		if (ttype == type && tclass == class &&
Packit 6c4009
		    ns_samename(tname, name) == 1)
Packit 6c4009
			return (1);
Packit 6c4009
	}
Packit 6c4009
	return (0);
Packit 6c4009
}
Packit 6c4009
libresolv_hidden_def (res_nameinquery)
Packit 6c4009
Packit 6c4009
/* Returns a shift value for the name server index.  Used to implement
Packit 6c4009
   RES_ROTATE.  */
Packit 6c4009
static unsigned int
Packit 6c4009
nameserver_offset (struct __res_state *statp)
Packit 6c4009
{
Packit 6c4009
  /* If we only have one name server or rotation is disabled, return
Packit 6c4009
     offset 0 (no rotation).  */
Packit 6c4009
  unsigned int nscount = statp->nscount;
Packit 6c4009
  if (nscount <= 1 || !(statp->options & RES_ROTATE))
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  /* Global offset.  The lowest bit indicates whether the offset has
Packit 6c4009
     been initialized with a random value.  Use relaxed MO to access
Packit 6c4009
     global_offset because all we need is a sequence of roughly
Packit 6c4009
     sequential value.  */
Packit 6c4009
  static unsigned int global_offset;
Packit 6c4009
  unsigned int offset = atomic_fetch_add_relaxed (&global_offset, 2);
Packit 6c4009
  if ((offset & 1) == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Initialization is required.  */
Packit 6c4009
#if HP_TIMING_AVAIL
Packit 6c4009
      uint64_t ticks;
Packit 6c4009
      HP_TIMING_NOW (ticks);
Packit 6c4009
      offset = ticks;
Packit 6c4009
#else
Packit 6c4009
      struct timeval tv;
Packit 6c4009
      __gettimeofday (&tv, NULL);
Packit 6c4009
      offset = ((tv.tv_sec << 8) ^ tv.tv_usec);
Packit 6c4009
#endif
Packit 6c4009
      /* The lowest bit is the most random.  Preserve it.  */
Packit 6c4009
      offset <<= 1;
Packit 6c4009
Packit 6c4009
      /* Store the new starting value.  atomic_fetch_add_relaxed
Packit 6c4009
	 returns the old value, so emulate that by storing the new
Packit 6c4009
	 (incremented) value.  Concurrent initialization with
Packit 6c4009
	 different random values is harmless.  */
Packit 6c4009
      atomic_store_relaxed (&global_offset, (offset | 1) + 2);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Remove the initialization bit.  */
Packit 6c4009
  offset >>= 1;
Packit 6c4009
Packit 6c4009
  /* Avoid the division in the most common cases.  */
Packit 6c4009
  switch (nscount)
Packit 6c4009
    {
Packit 6c4009
    case 2:
Packit 6c4009
      return offset & 1;
Packit 6c4009
    case 3:
Packit 6c4009
      return offset % 3;
Packit 6c4009
    case 4:
Packit 6c4009
      return offset & 3;
Packit 6c4009
    default:
Packit 6c4009
      return offset % nscount;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* int
Packit 6c4009
 * res_queriesmatch(buf1, eom1, buf2, eom2)
Packit 6c4009
 *	is there a 1:1 mapping of (name,type,class)
Packit 6c4009
 *	in (buf1,eom1) and (buf2,eom2)?
Packit 6c4009
 * returns:
Packit 6c4009
 *	-1 : format error
Packit 6c4009
 *	0  : not a 1:1 mapping
Packit 6c4009
 *	>0 : is a 1:1 mapping
Packit 6c4009
 * author:
Packit 6c4009
 *	paul vixie, 29may94
Packit 6c4009
 */
Packit 6c4009
int
Packit 6c4009
res_queriesmatch(const u_char *buf1, const u_char *eom1,
Packit 6c4009
		 const u_char *buf2, const u_char *eom2)
Packit 6c4009
{
Packit 6c4009
	if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2)
Packit 6c4009
		return (-1);
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * Only header section present in replies to
Packit 6c4009
	 * dynamic update packets.
Packit 6c4009
	 */
Packit 6c4009
	if ((((HEADER *)buf1)->opcode == ns_o_update) &&
Packit 6c4009
	    (((HEADER *)buf2)->opcode == ns_o_update))
Packit 6c4009
		return (1);
Packit 6c4009
Packit 6c4009
	/* Note that we initially do not convert QDCOUNT to the host byte
Packit 6c4009
	   order.  We can compare it with the second buffer's QDCOUNT
Packit 6c4009
	   value without doing this.  */
Packit 6c4009
	int qdcount = ((HEADER*)buf1)->qdcount;
Packit 6c4009
	if (qdcount != ((HEADER*)buf2)->qdcount)
Packit 6c4009
		return (0);
Packit 6c4009
Packit 6c4009
	qdcount = htons (qdcount);
Packit 6c4009
	const u_char *cp = buf1 + HFIXEDSZ;
Packit 6c4009
Packit 6c4009
	while (qdcount-- > 0) {
Packit 6c4009
		char tname[MAXDNAME+1];
Packit 6c4009
		int n, ttype, tclass;
Packit 6c4009
Packit 6c4009
		n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
Packit 6c4009
		if (n < 0)
Packit 6c4009
			return (-1);
Packit 6c4009
		cp += n;
Packit 6c4009
		if (cp + 2 * INT16SZ > eom1)
Packit 6c4009
			return (-1);
Packit 6c4009
		NS_GET16(ttype, cp);
Packit 6c4009
		NS_GET16(tclass, cp);
Packit 6c4009
		if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
Packit 6c4009
			return (0);
Packit 6c4009
	}
Packit 6c4009
	return (1);
Packit 6c4009
}
Packit 6c4009
libresolv_hidden_def (res_queriesmatch)
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__res_context_send (struct resolv_context *ctx,
Packit 6c4009
		    const unsigned char *buf, int buflen,
Packit 6c4009
		    const unsigned char *buf2, int buflen2,
Packit 6c4009
		    unsigned char *ans, int anssiz,
Packit 6c4009
		    unsigned char **ansp, unsigned char **ansp2,
Packit 6c4009
		    int *nansp2, int *resplen2, int *ansp2_malloced)
Packit 6c4009
{
Packit 6c4009
	struct __res_state *statp = ctx->resp;
Packit 6c4009
	int gotsomewhere, terrno, try, v_circuit, resplen, n;
Packit 6c4009
Packit 6c4009
	if (statp->nscount == 0) {
Packit 6c4009
		__set_errno (ESRCH);
Packit 6c4009
		return (-1);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	if (anssiz < (buf2 == NULL ? 1 : 2) * HFIXEDSZ) {
Packit 6c4009
		__set_errno (EINVAL);
Packit 6c4009
		return (-1);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	v_circuit = ((statp->options & RES_USEVC)
Packit 6c4009
		     || buflen > PACKETSZ
Packit 6c4009
		     || buflen2 > PACKETSZ);
Packit 6c4009
	gotsomewhere = 0;
Packit 6c4009
	terrno = ETIMEDOUT;
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * If the ns_addr_list in the resolver context has changed, then
Packit 6c4009
	 * invalidate our cached copy and the associated timing data.
Packit 6c4009
	 */
Packit 6c4009
	if (EXT(statp).nscount != 0) {
Packit 6c4009
		int needclose = 0;
Packit 6c4009
Packit 6c4009
		if (EXT(statp).nscount != statp->nscount)
Packit 6c4009
			needclose++;
Packit 6c4009
		else
Packit 6c4009
			for (unsigned int ns = 0; ns < statp->nscount; ns++) {
Packit 6c4009
				if (statp->nsaddr_list[ns].sin_family != 0
Packit 6c4009
				    && !sock_eq((struct sockaddr_in6 *)
Packit 6c4009
						&statp->nsaddr_list[ns],
Packit 6c4009
						EXT(statp).nsaddrs[ns]))
Packit 6c4009
				{
Packit 6c4009
					needclose++;
Packit 6c4009
					break;
Packit 6c4009
				}
Packit 6c4009
			}
Packit 6c4009
		if (needclose) {
Packit 6c4009
			__res_iclose(statp, false);
Packit 6c4009
			EXT(statp).nscount = 0;
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * Maybe initialize our private copy of the ns_addr_list.
Packit 6c4009
	 */
Packit 6c4009
	if (EXT(statp).nscount == 0) {
Packit 6c4009
		for (unsigned int ns = 0; ns < statp->nscount; ns++) {
Packit 6c4009
			EXT(statp).nssocks[ns] = -1;
Packit 6c4009
			if (statp->nsaddr_list[ns].sin_family == 0)
Packit 6c4009
				continue;
Packit 6c4009
			if (EXT(statp).nsaddrs[ns] == NULL)
Packit 6c4009
				EXT(statp).nsaddrs[ns] =
Packit 6c4009
				    malloc(sizeof (struct sockaddr_in6));
Packit 6c4009
			if (EXT(statp).nsaddrs[ns] != NULL)
Packit 6c4009
				memset (mempcpy(EXT(statp).nsaddrs[ns],
Packit 6c4009
						&statp->nsaddr_list[ns],
Packit 6c4009
						sizeof (struct sockaddr_in)),
Packit 6c4009
					'\0',
Packit 6c4009
					sizeof (struct sockaddr_in6)
Packit 6c4009
					- sizeof (struct sockaddr_in));
Packit 6c4009
			else
Packit 6c4009
				return -1;
Packit 6c4009
		}
Packit 6c4009
		EXT(statp).nscount = statp->nscount;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	/* Name server index offset.  Used to implement
Packit 6c4009
	   RES_ROTATE.  */
Packit 6c4009
	unsigned int ns_offset = nameserver_offset (statp);
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * Send request, RETRY times, or until successful.
Packit 6c4009
	 */
Packit 6c4009
	for (try = 0; try < statp->retry; try++) {
Packit 6c4009
	    for (unsigned ns_shift = 0; ns_shift < statp->nscount; ns_shift++)
Packit 6c4009
	    {
Packit 6c4009
		/* The actual name server index.  This implements
Packit 6c4009
		   RES_ROTATE.  */
Packit 6c4009
		unsigned int ns = ns_shift + ns_offset;
Packit 6c4009
		if (ns >= statp->nscount)
Packit 6c4009
			ns -= statp->nscount;
Packit 6c4009
Packit 6c4009
	    same_ns:
Packit 6c4009
		if (__glibc_unlikely (v_circuit))       {
Packit 6c4009
			/* Use VC; at most one attempt per server. */
Packit 6c4009
			try = statp->retry;
Packit 6c4009
			n = send_vc(statp, buf, buflen, buf2, buflen2,
Packit 6c4009
				    &ans, &anssiz, &terrno,
Packit 6c4009
				    ns, ansp, ansp2, nansp2, resplen2,
Packit 6c4009
				    ansp2_malloced);
Packit 6c4009
			if (n < 0)
Packit 6c4009
				return (-1);
Packit 6c4009
			if (n == 0 && (buf2 == NULL || *resplen2 == 0))
Packit 6c4009
				goto next_ns;
Packit 6c4009
		} else {
Packit 6c4009
			/* Use datagrams. */
Packit 6c4009
			n = send_dg(statp, buf, buflen, buf2, buflen2,
Packit 6c4009
				    &ans, &anssiz, &terrno,
Packit 6c4009
				    ns, &v_circuit, &gotsomewhere, ansp,
Packit 6c4009
				    ansp2, nansp2, resplen2, ansp2_malloced);
Packit 6c4009
			if (n < 0)
Packit 6c4009
				return (-1);
Packit 6c4009
			if (n == 0 && (buf2 == NULL || *resplen2 == 0))
Packit 6c4009
				goto next_ns;
Packit 6c4009
			if (v_circuit)
Packit 6c4009
			  // XXX Check whether both requests failed or
Packit 6c4009
			  // XXX whether one has been answered successfully
Packit 6c4009
				goto same_ns;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
		resplen = n;
Packit 6c4009
Packit 6c4009
		/*
Packit 6c4009
		 * If we have temporarily opened a virtual circuit,
Packit 6c4009
		 * or if we haven't been asked to keep a socket open,
Packit 6c4009
		 * close the socket.
Packit 6c4009
		 */
Packit 6c4009
		if ((v_circuit && (statp->options & RES_USEVC) == 0) ||
Packit 6c4009
		    (statp->options & RES_STAYOPEN) == 0) {
Packit 6c4009
			__res_iclose(statp, false);
Packit 6c4009
		}
Packit 6c4009
		return (resplen);
Packit 6c4009
 next_ns: ;
Packit 6c4009
	   } /*foreach ns*/
Packit 6c4009
	} /*foreach retry*/
Packit 6c4009
	__res_iclose(statp, false);
Packit 6c4009
	if (!v_circuit) {
Packit 6c4009
		if (!gotsomewhere)
Packit 6c4009
			__set_errno (ECONNREFUSED);	/* no nameservers found */
Packit 6c4009
		else
Packit 6c4009
			__set_errno (ETIMEDOUT);	/* no answer obtained */
Packit 6c4009
	} else
Packit 6c4009
		__set_errno (terrno);
Packit 6c4009
	return (-1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Common part of res_nsend and res_send.  */
Packit 6c4009
static int
Packit 6c4009
context_send_common (struct resolv_context *ctx,
Packit 6c4009
		     const unsigned char *buf, int buflen,
Packit 6c4009
		     unsigned char *ans, int anssiz)
Packit 6c4009
{
Packit 6c4009
  if (ctx == NULL)
Packit 6c4009
    {
Packit 6c4009
      RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
  int result = __res_context_send (ctx, buf, buflen, NULL, 0, ans, anssiz,
Packit 6c4009
				   NULL, NULL, NULL, NULL, NULL);
Packit 6c4009
  __resolv_context_put (ctx);
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
res_nsend (res_state statp, const unsigned char *buf, int buflen,
Packit 6c4009
	   unsigned char *ans, int anssiz)
Packit 6c4009
{
Packit 6c4009
  return context_send_common
Packit 6c4009
    (__resolv_context_get_override (statp), buf, buflen, ans, anssiz);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
res_send (const unsigned char *buf, int buflen, unsigned char *ans, int anssiz)
Packit 6c4009
{
Packit 6c4009
  return context_send_common
Packit 6c4009
    (__resolv_context_get (), buf, buflen, ans, anssiz);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Private */
Packit 6c4009
Packit 6c4009
static struct sockaddr *
Packit 6c4009
get_nsaddr (res_state statp, unsigned int n)
Packit 6c4009
{
Packit 6c4009
  assert (n < statp->nscount);
Packit 6c4009
Packit 6c4009
  if (statp->nsaddr_list[n].sin_family == 0 && EXT(statp).nsaddrs[n] != NULL)
Packit 6c4009
    /* EXT(statp).nsaddrs[n] holds an address that is larger than
Packit 6c4009
       struct sockaddr, and user code did not update
Packit 6c4009
       statp->nsaddr_list[n].  */
Packit 6c4009
    return (struct sockaddr *) EXT(statp).nsaddrs[n];
Packit 6c4009
  else
Packit 6c4009
    /* User code updated statp->nsaddr_list[n], or statp->nsaddr_list[n]
Packit 6c4009
       has the same content as EXT(statp).nsaddrs[n].  */
Packit 6c4009
    return (struct sockaddr *) (void *) &statp->nsaddr_list[n];
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Close the resolver structure, assign zero to *RESPLEN2 if RESPLEN2
Packit 6c4009
   is not NULL, and return zero.  */
Packit 6c4009
static int
Packit 6c4009
__attribute__ ((warn_unused_result))
Packit 6c4009
close_and_return_error (res_state statp, int *resplen2)
Packit 6c4009
{
Packit 6c4009
  __res_iclose(statp, false);
Packit 6c4009
  if (resplen2 != NULL)
Packit 6c4009
    *resplen2 = 0;
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The send_vc function is responsible for sending a DNS query over TCP
Packit 6c4009
   to the nameserver numbered NS from the res_state STATP i.e.
Packit 6c4009
   EXT(statp).nssocks[ns].  The function supports sending both IPv4 and
Packit 6c4009
   IPv6 queries at the same serially on the same socket.
Packit 6c4009
Packit 6c4009
   Please note that for TCP there is no way to disable sending both
Packit 6c4009
   queries, unlike UDP, which honours RES_SNGLKUP and RES_SNGLKUPREOP
Packit 6c4009
   and sends the queries serially and waits for the result after each
Packit 6c4009
   sent query.  This implementation should be corrected to honour these
Packit 6c4009
   options.
Packit 6c4009
Packit 6c4009
   Please also note that for TCP we send both queries over the same
Packit 6c4009
   socket one after another.  This technically violates best practice
Packit 6c4009
   since the server is allowed to read the first query, respond, and
Packit 6c4009
   then close the socket (to service another client).  If the server
Packit 6c4009
   does this, then the remaining second query in the socket data buffer
Packit 6c4009
   will cause the server to send the client an RST which will arrive
Packit 6c4009
   asynchronously and the client's OS will likely tear down the socket
Packit 6c4009
   receive buffer resulting in a potentially short read and lost
Packit 6c4009
   response data.  This will force the client to retry the query again,
Packit 6c4009
   and this process may repeat until all servers and connection resets
Packit 6c4009
   are exhausted and then the query will fail.  It's not known if this
Packit 6c4009
   happens with any frequency in real DNS server implementations.  This
Packit 6c4009
   implementation should be corrected to use two sockets by default for
Packit 6c4009
   parallel queries.
Packit 6c4009
Packit 6c4009
   The query stored in BUF of BUFLEN length is sent first followed by
Packit 6c4009
   the query stored in BUF2 of BUFLEN2 length.  Queries are sent
Packit 6c4009
   serially on the same socket.
Packit 6c4009
Packit 6c4009
   Answers to the query are stored firstly in *ANSP up to a max of
Packit 6c4009
   *ANSSIZP bytes.  If more than *ANSSIZP bytes are needed and ANSCP
Packit 6c4009
   is non-NULL (to indicate that modifying the answer buffer is allowed)
Packit 6c4009
   then malloc is used to allocate a new response buffer and ANSCP and
Packit 6c4009
   ANSP will both point to the new buffer.  If more than *ANSSIZP bytes
Packit 6c4009
   are needed but ANSCP is NULL, then as much of the response as
Packit 6c4009
   possible is read into the buffer, but the results will be truncated.
Packit 6c4009
   When truncation happens because of a small answer buffer the DNS
Packit 6c4009
   packets header field TC will bet set to 1, indicating a truncated
Packit 6c4009
   message and the rest of the socket data will be read and discarded.
Packit 6c4009
Packit 6c4009
   Answers to the query are stored secondly in *ANSP2 up to a max of
Packit 6c4009
   *ANSSIZP2 bytes, with the actual response length stored in
Packit 6c4009
   *RESPLEN2.  If more than *ANSSIZP bytes are needed and ANSP2
Packit 6c4009
   is non-NULL (required for a second query) then malloc is used to
Packit 6c4009
   allocate a new response buffer, *ANSSIZP2 is set to the new buffer
Packit 6c4009
   size and *ANSP2_MALLOCED is set to 1.
Packit 6c4009
Packit 6c4009
   The ANSP2_MALLOCED argument will eventually be removed as the
Packit 6c4009
   change in buffer pointer can be used to detect the buffer has
Packit 6c4009
   changed and that the caller should use free on the new buffer.
Packit 6c4009
Packit 6c4009
   Note that the answers may arrive in any order from the server and
Packit 6c4009
   therefore the first and second answer buffers may not correspond to
Packit 6c4009
   the first and second queries.
Packit 6c4009
Packit 6c4009
   It is not supported to call this function with a non-NULL ANSP2
Packit 6c4009
   but a NULL ANSCP.  Put another way, you can call send_vc with a
Packit 6c4009
   single unmodifiable buffer or two modifiable buffers, but no other
Packit 6c4009
   combination is supported.
Packit 6c4009
Packit 6c4009
   It is the caller's responsibility to free the malloc allocated
Packit 6c4009
   buffers by detecting that the pointers have changed from their
Packit 6c4009
   original values i.e. *ANSCP or *ANSP2 has changed.
Packit 6c4009
Packit 6c4009
   If errors are encountered then *TERRNO is set to an appropriate
Packit 6c4009
   errno value and a zero result is returned for a recoverable error,
Packit 6c4009
   and a less-than zero result is returned for a non-recoverable error.
Packit 6c4009
Packit 6c4009
   If no errors are encountered then *TERRNO is left unmodified and
Packit 6c4009
   a the length of the first response in bytes is returned.  */
Packit 6c4009
static int
Packit 6c4009
send_vc(res_state statp,
Packit 6c4009
	const u_char *buf, int buflen, const u_char *buf2, int buflen2,
Packit 6c4009
	u_char **ansp, int *anssizp,
Packit 6c4009
	int *terrno, int ns, u_char **anscp, u_char **ansp2, int *anssizp2,
Packit 6c4009
	int *resplen2, int *ansp2_malloced)
Packit 6c4009
{
Packit 6c4009
	const HEADER *hp = (HEADER *) buf;
Packit 6c4009
	const HEADER *hp2 = (HEADER *) buf2;
Packit 6c4009
	HEADER *anhp = (HEADER *) *ansp;
Packit 6c4009
	struct sockaddr *nsap = get_nsaddr (statp, ns);
Packit 6c4009
	int truncating, connreset, n;
Packit 6c4009
	/* On some architectures compiler might emit a warning indicating
Packit 6c4009
	   'resplen' may be used uninitialized.  However if buf2 == NULL
Packit 6c4009
	   then this code won't be executed; if buf2 != NULL, then first
Packit 6c4009
	   time round the loop recvresp1 and recvresp2 will be 0 so this
Packit 6c4009
	   code won't be executed but "thisresplenp = &resplen;" followed
Packit 6c4009
	   by "*thisresplenp = rlen;" will be executed so that subsequent
Packit 6c4009
	   times round the loop resplen has been initialized.  So this is
Packit 6c4009
	   a false-positive.
Packit 6c4009
	 */
Packit 6c4009
	DIAG_PUSH_NEEDS_COMMENT;
Packit 6c4009
	DIAG_IGNORE_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
Packit 6c4009
	int resplen;
Packit 6c4009
	DIAG_POP_NEEDS_COMMENT;
Packit 6c4009
	struct iovec iov[4];
Packit 6c4009
	u_short len;
Packit 6c4009
	u_short len2;
Packit 6c4009
	u_char *cp;
Packit 6c4009
Packit 6c4009
	connreset = 0;
Packit 6c4009
 same_ns:
Packit 6c4009
	truncating = 0;
Packit 6c4009
Packit 6c4009
	/* Are we still talking to whom we want to talk to? */
Packit 6c4009
	if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
Packit 6c4009
		struct sockaddr_in6 peer;
Packit 6c4009
		socklen_t size = sizeof peer;
Packit 6c4009
Packit 6c4009
		if (getpeername(statp->_vcsock,
Packit 6c4009
				(struct sockaddr *)&peer, &size) < 0 ||
Packit 6c4009
		    !sock_eq(&peer, (struct sockaddr_in6 *) nsap)) {
Packit 6c4009
			__res_iclose(statp, false);
Packit 6c4009
			statp->_flags &= ~RES_F_VC;
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
Packit 6c4009
		if (statp->_vcsock >= 0)
Packit 6c4009
		  __res_iclose(statp, false);
Packit 6c4009
Packit 6c4009
		statp->_vcsock = socket
Packit 6c4009
		  (nsap->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
Packit 6c4009
		if (statp->_vcsock < 0) {
Packit 6c4009
			*terrno = errno;
Packit 6c4009
			if (resplen2 != NULL)
Packit 6c4009
			  *resplen2 = 0;
Packit 6c4009
			return (-1);
Packit 6c4009
		}
Packit 6c4009
		__set_errno (0);
Packit 6c4009
		if (connect(statp->_vcsock, nsap,
Packit 6c4009
			    nsap->sa_family == AF_INET
Packit 6c4009
			    ? sizeof (struct sockaddr_in)
Packit 6c4009
			    : sizeof (struct sockaddr_in6)) < 0) {
Packit 6c4009
			*terrno = errno;
Packit 6c4009
			return close_and_return_error (statp, resplen2);
Packit 6c4009
		}
Packit 6c4009
		statp->_flags |= RES_F_VC;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * Send length & message
Packit 6c4009
	 */
Packit 6c4009
	len = htons ((u_short) buflen);
Packit 6c4009
	evConsIovec(&len, INT16SZ, &iov[0]);
Packit 6c4009
	evConsIovec((void*)buf, buflen, &iov[1]);
Packit 6c4009
	int niov = 2;
Packit 6c4009
	ssize_t explen = INT16SZ + buflen;
Packit 6c4009
	if (buf2 != NULL) {
Packit 6c4009
		len2 = htons ((u_short) buflen2);
Packit 6c4009
		evConsIovec(&len2, INT16SZ, &iov[2]);
Packit 6c4009
		evConsIovec((void*)buf2, buflen2, &iov[3]);
Packit 6c4009
		niov = 4;
Packit 6c4009
		explen += INT16SZ + buflen2;
Packit 6c4009
	}
Packit 6c4009
	if (TEMP_FAILURE_RETRY (writev(statp->_vcsock, iov, niov)) != explen) {
Packit 6c4009
		*terrno = errno;
Packit 6c4009
		return close_and_return_error (statp, resplen2);
Packit 6c4009
	}
Packit 6c4009
	/*
Packit 6c4009
	 * Receive length & response
Packit 6c4009
	 */
Packit 6c4009
	int recvresp1 = 0;
Packit 6c4009
	/* Skip the second response if there is no second query.
Packit 6c4009
	   To do that we mark the second response as received.  */
Packit 6c4009
	int recvresp2 = buf2 == NULL;
Packit 6c4009
	uint16_t rlen16;
Packit 6c4009
 read_len:
Packit 6c4009
	cp = (u_char *)&rlen16;
Packit 6c4009
	len = sizeof(rlen16);
Packit 6c4009
	while ((n = TEMP_FAILURE_RETRY (read(statp->_vcsock, cp,
Packit 6c4009
					     (int)len))) > 0) {
Packit 6c4009
		cp += n;
Packit 6c4009
		if ((len -= n) <= 0)
Packit 6c4009
			break;
Packit 6c4009
	}
Packit 6c4009
	if (n <= 0) {
Packit 6c4009
		*terrno = errno;
Packit 6c4009
		/*
Packit 6c4009
		 * A long running process might get its TCP
Packit 6c4009
		 * connection reset if the remote server was
Packit 6c4009
		 * restarted.  Requery the server instead of
Packit 6c4009
		 * trying a new one.  When there is only one
Packit 6c4009
		 * server, this means that a query might work
Packit 6c4009
		 * instead of failing.  We only allow one reset
Packit 6c4009
		 * per query to prevent looping.
Packit 6c4009
		 */
Packit 6c4009
		if (*terrno == ECONNRESET && !connreset)
Packit 6c4009
		  {
Packit 6c4009
		    __res_iclose (statp, false);
Packit 6c4009
		    connreset = 1;
Packit 6c4009
		    goto same_ns;
Packit 6c4009
		  }
Packit 6c4009
		return close_and_return_error (statp, resplen2);
Packit 6c4009
	}
Packit 6c4009
	int rlen = ntohs (rlen16);
Packit 6c4009
Packit 6c4009
	int *thisanssizp;
Packit 6c4009
	u_char **thisansp;
Packit 6c4009
	int *thisresplenp;
Packit 6c4009
	if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
Packit 6c4009
		/* We have not received any responses
Packit 6c4009
		   yet or we only have one response to
Packit 6c4009
		   receive.  */
Packit 6c4009
		thisanssizp = anssizp;
Packit 6c4009
		thisansp = anscp ?: ansp;
Packit 6c4009
		assert (anscp != NULL || ansp2 == NULL);
Packit 6c4009
		thisresplenp = &resplen;
Packit 6c4009
	} else {
Packit 6c4009
		thisanssizp = anssizp2;
Packit 6c4009
		thisansp = ansp2;
Packit 6c4009
		thisresplenp = resplen2;
Packit 6c4009
	}
Packit 6c4009
	anhp = (HEADER *) *thisansp;
Packit 6c4009
Packit 6c4009
	*thisresplenp = rlen;
Packit 6c4009
	/* Is the answer buffer too small?  */
Packit 6c4009
	if (*thisanssizp < rlen) {
Packit 6c4009
		/* If the current buffer is not the the static
Packit 6c4009
		   user-supplied buffer then we can reallocate
Packit 6c4009
		   it.  */
Packit 6c4009
		if (thisansp != NULL && thisansp != ansp) {
Packit 6c4009
			/* Always allocate MAXPACKET, callers expect
Packit 6c4009
			   this specific size.  */
Packit 6c4009
			u_char *newp = malloc (MAXPACKET);
Packit 6c4009
			if (newp == NULL)
Packit 6c4009
			  {
Packit 6c4009
			    *terrno = ENOMEM;
Packit 6c4009
			    return close_and_return_error (statp, resplen2);
Packit 6c4009
			  }
Packit 6c4009
			*thisanssizp = MAXPACKET;
Packit 6c4009
			*thisansp = newp;
Packit 6c4009
			if (thisansp == ansp2)
Packit 6c4009
			  *ansp2_malloced = 1;
Packit 6c4009
			anhp = (HEADER *) newp;
Packit 6c4009
			/* A uint16_t can't be larger than MAXPACKET
Packit 6c4009
			   thus it's safe to allocate MAXPACKET but
Packit 6c4009
			   read RLEN bytes instead.  */
Packit 6c4009
			len = rlen;
Packit 6c4009
		} else {
Packit 6c4009
			truncating = 1;
Packit 6c4009
			len = *thisanssizp;
Packit 6c4009
		}
Packit 6c4009
	} else
Packit 6c4009
		len = rlen;
Packit 6c4009
Packit 6c4009
	if (__glibc_unlikely (len < HFIXEDSZ))       {
Packit 6c4009
		/*
Packit 6c4009
		 * Undersized message.
Packit 6c4009
		 */
Packit 6c4009
		*terrno = EMSGSIZE;
Packit 6c4009
		return close_and_return_error (statp, resplen2);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	cp = *thisansp;
Packit 6c4009
	while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
Packit 6c4009
		cp += n;
Packit 6c4009
		len -= n;
Packit 6c4009
	}
Packit 6c4009
	if (__glibc_unlikely (n <= 0))       {
Packit 6c4009
		*terrno = errno;
Packit 6c4009
		return close_and_return_error (statp, resplen2);
Packit 6c4009
	}
Packit 6c4009
	if (__glibc_unlikely (truncating))       {
Packit 6c4009
		/*
Packit 6c4009
		 * Flush rest of answer so connection stays in synch.
Packit 6c4009
		 */
Packit 6c4009
		anhp->tc = 1;
Packit 6c4009
		len = rlen - *thisanssizp;
Packit 6c4009
		while (len != 0) {
Packit 6c4009
			char junk[PACKETSZ];
Packit 6c4009
Packit 6c4009
			n = read(statp->_vcsock, junk,
Packit 6c4009
				 (len > sizeof junk) ? sizeof junk : len);
Packit 6c4009
			if (n > 0)
Packit 6c4009
				len -= n;
Packit 6c4009
			else
Packit 6c4009
				break;
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
	/*
Packit 6c4009
	 * If the calling application has bailed out of
Packit 6c4009
	 * a previous call and failed to arrange to have
Packit 6c4009
	 * the circuit closed or the server has got
Packit 6c4009
	 * itself confused, then drop the packet and
Packit 6c4009
	 * wait for the correct one.
Packit 6c4009
	 */
Packit 6c4009
	if ((recvresp1 || hp->id != anhp->id)
Packit 6c4009
	    && (recvresp2 || hp2->id != anhp->id))
Packit 6c4009
		goto read_len;
Packit 6c4009
Packit 6c4009
	/* Mark which reply we received.  */
Packit 6c4009
	if (recvresp1 == 0 && hp->id == anhp->id)
Packit 6c4009
	  recvresp1 = 1;
Packit 6c4009
	else
Packit 6c4009
	  recvresp2 = 1;
Packit 6c4009
	/* Repeat waiting if we have a second answer to arrive.  */
Packit 6c4009
	if ((recvresp1 & recvresp2) == 0)
Packit 6c4009
		goto read_len;
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * All is well, or the error is fatal.  Signal that the
Packit 6c4009
	 * next nameserver ought not be tried.
Packit 6c4009
	 */
Packit 6c4009
	return resplen;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
reopen (res_state statp, int *terrno, int ns)
Packit 6c4009
{
Packit 6c4009
	if (EXT(statp).nssocks[ns] == -1) {
Packit 6c4009
		struct sockaddr *nsap = get_nsaddr (statp, ns);
Packit 6c4009
		socklen_t slen;
Packit 6c4009
Packit 6c4009
		/* only try IPv6 if IPv6 NS and if not failed before */
Packit 6c4009
		if (nsap->sa_family == AF_INET6 && !statp->ipv6_unavail) {
Packit 6c4009
			EXT(statp).nssocks[ns] = socket
Packit 6c4009
			  (PF_INET6,
Packit 6c4009
			   SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
Packit 6c4009
			if (EXT(statp).nssocks[ns] < 0)
Packit 6c4009
			    statp->ipv6_unavail = errno == EAFNOSUPPORT;
Packit 6c4009
			slen = sizeof (struct sockaddr_in6);
Packit 6c4009
		} else if (nsap->sa_family == AF_INET) {
Packit 6c4009
			EXT(statp).nssocks[ns] = socket
Packit 6c4009
			  (PF_INET,
Packit 6c4009
			   SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
Packit 6c4009
			slen = sizeof (struct sockaddr_in);
Packit 6c4009
		}
Packit 6c4009
		if (EXT(statp).nssocks[ns] < 0) {
Packit 6c4009
			*terrno = errno;
Packit 6c4009
			return (-1);
Packit 6c4009
		}
Packit 6c4009
Packit Service 9c1327
		/* Enable full ICMP error reporting for this
Packit Service 9c1327
		   socket.  */
Packit Service 9c1327
		if (__res_enable_icmp (nsap->sa_family,
Packit Service 9c1327
				       EXT (statp).nssocks[ns]) < 0)
Packit Service 9c1327
		  {
Packit Service 9c1327
		    int saved_errno = errno;
Packit Service 9c1327
		    __res_iclose (statp, false);
Packit Service 9c1327
		    __set_errno (saved_errno);
Packit Service 9c1327
		    *terrno = saved_errno;
Packit Service 9c1327
		    return -1;
Packit Service 9c1327
		  }
Packit Service 9c1327
Packit 6c4009
		/*
Packit 6c4009
		 * On a 4.3BSD+ machine (client and server,
Packit 6c4009
		 * actually), sending to a nameserver datagram
Packit 6c4009
		 * port with no nameserver will cause an
Packit 6c4009
		 * ICMP port unreachable message to be returned.
Packit 6c4009
		 * If our datagram socket is "connected" to the
Packit 6c4009
		 * server, we get an ECONNREFUSED error on the next
Packit 6c4009
		 * socket operation, and select returns if the
Packit 6c4009
		 * error message is received.  We can thus detect
Packit 6c4009
		 * the absence of a nameserver without timing out.
Packit 6c4009
		 */
Packit 6c4009
		/* With GCC 5.3 when compiling with -Os the compiler
Packit 6c4009
		   emits a warning that slen may be used uninitialized,
Packit 6c4009
		   but that is never true.  Both slen and
Packit 6c4009
		   EXT(statp).nssocks[ns] are initialized together or
Packit 6c4009
		   the function return -1 before control flow reaches
Packit 6c4009
		   the call to connect with slen.  */
Packit 6c4009
		DIAG_PUSH_NEEDS_COMMENT;
Packit 6c4009
		DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
Packit 6c4009
		if (connect(EXT(statp).nssocks[ns], nsap, slen) < 0) {
Packit 6c4009
		DIAG_POP_NEEDS_COMMENT;
Packit 6c4009
			__res_iclose(statp, false);
Packit 6c4009
			return (0);
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	return 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The send_dg function is responsible for sending a DNS query over UDP
Packit 6c4009
   to the nameserver numbered NS from the res_state STATP i.e.
Packit 6c4009
   EXT(statp).nssocks[ns].  The function supports IPv4 and IPv6 queries
Packit 6c4009
   along with the ability to send the query in parallel for both stacks
Packit 6c4009
   (default) or serially (RES_SINGLKUP).  It also supports serial lookup
Packit 6c4009
   with a close and reopen of the socket used to talk to the server
Packit 6c4009
   (RES_SNGLKUPREOP) to work around broken name servers.
Packit 6c4009
Packit 6c4009
   The query stored in BUF of BUFLEN length is sent first followed by
Packit 6c4009
   the query stored in BUF2 of BUFLEN2 length.  Queries are sent
Packit 6c4009
   in parallel (default) or serially (RES_SINGLKUP or RES_SNGLKUPREOP).
Packit 6c4009
Packit 6c4009
   Answers to the query are stored firstly in *ANSP up to a max of
Packit 6c4009
   *ANSSIZP bytes.  If more than *ANSSIZP bytes are needed and ANSCP
Packit 6c4009
   is non-NULL (to indicate that modifying the answer buffer is allowed)
Packit 6c4009
   then malloc is used to allocate a new response buffer and ANSCP and
Packit 6c4009
   ANSP will both point to the new buffer.  If more than *ANSSIZP bytes
Packit 6c4009
   are needed but ANSCP is NULL, then as much of the response as
Packit 6c4009
   possible is read into the buffer, but the results will be truncated.
Packit 6c4009
   When truncation happens because of a small answer buffer the DNS
Packit 6c4009
   packets header field TC will bet set to 1, indicating a truncated
Packit 6c4009
   message, while the rest of the UDP packet is discarded.
Packit 6c4009
Packit 6c4009
   Answers to the query are stored secondly in *ANSP2 up to a max of
Packit 6c4009
   *ANSSIZP2 bytes, with the actual response length stored in
Packit 6c4009
   *RESPLEN2.  If more than *ANSSIZP bytes are needed and ANSP2
Packit 6c4009
   is non-NULL (required for a second query) then malloc is used to
Packit 6c4009
   allocate a new response buffer, *ANSSIZP2 is set to the new buffer
Packit 6c4009
   size and *ANSP2_MALLOCED is set to 1.
Packit 6c4009
Packit 6c4009
   The ANSP2_MALLOCED argument will eventually be removed as the
Packit 6c4009
   change in buffer pointer can be used to detect the buffer has
Packit 6c4009
   changed and that the caller should use free on the new buffer.
Packit 6c4009
Packit 6c4009
   Note that the answers may arrive in any order from the server and
Packit 6c4009
   therefore the first and second answer buffers may not correspond to
Packit 6c4009
   the first and second queries.
Packit 6c4009
Packit 6c4009
   It is not supported to call this function with a non-NULL ANSP2
Packit 6c4009
   but a NULL ANSCP.  Put another way, you can call send_vc with a
Packit 6c4009
   single unmodifiable buffer or two modifiable buffers, but no other
Packit 6c4009
   combination is supported.
Packit 6c4009
Packit 6c4009
   It is the caller's responsibility to free the malloc allocated
Packit 6c4009
   buffers by detecting that the pointers have changed from their
Packit 6c4009
   original values i.e. *ANSCP or *ANSP2 has changed.
Packit 6c4009
Packit 6c4009
   If an answer is truncated because of UDP datagram DNS limits then
Packit 6c4009
   *V_CIRCUIT is set to 1 and the return value non-zero to indicate to
Packit 6c4009
   the caller to retry with TCP.  The value *GOTSOMEWHERE is set to 1
Packit 6c4009
   if any progress was made reading a response from the nameserver and
Packit 6c4009
   is used by the caller to distinguish between ECONNREFUSED and
Packit 6c4009
   ETIMEDOUT (the latter if *GOTSOMEWHERE is 1).
Packit 6c4009
Packit 6c4009
   If errors are encountered then *TERRNO is set to an appropriate
Packit 6c4009
   errno value and a zero result is returned for a recoverable error,
Packit 6c4009
   and a less-than zero result is returned for a non-recoverable error.
Packit 6c4009
Packit 6c4009
   If no errors are encountered then *TERRNO is left unmodified and
Packit 6c4009
   a the length of the first response in bytes is returned.  */
Packit 6c4009
static int
Packit 6c4009
send_dg(res_state statp,
Packit 6c4009
	const u_char *buf, int buflen, const u_char *buf2, int buflen2,
Packit 6c4009
	u_char **ansp, int *anssizp,
Packit 6c4009
	int *terrno, int ns, int *v_circuit, int *gotsomewhere, u_char **anscp,
Packit 6c4009
	u_char **ansp2, int *anssizp2, int *resplen2, int *ansp2_malloced)
Packit 6c4009
{
Packit 6c4009
	const HEADER *hp = (HEADER *) buf;
Packit 6c4009
	const HEADER *hp2 = (HEADER *) buf2;
Packit 6c4009
	struct timespec now, timeout, finish;
Packit 6c4009
	struct pollfd pfd[1];
Packit 6c4009
	int ptimeout;
Packit 6c4009
	struct sockaddr_in6 from;
Packit 6c4009
	int resplen = 0;
Packit 6c4009
	int n;
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * Compute time for the total operation.
Packit 6c4009
	 */
Packit 6c4009
	int seconds = (statp->retrans << ns);
Packit 6c4009
	if (ns > 0)
Packit 6c4009
		seconds /= statp->nscount;
Packit 6c4009
	if (seconds <= 0)
Packit 6c4009
		seconds = 1;
Packit 6c4009
	bool single_request_reopen = (statp->options & RES_SNGLKUPREOP) != 0;
Packit 6c4009
	bool single_request = (((statp->options & RES_SNGLKUP) != 0)
Packit 6c4009
			       | single_request_reopen);
Packit 6c4009
	int save_gotsomewhere = *gotsomewhere;
Packit 6c4009
Packit 6c4009
	int retval;
Packit 6c4009
 retry_reopen:
Packit 6c4009
	retval = reopen (statp, terrno, ns);
Packit 6c4009
	if (retval <= 0)
Packit 6c4009
	  {
Packit 6c4009
	    if (resplen2 != NULL)
Packit 6c4009
	      *resplen2 = 0;
Packit 6c4009
	    return retval;
Packit 6c4009
	  }
Packit 6c4009
 retry:
Packit 6c4009
	evNowTime(&now;;
Packit 6c4009
	evConsTime(&timeout, seconds, 0);
Packit 6c4009
	evAddTime(&finish, &now, &timeout);
Packit 6c4009
	int need_recompute = 0;
Packit 6c4009
	int nwritten = 0;
Packit 6c4009
	int recvresp1 = 0;
Packit 6c4009
	/* Skip the second response if there is no second query.
Packit 6c4009
	   To do that we mark the second response as received.  */
Packit 6c4009
	int recvresp2 = buf2 == NULL;
Packit 6c4009
	pfd[0].fd = EXT(statp).nssocks[ns];
Packit 6c4009
	pfd[0].events = POLLOUT;
Packit 6c4009
 wait:
Packit 6c4009
	if (need_recompute) {
Packit 6c4009
	recompute_resend:
Packit 6c4009
		evNowTime(&now;;
Packit 6c4009
		if (evCmpTime(finish, now) <= 0) {
Packit 6c4009
		poll_err_out:
Packit 6c4009
			return close_and_return_error (statp, resplen2);
Packit 6c4009
		}
Packit 6c4009
		evSubTime(&timeout, &finish, &now;;
Packit 6c4009
		need_recompute = 0;
Packit 6c4009
	}
Packit 6c4009
	/* Convert struct timespec in milliseconds.  */
Packit 6c4009
	ptimeout = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000;
Packit 6c4009
Packit 6c4009
	n = 0;
Packit 6c4009
	if (nwritten == 0)
Packit 6c4009
	  n = __poll (pfd, 1, 0);
Packit 6c4009
	if (__glibc_unlikely (n == 0))       {
Packit 6c4009
		n = __poll (pfd, 1, ptimeout);
Packit 6c4009
		need_recompute = 1;
Packit 6c4009
	}
Packit 6c4009
	if (n == 0) {
Packit 6c4009
		if (resplen > 1 && (recvresp1 || (buf2 != NULL && recvresp2)))
Packit 6c4009
		  {
Packit 6c4009
		    /* There are quite a few broken name servers out
Packit 6c4009
		       there which don't handle two outstanding
Packit 6c4009
		       requests from the same source.  There are also
Packit 6c4009
		       broken firewall settings.  If we time out after
Packit 6c4009
		       having received one answer switch to the mode
Packit 6c4009
		       where we send the second request only once we
Packit 6c4009
		       have received the first answer.  */
Packit 6c4009
		    if (!single_request)
Packit 6c4009
		      {
Packit 6c4009
			statp->options |= RES_SNGLKUP;
Packit 6c4009
			single_request = true;
Packit 6c4009
			*gotsomewhere = save_gotsomewhere;
Packit 6c4009
			goto retry;
Packit 6c4009
		      }
Packit 6c4009
		    else if (!single_request_reopen)
Packit 6c4009
		      {
Packit 6c4009
			statp->options |= RES_SNGLKUPREOP;
Packit 6c4009
			single_request_reopen = true;
Packit 6c4009
			*gotsomewhere = save_gotsomewhere;
Packit 6c4009
			__res_iclose (statp, false);
Packit 6c4009
			goto retry_reopen;
Packit 6c4009
		      }
Packit 6c4009
Packit 6c4009
		    *resplen2 = 1;
Packit 6c4009
		    return resplen;
Packit 6c4009
		  }
Packit 6c4009
Packit 6c4009
		*gotsomewhere = 1;
Packit 6c4009
		if (resplen2 != NULL)
Packit 6c4009
		  *resplen2 = 0;
Packit 6c4009
		return 0;
Packit 6c4009
	}
Packit 6c4009
	if (n < 0) {
Packit 6c4009
		if (errno == EINTR)
Packit 6c4009
			goto recompute_resend;
Packit 6c4009
Packit 6c4009
		goto poll_err_out;
Packit 6c4009
	}
Packit 6c4009
	__set_errno (0);
Packit 6c4009
	if (pfd[0].revents & POLLOUT) {
Packit 6c4009
#ifndef __ASSUME_SENDMMSG
Packit 6c4009
		static int have_sendmmsg;
Packit 6c4009
#else
Packit 6c4009
# define have_sendmmsg 1
Packit 6c4009
#endif
Packit 6c4009
		if (have_sendmmsg >= 0 && nwritten == 0 && buf2 != NULL
Packit 6c4009
		    && !single_request)
Packit 6c4009
		  {
Packit 6c4009
		    struct iovec iov =
Packit 6c4009
		      { .iov_base = (void *) buf, .iov_len = buflen };
Packit 6c4009
		    struct iovec iov2 =
Packit 6c4009
		      { .iov_base = (void *) buf2, .iov_len = buflen2 };
Packit 6c4009
		    struct mmsghdr reqs[2] =
Packit 6c4009
		      {
Packit 6c4009
			{
Packit 6c4009
			  .msg_hdr =
Packit 6c4009
			    {
Packit 6c4009
			      .msg_iov = &iov,
Packit 6c4009
			      .msg_iovlen = 1,
Packit 6c4009
			    },
Packit 6c4009
			},
Packit 6c4009
			{
Packit 6c4009
			  .msg_hdr =
Packit 6c4009
			    {
Packit 6c4009
			      .msg_iov = &iov2,
Packit 6c4009
			      .msg_iovlen = 1,
Packit 6c4009
			    }
Packit 6c4009
			},
Packit 6c4009
		      };
Packit 6c4009
Packit 6c4009
		    int ndg = __sendmmsg (pfd[0].fd, reqs, 2, MSG_NOSIGNAL);
Packit 6c4009
		    if (__glibc_likely (ndg == 2))
Packit 6c4009
		      {
Packit 6c4009
			if (reqs[0].msg_len != buflen
Packit 6c4009
			    || reqs[1].msg_len != buflen2)
Packit 6c4009
			  goto fail_sendmmsg;
Packit 6c4009
Packit 6c4009
			pfd[0].events = POLLIN;
Packit 6c4009
			nwritten += 2;
Packit 6c4009
		      }
Packit 6c4009
		    else if (ndg == 1 && reqs[0].msg_len == buflen)
Packit 6c4009
		      goto just_one;
Packit 6c4009
		    else if (ndg < 0 && (errno == EINTR || errno == EAGAIN))
Packit 6c4009
		      goto recompute_resend;
Packit 6c4009
		    else
Packit 6c4009
		      {
Packit 6c4009
#ifndef __ASSUME_SENDMMSG
Packit 6c4009
			if (__glibc_unlikely (have_sendmmsg == 0))
Packit 6c4009
			  {
Packit 6c4009
			    if (ndg < 0 && errno == ENOSYS)
Packit 6c4009
			      {
Packit 6c4009
				have_sendmmsg = -1;
Packit 6c4009
				goto try_send;
Packit 6c4009
			      }
Packit 6c4009
			    have_sendmmsg = 1;
Packit 6c4009
			  }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
		      fail_sendmmsg:
Packit 6c4009
			return close_and_return_error (statp, resplen2);
Packit 6c4009
		      }
Packit 6c4009
		  }
Packit 6c4009
		else
Packit 6c4009
		  {
Packit 6c4009
		    ssize_t sr;
Packit 6c4009
#ifndef __ASSUME_SENDMMSG
Packit 6c4009
		  try_send:
Packit 6c4009
#endif
Packit 6c4009
		    if (nwritten != 0)
Packit 6c4009
		      sr = send (pfd[0].fd, buf2, buflen2, MSG_NOSIGNAL);
Packit 6c4009
		    else
Packit 6c4009
		      sr = send (pfd[0].fd, buf, buflen, MSG_NOSIGNAL);
Packit 6c4009
Packit 6c4009
		    if (sr != (nwritten != 0 ? buflen2 : buflen)) {
Packit 6c4009
		      if (errno == EINTR || errno == EAGAIN)
Packit 6c4009
			goto recompute_resend;
Packit 6c4009
		      return close_and_return_error (statp, resplen2);
Packit 6c4009
		    }
Packit 6c4009
		  just_one:
Packit 6c4009
		    if (nwritten != 0 || buf2 == NULL || single_request)
Packit 6c4009
		      pfd[0].events = POLLIN;
Packit 6c4009
		    else
Packit 6c4009
		      pfd[0].events = POLLIN | POLLOUT;
Packit 6c4009
		    ++nwritten;
Packit 6c4009
		  }
Packit 6c4009
		goto wait;
Packit 6c4009
	} else if (pfd[0].revents & POLLIN) {
Packit 6c4009
		int *thisanssizp;
Packit 6c4009
		u_char **thisansp;
Packit 6c4009
		int *thisresplenp;
Packit 6c4009
Packit 6c4009
		if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
Packit 6c4009
			/* We have not received any responses
Packit 6c4009
			   yet or we only have one response to
Packit 6c4009
			   receive.  */
Packit 6c4009
			thisanssizp = anssizp;
Packit 6c4009
			thisansp = anscp ?: ansp;
Packit 6c4009
			assert (anscp != NULL || ansp2 == NULL);
Packit 6c4009
			thisresplenp = &resplen;
Packit 6c4009
		} else {
Packit 6c4009
			thisanssizp = anssizp2;
Packit 6c4009
			thisansp = ansp2;
Packit 6c4009
			thisresplenp = resplen2;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
		if (*thisanssizp < MAXPACKET
Packit 6c4009
		    /* If the current buffer is not the the static
Packit 6c4009
		       user-supplied buffer then we can reallocate
Packit 6c4009
		       it.  */
Packit 6c4009
		    && (thisansp != NULL && thisansp != ansp)
Packit 6c4009
#ifdef FIONREAD
Packit 6c4009
		    /* Is the size too small?  */
Packit 6c4009
		    && (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
Packit 6c4009
			|| *thisanssizp < *thisresplenp)
Packit 6c4009
#endif
Packit 6c4009
                    ) {
Packit 6c4009
			/* Always allocate MAXPACKET, callers expect
Packit 6c4009
			   this specific size.  */
Packit 6c4009
			u_char *newp = malloc (MAXPACKET);
Packit 6c4009
			if (newp != NULL) {
Packit 6c4009
				*thisanssizp = MAXPACKET;
Packit 6c4009
				*thisansp = newp;
Packit 6c4009
				if (thisansp == ansp2)
Packit 6c4009
				  *ansp2_malloced = 1;
Packit 6c4009
			}
Packit 6c4009
		}
Packit 6c4009
		/* We could end up with truncation if anscp was NULL
Packit 6c4009
		   (not allowed to change caller's buffer) and the
Packit 6c4009
		   response buffer size is too small.  This isn't a
Packit 6c4009
		   reliable way to detect truncation because the ioctl
Packit 6c4009
		   may be an inaccurate report of the UDP message size.
Packit 6c4009
		   Therefore we use this only to issue debug output.
Packit 6c4009
		   To do truncation accurately with UDP we need
Packit 6c4009
		   MSG_TRUNC which is only available on Linux.  We
Packit 6c4009
		   can abstract out the Linux-specific feature in the
Packit 6c4009
		   future to detect truncation.  */
Packit 6c4009
		HEADER *anhp = (HEADER *) *thisansp;
Packit 6c4009
		socklen_t fromlen = sizeof(struct sockaddr_in6);
Packit 6c4009
		assert (sizeof(from) <= fromlen);
Packit 6c4009
		*thisresplenp = recvfrom(pfd[0].fd, (char*)*thisansp,
Packit 6c4009
					 *thisanssizp, 0,
Packit 6c4009
					(struct sockaddr *)&from, &fromlen);
Packit 6c4009
		if (__glibc_unlikely (*thisresplenp <= 0))       {
Packit 6c4009
			if (errno == EINTR || errno == EAGAIN) {
Packit 6c4009
				need_recompute = 1;
Packit 6c4009
				goto wait;
Packit 6c4009
			}
Packit 6c4009
			return close_and_return_error (statp, resplen2);
Packit 6c4009
		}
Packit 6c4009
		*gotsomewhere = 1;
Packit 6c4009
		if (__glibc_unlikely (*thisresplenp < HFIXEDSZ))       {
Packit 6c4009
			/*
Packit 6c4009
			 * Undersized message.
Packit 6c4009
			 */
Packit 6c4009
			*terrno = EMSGSIZE;
Packit 6c4009
			return close_and_return_error (statp, resplen2);
Packit 6c4009
		}
Packit 6c4009
		if ((recvresp1 || hp->id != anhp->id)
Packit 6c4009
		    && (recvresp2 || hp2->id != anhp->id)) {
Packit 6c4009
			/*
Packit 6c4009
			 * response from old query, ignore it.
Packit 6c4009
			 * XXX - potential security hazard could
Packit 6c4009
			 *	 be detected here.
Packit 6c4009
			 */
Packit 6c4009
			goto wait;
Packit 6c4009
		}
Packit Service 340b5b
Packit Service 340b5b
		/* Paranoia check.  Due to the connected UDP socket,
Packit Service 340b5b
		   the kernel has already filtered invalid addresses
Packit Service 340b5b
		   for us.  */
Packit Service 340b5b
		if (!res_ourserver_p(statp, &from))
Packit Service 340b5b
		  goto wait;
Packit Service 340b5b
Packit Service 340b5b
		/* Check for the correct header layout and a matching
Packit Service 340b5b
		   question.  */
Packit Service 340b5b
		if ((recvresp1 || !res_queriesmatch(buf, buf + buflen,
Packit 6c4009
						       *thisansp,
Packit 6c4009
						       *thisansp
Packit 6c4009
						       + *thisanssizp))
Packit 6c4009
		    && (recvresp2 || !res_queriesmatch(buf2, buf2 + buflen2,
Packit 6c4009
						       *thisansp,
Packit 6c4009
						       *thisansp
Packit Service 340b5b
						       + *thisanssizp)))
Packit Service 340b5b
		  goto wait;
Packit Service 340b5b
Packit 6c4009
		if (anhp->rcode == SERVFAIL ||
Packit 6c4009
		    anhp->rcode == NOTIMP ||
Packit 6c4009
		    anhp->rcode == REFUSED) {
Packit 6c4009
		next_ns:
Packit 6c4009
			if (recvresp1 || (buf2 != NULL && recvresp2)) {
Packit 6c4009
			  *resplen2 = 0;
Packit 6c4009
			  return resplen;
Packit 6c4009
			}
Packit 6c4009
			if (buf2 != NULL)
Packit 6c4009
			  {
Packit 6c4009
			    /* No data from the first reply.  */
Packit 6c4009
			    resplen = 0;
Packit 6c4009
			    /* We are waiting for a possible second reply.  */
Packit 6c4009
			    if (hp->id == anhp->id)
Packit 6c4009
			      recvresp1 = 1;
Packit 6c4009
			    else
Packit 6c4009
			      recvresp2 = 1;
Packit 6c4009
Packit 6c4009
			    goto wait;
Packit 6c4009
			  }
Packit 6c4009
Packit 6c4009
			/* don't retry if called from dig */
Packit 6c4009
			if (!statp->pfcode)
Packit 6c4009
			  return close_and_return_error (statp, resplen2);
Packit 6c4009
			__res_iclose(statp, false);
Packit 6c4009
		}
Packit 6c4009
		if (anhp->rcode == NOERROR && anhp->ancount == 0
Packit 6c4009
		    && anhp->aa == 0 && anhp->ra == 0 && anhp->arcount == 0) {
Packit 6c4009
			goto next_ns;
Packit 6c4009
		}
Packit 6c4009
		if (!(statp->options & RES_IGNTC) && anhp->tc) {
Packit 6c4009
			/*
Packit 6c4009
			 * To get the rest of answer,
Packit 6c4009
			 * use TCP with same server.
Packit 6c4009
			 */
Packit 6c4009
			*v_circuit = 1;
Packit 6c4009
			__res_iclose(statp, false);
Packit 6c4009
			// XXX if we have received one reply we could
Packit 6c4009
			// XXX use it and not repeat it over TCP...
Packit 6c4009
			if (resplen2 != NULL)
Packit 6c4009
			  *resplen2 = 0;
Packit 6c4009
			return (1);
Packit 6c4009
		}
Packit 6c4009
		/* Mark which reply we received.  */
Packit 6c4009
		if (recvresp1 == 0 && hp->id == anhp->id)
Packit 6c4009
			recvresp1 = 1;
Packit 6c4009
		else
Packit 6c4009
			recvresp2 = 1;
Packit 6c4009
		/* Repeat waiting if we have a second answer to arrive.  */
Packit 6c4009
		if ((recvresp1 & recvresp2) == 0) {
Packit 6c4009
			if (single_request) {
Packit 6c4009
				pfd[0].events = POLLOUT;
Packit 6c4009
				if (single_request_reopen) {
Packit 6c4009
					__res_iclose (statp, false);
Packit 6c4009
					retval = reopen (statp, terrno, ns);
Packit 6c4009
					if (retval <= 0)
Packit 6c4009
					  {
Packit 6c4009
					    if (resplen2 != NULL)
Packit 6c4009
					      *resplen2 = 0;
Packit 6c4009
					    return retval;
Packit 6c4009
					  }
Packit 6c4009
					pfd[0].fd = EXT(statp).nssocks[ns];
Packit 6c4009
				}
Packit 6c4009
			}
Packit 6c4009
			goto wait;
Packit 6c4009
		}
Packit 6c4009
		/* All is well.  We have received both responses (if
Packit 6c4009
		   two responses were requested).  */
Packit 6c4009
		return (resplen);
Packit 6c4009
	} else if (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))
Packit 6c4009
	  /* Something went wrong.  We can stop trying.  */
Packit 6c4009
	  return close_and_return_error (statp, resplen2);
Packit 6c4009
	else {
Packit 6c4009
		/* poll should not have returned > 0 in this case.  */
Packit 6c4009
		abort ();
Packit 6c4009
	}
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
sock_eq(struct sockaddr_in6 *a1, struct sockaddr_in6 *a2) {
Packit 6c4009
	if (a1->sin6_family == a2->sin6_family) {
Packit 6c4009
		if (a1->sin6_family == AF_INET)
Packit 6c4009
			return ((((struct sockaddr_in *)a1)->sin_port ==
Packit 6c4009
				 ((struct sockaddr_in *)a2)->sin_port) &&
Packit 6c4009
				(((struct sockaddr_in *)a1)->sin_addr.s_addr ==
Packit 6c4009
				 ((struct sockaddr_in *)a2)->sin_addr.s_addr));
Packit 6c4009
		else
Packit 6c4009
			return ((a1->sin6_port == a2->sin6_port) &&
Packit 6c4009
				!memcmp(&a1->sin6_addr, &a2->sin6_addr,
Packit 6c4009
					sizeof (struct in6_addr)));
Packit 6c4009
	}
Packit 6c4009
	if (a1->sin6_family == AF_INET) {
Packit 6c4009
		struct sockaddr_in6 *sap = a1;
Packit 6c4009
		a1 = a2;
Packit 6c4009
		a2 = sap;
Packit 6c4009
	} /* assumes that AF_INET and AF_INET6 are the only possibilities */
Packit 6c4009
	return ((a1->sin6_port == ((struct sockaddr_in *)a2)->sin_port) &&
Packit 6c4009
		IN6_IS_ADDR_V4MAPPED(&a1->sin6_addr) &&
Packit 6c4009
		(a1->sin6_addr.s6_addr32[3] ==
Packit 6c4009
		 ((struct sockaddr_in *)a2)->sin_addr.s_addr));
Packit 6c4009
}