| /* Copyright (C) 2016 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. | 
 |  */ | 
 |  | 
 | #if defined(LIBC_SCCS) && !defined(lint) | 
 | static const char sccsid[] = "@(#)res_send.c	8.1 (Berkeley) 6/4/93"; | 
 | static const char rcsid[] = "$BINDId: res_send.c,v 8.38 2000/03/30 20:16:51 vixie Exp $"; | 
 | #endif /* LIBC_SCCS and not lint */ | 
 |  | 
 | /* | 
 |  * 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.h> | 
 | #include <signal.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 | #include <kernel-features.h> | 
 | #include <libc-internal.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); | 
 | 	} | 
 |  | 
 | #ifdef USE_HOOKS | 
 | 	if (__glibc_unlikely (statp->qhook || statp->rhook))       { | 
 | 		if (anssiz < MAXPACKET && ansp) { | 
 | 			/* Always allocate MAXPACKET, callers expect | 
 | 			   this specific size.  */ | 
 | 			u_char *buf = malloc (MAXPACKET); | 
 | 			if (buf == NULL) | 
 | 				return (-1); | 
 | 			memcpy (buf, ans, HFIXEDSZ); | 
 | 			*ansp = buf; | 
 | 			ans = buf; | 
 | 			anssiz = MAXPACKET; | 
 | 		} | 
 | 	} | 
 | #endif | 
 |  | 
 | 	DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), | 
 | 		(stdout, ";; res_send()\n"), buf, buflen); | 
 | 	v_circuit = ((statp->options & RES_USEVC) | 
 | 		     || buflen > PACKETSZ | 
 | 		     || 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 (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) && | 
 | 	    (statp->options & RES_BLAST) == 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]; | 
 | #endif | 
 | #if defined USE_HOOKS || defined DEBUG | 
 | 		struct sockaddr *nsap = get_nsaddr (statp, ns); | 
 | #endif | 
 |  | 
 | 	    same_ns: | 
 | #ifdef USE_HOOKS | 
 | 		if (__glibc_unlikely (statp->qhook != NULL))       { | 
 | 			int done = 0, loops = 0; | 
 |  | 
 | 			do { | 
 | 				res_sendhookact act; | 
 |  | 
 | 				struct sockaddr_in *nsap4; | 
 | 				nsap4 = (struct sockaddr_in *) nsap; | 
 | 				act = (*statp->qhook)(&nsap4, &buf, &buflen, | 
 | 						      ans, anssiz, &resplen); | 
 | 				nsap = (struct sockaddr_in6 *) nsap4; | 
 | 				switch (act) { | 
 | 				case res_goahead: | 
 | 					done = 1; | 
 | 					break; | 
 | 				case res_nextns: | 
 | 					__res_iclose(statp, false); | 
 | 					goto next_ns; | 
 | 				case res_done: | 
 | 					return (resplen); | 
 | 				case res_modified: | 
 | 					/* give the hook another try */ | 
 | 					if (++loops < 42) /*doug adams*/ | 
 | 						break; | 
 | 					/*FALLTHROUGH*/ | 
 | 				case res_error: | 
 | 					/*FALLTHROUGH*/ | 
 | 				default: | 
 | 					return (-1); | 
 | 				} | 
 | 			} while (!done); | 
 | 		} | 
 | #endif | 
 |  | 
 | 		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); | 
 | 		} | 
 | #ifdef USE_HOOKS | 
 | 		if (__glibc_unlikely (statp->rhook))       { | 
 | 			int done = 0, loops = 0; | 
 |  | 
 | 			do { | 
 | 				res_sendhookact act; | 
 |  | 
 | 				act = (*statp->rhook)((struct sockaddr_in *) | 
 | 						      nsap, buf, buflen, | 
 | 						      ans, anssiz, &resplen); | 
 | 				switch (act) { | 
 | 				case res_goahead: | 
 | 				case res_done: | 
 | 					done = 1; | 
 | 					break; | 
 | 				case res_nextns: | 
 | 					__res_iclose(statp, false); | 
 | 					goto next_ns; | 
 | 				case res_modified: | 
 | 					/* give the hook another try */ | 
 | 					if (++loops < 42) /*doug adams*/ | 
 | 						break; | 
 | 					/*FALLTHROUGH*/ | 
 | 				case res_error: | 
 | 					/*FALLTHROUGH*/ | 
 | 				default: | 
 | 					return (-1); | 
 | 				} | 
 | 			} while (!done); | 
 |  | 
 | 		} | 
 | #endif | 
 | 		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]; | 
 | } | 
 |  | 
 | /* 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 implemetnation 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; | 
 |  | 
 | 	if (resplen2 != NULL) | 
 | 	  *resplen2 = 0; | 
 | 	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, 0); | 
 | 		if (statp->_vcsock < 0) { | 
 | 			*terrno = errno; | 
 | 			Perror(statp, stderr, "socket(vc)", errno); | 
 | 			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); | 
 | 			__res_iclose(statp, false); | 
 | 			return (0); | 
 | 		} | 
 | 		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); | 
 | 		__res_iclose(statp, false); | 
 | 		return (0); | 
 | 	} | 
 | 	/* | 
 | 	 * 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); | 
 | 		__res_iclose(statp, false); | 
 | 		/* | 
 | 		 * 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) { | 
 | 			connreset = 1; | 
 | 			goto same_ns; | 
 | 		} | 
 | 		return (0); | 
 | 	} | 
 | 	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; | 
 | 				__res_iclose(statp, false); | 
 | 				return (0); | 
 | 			} | 
 | 			*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; | 
 | 		__res_iclose(statp, false); | 
 | 		return (0); | 
 | 	} | 
 |  | 
 | 	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); | 
 | 		__res_iclose(statp, false); | 
 | 		return (0); | 
 | 	} | 
 | 	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, 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, 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. | 
 | 		 */ | 
 | 		if (connect(EXT(statp).nssocks[ns], nsap, slen) < 0) { | 
 | 			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) | 
 | 		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; | 
 | 	if (resplen2 != NULL) | 
 | 	  *resplen2 = 0; | 
 |  wait: | 
 | 	if (need_recompute) { | 
 | 	recompute_resend: | 
 | 		evNowTime(&now); | 
 | 		if (evCmpTime(finish, now) <= 0) { | 
 | 		poll_err_out: | 
 | 			Perror(statp, stderr, "poll", errno); | 
 | 		err_out: | 
 | 			__res_iclose(statp, false); | 
 | 			return (0); | 
 | 		} | 
 | 		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; | 
 | 		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); | 
 | 			goto err_out; | 
 | 		      } | 
 | 		  } | 
 | 		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); | 
 | 		      goto err_out; | 
 | 		    } | 
 | 		  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); | 
 | 			goto err_out; | 
 | 		} | 
 | 		*gotsomewhere = 1; | 
 | 		if (__glibc_unlikely (*thisresplenp < HFIXEDSZ))       { | 
 | 			/* | 
 | 			 * Undersized message. | 
 | 			 */ | 
 | 			Dprint(statp->options & RES_DEBUG, | 
 | 			       (stdout, ";; undersized: %d\n", | 
 | 				*thisresplenp)); | 
 | 			*terrno = EMSGSIZE; | 
 | 			goto err_out; | 
 | 		} | 
 | 		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; | 
 | 		} | 
 | #ifdef RES_USE_EDNS0 | 
 | 		if (anhp->rcode == FORMERR | 
 | 		    && (statp->options & RES_USE_EDNS0) != 0U) { | 
 | 			/* | 
 | 			 * Do not retry if the server does not understand | 
 | 			 * EDNS0.  The case has to be captured here, as | 
 | 			 * FORMERR packet do not carry query section, hence | 
 | 			 * res_queriesmatch() returns 0. | 
 | 			 */ | 
 | 			DprintQ(statp->options & RES_DEBUG, | 
 | 				(stdout, | 
 | 				 "server rejected query with EDNS0:\n"), | 
 | 				*thisansp, | 
 | 				(*thisresplenp > *thisanssizp) | 
 | 				? *thisanssizp : *thisresplenp); | 
 | 			/* record the error */ | 
 | 			statp->_flags |= RES_F_EDNS0ERR; | 
 | 			goto err_out; | 
 | 	} | 
 | #endif | 
 | 		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; | 
 | 			  } | 
 |  | 
 | 			__res_iclose(statp, false); | 
 | 			/* don't retry if called from dig */ | 
 | 			if (!statp->pfcode) | 
 | 				return (0); | 
 | 		} | 
 | 		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... | 
 | 			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) | 
 | 						return retval; | 
 | 					pfd[0].fd = EXT(statp).nssocks[ns]; | 
 | 				} | 
 | 			} | 
 | 			goto wait; | 
 | 		} | 
 | 		/* | 
 | 		 * All is well, or the error is fatal.  Signal that the | 
 | 		 * next nameserver ought not be tried. | 
 | 		 */ | 
 | 		return (resplen); | 
 | 	} else if (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { | 
 | 		/* Something went wrong.  We can stop trying.  */ | 
 | 		goto err_out; | 
 | 	} | 
 | 	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)); | 
 | } |