res_send.c revision 156952
1156952Sume/*
2156952Sume * Copyright (c) 1985, 1989, 1993
3156952Sume *    The Regents of the University of California.  All rights reserved.
4156952Sume *
5156952Sume * Redistribution and use in source and binary forms, with or without
6156952Sume * modification, are permitted provided that the following conditions
7156952Sume * are met:
8156952Sume * 1. Redistributions of source code must retain the above copyright
9156952Sume *    notice, this list of conditions and the following disclaimer.
10156952Sume * 2. Redistributions in binary form must reproduce the above copyright
11156952Sume *    notice, this list of conditions and the following disclaimer in the
12156952Sume *    documentation and/or other materials provided with the distribution.
13156952Sume * 3. All advertising materials mentioning features or use of this software
14156952Sume *    must display the following acknowledgement:
15156952Sume * 	This product includes software developed by the University of
16156952Sume * 	California, Berkeley and its contributors.
17156952Sume * 4. Neither the name of the University nor the names of its contributors
18156952Sume *    may be used to endorse or promote products derived from this software
19156952Sume *    without specific prior written permission.
20156952Sume *
21156952Sume * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22156952Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23156952Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24156952Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25156952Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26156952Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27156952Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28156952Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29156952Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30156952Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31156952Sume * SUCH DAMAGE.
32156952Sume */
33156952Sume
34156952Sume/*
35156952Sume * Portions Copyright (c) 1993 by Digital Equipment Corporation.
36156952Sume *
37156952Sume * Permission to use, copy, modify, and distribute this software for any
38156952Sume * purpose with or without fee is hereby granted, provided that the above
39156952Sume * copyright notice and this permission notice appear in all copies, and that
40156952Sume * the name of Digital Equipment Corporation not be used in advertising or
41156952Sume * publicity pertaining to distribution of the document or software without
42156952Sume * specific, written prior permission.
43156952Sume *
44156952Sume * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
45156952Sume * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
46156952Sume * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
47156952Sume * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
48156952Sume * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
49156952Sume * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
50156952Sume * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51156952Sume * SOFTWARE.
52156952Sume */
53156952Sume
54156952Sume/*
55156952Sume * Copyright (c) 2005 by Internet Systems Consortium, Inc. ("ISC")
56156952Sume * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
57156952Sume *
58156952Sume * Permission to use, copy, modify, and distribute this software for any
59156952Sume * purpose with or without fee is hereby granted, provided that the above
60156952Sume * copyright notice and this permission notice appear in all copies.
61156952Sume *
62156952Sume * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
63156952Sume * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
64156952Sume * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
65156952Sume * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
66156952Sume * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
67156952Sume * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
68156952Sume * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
69156952Sume */
70156952Sume
71156952Sume#if defined(LIBC_SCCS) && !defined(lint)
72156952Sumestatic const char sccsid[] = "@(#)res_send.c	8.1 (Berkeley) 6/4/93";
73156952Sumestatic const char rcsid[] = "$Id: res_send.c,v 1.5.2.2.4.7 2005/08/15 02:04:41 marka Exp $";
74156952Sume#endif /* LIBC_SCCS and not lint */
75156952Sume
76156952Sume/*
77156952Sume * Send query to name server and wait for reply.
78156952Sume */
79156952Sume
80156952Sume#include "port_before.h"
81156952Sume#include "fd_setsize.h"
82156952Sume
83156952Sume#include <sys/types.h>
84156952Sume#include <sys/param.h>
85156952Sume#include <sys/time.h>
86156952Sume#include <sys/socket.h>
87156952Sume#include <sys/uio.h>
88156952Sume
89156952Sume#include <netinet/in.h>
90156952Sume#include <arpa/nameser.h>
91156952Sume#include <arpa/inet.h>
92156952Sume
93156952Sume#include <errno.h>
94156952Sume#include <netdb.h>
95156952Sume#include <resolv.h>
96156952Sume#include <signal.h>
97156952Sume#include <stdio.h>
98156952Sume#include <stdlib.h>
99156952Sume#include <string.h>
100156952Sume#include <unistd.h>
101156952Sume
102156952Sume#include <isc/eventlib.h>
103156952Sume
104156952Sume#include "port_after.h"
105156952Sume
106156952Sume#ifdef USE_POLL
107156952Sume#ifdef HAVE_STROPTS_H
108156952Sume#include <stropts.h>
109156952Sume#endif
110156952Sume#include <poll.h>
111156952Sume#endif /* USE_POLL */
112156952Sume
113156952Sume/* Options.  Leave them on. */
114156952Sume#define DEBUG
115156952Sume#include "res_debug.h"
116156952Sume#include "res_private.h"
117156952Sume
118156952Sume#define EXT(res) ((res)->_u._ext)
119156952Sume
120156952Sume#ifndef USE_POLL
121156952Sumestatic const int highestFD = FD_SETSIZE - 1;
122156952Sume#else
123156952Sumestatic int highestFD = 0;
124156952Sume#endif
125156952Sume
126156952Sume/* Forward. */
127156952Sume
128156952Sumestatic int		get_salen __P((const struct sockaddr *));
129156952Sumestatic struct sockaddr * get_nsaddr __P((res_state, size_t));
130156952Sumestatic int		send_vc(res_state, const u_char *, int,
131156952Sume				u_char *, int, int *, int);
132156952Sumestatic int		send_dg(res_state, const u_char *, int,
133156952Sume				u_char *, int, int *, int,
134156952Sume				int *, int *);
135156952Sumestatic void		Aerror(const res_state, FILE *, const char *, int,
136156952Sume			       const struct sockaddr *, int);
137156952Sumestatic void		Perror(const res_state, FILE *, const char *, int);
138156952Sumestatic int		sock_eq(struct sockaddr *, struct sockaddr *);
139156952Sume#if defined(NEED_PSELECT) && !defined(USE_POLL)
140156952Sumestatic int		pselect(int, void *, void *, void *,
141156952Sume				struct timespec *,
142156952Sume				const sigset_t *);
143156952Sume#endif
144156952Sumevoid res_pquery(const res_state, const u_char *, int, FILE *);
145156952Sume
146156952Sumestatic const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
147156952Sume
148156952Sume/* Public. */
149156952Sume
150156952Sume/* int
151156952Sume * res_isourserver(ina)
152156952Sume *	looks up "ina" in _res.ns_addr_list[]
153156952Sume * returns:
154156952Sume *	0  : not found
155156952Sume *	>0 : found
156156952Sume * author:
157156952Sume *	paul vixie, 29may94
158156952Sume */
159156952Sumeint
160156952Sumeres_ourserver_p(const res_state statp, const struct sockaddr *sa) {
161156952Sume	const struct sockaddr_in *inp, *srv;
162156952Sume	const struct sockaddr_in6 *in6p, *srv6;
163156952Sume	int ns;
164156952Sume
165156952Sume	switch (sa->sa_family) {
166156952Sume	case AF_INET:
167156952Sume		inp = (const struct sockaddr_in *)sa;
168156952Sume		for (ns = 0;  ns < statp->nscount;  ns++) {
169156952Sume			srv = (struct sockaddr_in *)get_nsaddr(statp, ns);
170156952Sume			if (srv->sin_family == inp->sin_family &&
171156952Sume			    srv->sin_port == inp->sin_port &&
172156952Sume			    (srv->sin_addr.s_addr == INADDR_ANY ||
173156952Sume			     srv->sin_addr.s_addr == inp->sin_addr.s_addr))
174156952Sume				return (1);
175156952Sume		}
176156952Sume		break;
177156952Sume	case AF_INET6:
178156952Sume		if (EXT(statp).ext == NULL)
179156952Sume			break;
180156952Sume		in6p = (const struct sockaddr_in6 *)sa;
181156952Sume		for (ns = 0;  ns < statp->nscount;  ns++) {
182156952Sume			srv6 = (struct sockaddr_in6 *)get_nsaddr(statp, ns);
183156952Sume			if (srv6->sin6_family == in6p->sin6_family &&
184156952Sume			    srv6->sin6_port == in6p->sin6_port &&
185156952Sume#ifdef HAVE_SIN6_SCOPE_ID
186156952Sume			    (srv6->sin6_scope_id == 0 ||
187156952Sume			     srv6->sin6_scope_id == in6p->sin6_scope_id) &&
188156952Sume#endif
189156952Sume			    (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) ||
190156952Sume			     IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr)))
191156952Sume				return (1);
192156952Sume		}
193156952Sume		break;
194156952Sume	default:
195156952Sume		break;
196156952Sume	}
197156952Sume	return (0);
198156952Sume}
199156952Sume
200156952Sume/* int
201156952Sume * res_nameinquery(name, type, class, buf, eom)
202156952Sume *	look for (name,type,class) in the query section of packet (buf,eom)
203156952Sume * requires:
204156952Sume *	buf + HFIXEDSZ <= eom
205156952Sume * returns:
206156952Sume *	-1 : format error
207156952Sume *	0  : not found
208156952Sume *	>0 : found
209156952Sume * author:
210156952Sume *	paul vixie, 29may94
211156952Sume */
212156952Sumeint
213156952Sumeres_nameinquery(const char *name, int type, int class,
214156952Sume		const u_char *buf, const u_char *eom)
215156952Sume{
216156952Sume	const u_char *cp = buf + HFIXEDSZ;
217156952Sume	int qdcount = ntohs(((const HEADER*)buf)->qdcount);
218156952Sume
219156952Sume	while (qdcount-- > 0) {
220156952Sume		char tname[MAXDNAME+1];
221156952Sume		int n, ttype, tclass;
222156952Sume
223156952Sume		n = dn_expand(buf, eom, cp, tname, sizeof tname);
224156952Sume		if (n < 0)
225156952Sume			return (-1);
226156952Sume		cp += n;
227156952Sume		if (cp + 2 * INT16SZ > eom)
228156952Sume			return (-1);
229156952Sume		ttype = ns_get16(cp); cp += INT16SZ;
230156952Sume		tclass = ns_get16(cp); cp += INT16SZ;
231156952Sume		if (ttype == type && tclass == class &&
232156952Sume		    ns_samename(tname, name) == 1)
233156952Sume			return (1);
234156952Sume	}
235156952Sume	return (0);
236156952Sume}
237156952Sume
238156952Sume/* int
239156952Sume * res_queriesmatch(buf1, eom1, buf2, eom2)
240156952Sume *	is there a 1:1 mapping of (name,type,class)
241156952Sume *	in (buf1,eom1) and (buf2,eom2)?
242156952Sume * returns:
243156952Sume *	-1 : format error
244156952Sume *	0  : not a 1:1 mapping
245156952Sume *	>0 : is a 1:1 mapping
246156952Sume * author:
247156952Sume *	paul vixie, 29may94
248156952Sume */
249156952Sumeint
250156952Sumeres_queriesmatch(const u_char *buf1, const u_char *eom1,
251156952Sume		 const u_char *buf2, const u_char *eom2)
252156952Sume{
253156952Sume	const u_char *cp = buf1 + HFIXEDSZ;
254156952Sume	int qdcount = ntohs(((const HEADER*)buf1)->qdcount);
255156952Sume
256156952Sume	if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2)
257156952Sume		return (-1);
258156952Sume
259156952Sume	/*
260156952Sume	 * Only header section present in replies to
261156952Sume	 * dynamic update packets.
262156952Sume	 */
263156952Sume	if ((((const HEADER *)buf1)->opcode == ns_o_update) &&
264156952Sume	    (((const HEADER *)buf2)->opcode == ns_o_update))
265156952Sume		return (1);
266156952Sume
267156952Sume	if (qdcount != ntohs(((const HEADER*)buf2)->qdcount))
268156952Sume		return (0);
269156952Sume	while (qdcount-- > 0) {
270156952Sume		char tname[MAXDNAME+1];
271156952Sume		int n, ttype, tclass;
272156952Sume
273156952Sume		n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
274156952Sume		if (n < 0)
275156952Sume			return (-1);
276156952Sume		cp += n;
277156952Sume		if (cp + 2 * INT16SZ > eom1)
278156952Sume			return (-1);
279156952Sume		ttype = ns_get16(cp);	cp += INT16SZ;
280156952Sume		tclass = ns_get16(cp); cp += INT16SZ;
281156952Sume		if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
282156952Sume			return (0);
283156952Sume	}
284156952Sume	return (1);
285156952Sume}
286156952Sume
287156952Sumeint
288156952Sumeres_nsend(res_state statp,
289156952Sume	  const u_char *buf, int buflen, u_char *ans, int anssiz)
290156952Sume{
291156952Sume	int gotsomewhere, terrno, try, v_circuit, resplen, ns, n;
292156952Sume	char abuf[NI_MAXHOST];
293156952Sume
294156952Sume#ifdef USE_POLL
295156952Sume	highestFD = sysconf(_SC_OPEN_MAX) - 1;
296156952Sume#endif
297156952Sume
298156952Sume	if (statp->nscount == 0) {
299156952Sume		errno = ESRCH;
300156952Sume		return (-1);
301156952Sume	}
302156952Sume	if (anssiz < HFIXEDSZ) {
303156952Sume		errno = EINVAL;
304156952Sume		return (-1);
305156952Sume	}
306156952Sume	DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
307156952Sume		(stdout, ";; res_send()\n"), buf, buflen);
308156952Sume	v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
309156952Sume	gotsomewhere = 0;
310156952Sume	terrno = ETIMEDOUT;
311156952Sume
312156952Sume	/*
313156952Sume	 * If the ns_addr_list in the resolver context has changed, then
314156952Sume	 * invalidate our cached copy and the associated timing data.
315156952Sume	 */
316156952Sume	if (EXT(statp).nscount != 0) {
317156952Sume		int needclose = 0;
318156952Sume		struct sockaddr_storage peer;
319156952Sume		ISC_SOCKLEN_T peerlen;
320156952Sume
321156952Sume		if (EXT(statp).nscount != statp->nscount)
322156952Sume			needclose++;
323156952Sume		else
324156952Sume			for (ns = 0; ns < statp->nscount; ns++) {
325156952Sume				if (statp->nsaddr_list[ns].sin_family &&
326156952Sume				    !sock_eq((struct sockaddr *)&statp->nsaddr_list[ns],
327156952Sume					     (struct sockaddr *)&EXT(statp).ext->nsaddrs[ns])) {
328156952Sume					needclose++;
329156952Sume					break;
330156952Sume				}
331156952Sume
332156952Sume				if (EXT(statp).nssocks[ns] == -1)
333156952Sume					continue;
334156952Sume				peerlen = sizeof(peer);
335156952Sume				if (getsockname(EXT(statp).nssocks[ns],
336156952Sume				    (struct sockaddr *)&peer, &peerlen) < 0) {
337156952Sume					needclose++;
338156952Sume					break;
339156952Sume				}
340156952Sume				if (!sock_eq((struct sockaddr *)&peer,
341156952Sume				    get_nsaddr(statp, ns))) {
342156952Sume					needclose++;
343156952Sume					break;
344156952Sume				}
345156952Sume			}
346156952Sume		if (needclose) {
347156952Sume			res_nclose(statp);
348156952Sume			EXT(statp).nscount = 0;
349156952Sume		}
350156952Sume	}
351156952Sume
352156952Sume	/*
353156952Sume	 * Maybe initialize our private copy of the ns_addr_list.
354156952Sume	 */
355156952Sume	if (EXT(statp).nscount == 0) {
356156952Sume		for (ns = 0; ns < statp->nscount; ns++) {
357156952Sume			EXT(statp).nstimes[ns] = RES_MAXTIME;
358156952Sume			EXT(statp).nssocks[ns] = -1;
359156952Sume			if (!statp->nsaddr_list[ns].sin_family)
360156952Sume				continue;
361156952Sume			EXT(statp).ext->nsaddrs[ns].sin =
362156952Sume				 statp->nsaddr_list[ns];
363156952Sume		}
364156952Sume		EXT(statp).nscount = statp->nscount;
365156952Sume	}
366156952Sume
367156952Sume	/*
368156952Sume	 * Some resolvers want to even out the load on their nameservers.
369156952Sume	 * Note that RES_BLAST overrides RES_ROTATE.
370156952Sume	 */
371156952Sume	if ((statp->options & RES_ROTATE) != 0U &&
372156952Sume	    (statp->options & RES_BLAST) == 0U) {
373156952Sume		union res_sockaddr_union inu;
374156952Sume		struct sockaddr_in ina;
375156952Sume		int lastns = statp->nscount - 1;
376156952Sume		int fd;
377156952Sume		u_int16_t nstime;
378156952Sume
379156952Sume		if (EXT(statp).ext != NULL)
380156952Sume			inu = EXT(statp).ext->nsaddrs[0];
381156952Sume		ina = statp->nsaddr_list[0];
382156952Sume		fd = EXT(statp).nssocks[0];
383156952Sume		nstime = EXT(statp).nstimes[0];
384156952Sume		for (ns = 0; ns < lastns; ns++) {
385156952Sume			if (EXT(statp).ext != NULL)
386156952Sume                                EXT(statp).ext->nsaddrs[ns] =
387156952Sume					EXT(statp).ext->nsaddrs[ns + 1];
388156952Sume			statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1];
389156952Sume			EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1];
390156952Sume			EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1];
391156952Sume		}
392156952Sume		if (EXT(statp).ext != NULL)
393156952Sume			EXT(statp).ext->nsaddrs[lastns] = inu;
394156952Sume		statp->nsaddr_list[lastns] = ina;
395156952Sume		EXT(statp).nssocks[lastns] = fd;
396156952Sume		EXT(statp).nstimes[lastns] = nstime;
397156952Sume	}
398156952Sume
399156952Sume	/*
400156952Sume	 * Send request, RETRY times, or until successful.
401156952Sume	 */
402156952Sume	for (try = 0; try < statp->retry; try++) {
403156952Sume	    for (ns = 0; ns < statp->nscount; ns++) {
404156952Sume		struct sockaddr *nsap;
405156952Sume		int nsaplen;
406156952Sume		nsap = get_nsaddr(statp, ns);
407156952Sume		nsaplen = get_salen(nsap);
408156952Sume		statp->_flags &= ~RES_F_LASTMASK;
409156952Sume		statp->_flags |= (ns << RES_F_LASTSHIFT);
410156952Sume same_ns:
411156952Sume		if (statp->qhook) {
412156952Sume			int done = 0, loops = 0;
413156952Sume
414156952Sume			do {
415156952Sume				res_sendhookact act;
416156952Sume
417156952Sume				act = (*statp->qhook)(&nsap, &buf, &buflen,
418156952Sume						      ans, anssiz, &resplen);
419156952Sume				switch (act) {
420156952Sume				case res_goahead:
421156952Sume					done = 1;
422156952Sume					break;
423156952Sume				case res_nextns:
424156952Sume					res_nclose(statp);
425156952Sume					goto next_ns;
426156952Sume				case res_done:
427156952Sume					return (resplen);
428156952Sume				case res_modified:
429156952Sume					/* give the hook another try */
430156952Sume					if (++loops < 42) /*doug adams*/
431156952Sume						break;
432156952Sume					/*FALLTHROUGH*/
433156952Sume				case res_error:
434156952Sume					/*FALLTHROUGH*/
435156952Sume				default:
436156952Sume					goto fail;
437156952Sume				}
438156952Sume			} while (!done);
439156952Sume		}
440156952Sume
441156952Sume		Dprint(((statp->options & RES_DEBUG) &&
442156952Sume			getnameinfo(nsap, nsaplen, abuf, sizeof(abuf),
443156952Sume				    NULL, 0, niflags) == 0),
444156952Sume		       (stdout, ";; Querying server (# %d) address = %s\n",
445156952Sume			ns + 1, abuf));
446156952Sume
447156952Sume
448156952Sume		if (v_circuit) {
449156952Sume			/* Use VC; at most one attempt per server. */
450156952Sume			try = statp->retry;
451156952Sume			n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
452156952Sume				    ns);
453156952Sume			if (n < 0)
454156952Sume				goto fail;
455156952Sume			if (n == 0)
456156952Sume				goto next_ns;
457156952Sume			resplen = n;
458156952Sume		} else {
459156952Sume			/* Use datagrams. */
460156952Sume			n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
461156952Sume				    ns, &v_circuit, &gotsomewhere);
462156952Sume			if (n < 0)
463156952Sume				goto fail;
464156952Sume			if (n == 0)
465156952Sume				goto next_ns;
466156952Sume			if (v_circuit)
467156952Sume				goto same_ns;
468156952Sume			resplen = n;
469156952Sume		}
470156952Sume
471156952Sume		Dprint((statp->options & RES_DEBUG) ||
472156952Sume		       ((statp->pfcode & RES_PRF_REPLY) &&
473156952Sume			(statp->pfcode & RES_PRF_HEAD1)),
474156952Sume		       (stdout, ";; got answer:\n"));
475156952Sume
476156952Sume		DprintQ((statp->options & RES_DEBUG) ||
477156952Sume			(statp->pfcode & RES_PRF_REPLY),
478156952Sume			(stdout, "%s", ""),
479156952Sume			ans, (resplen > anssiz) ? anssiz : resplen);
480156952Sume
481156952Sume		/*
482156952Sume		 * If we have temporarily opened a virtual circuit,
483156952Sume		 * or if we haven't been asked to keep a socket open,
484156952Sume		 * close the socket.
485156952Sume		 */
486156952Sume		if ((v_circuit && (statp->options & RES_USEVC) == 0U) ||
487156952Sume		    (statp->options & RES_STAYOPEN) == 0U) {
488156952Sume			res_nclose(statp);
489156952Sume		}
490156952Sume		if (statp->rhook) {
491156952Sume			int done = 0, loops = 0;
492156952Sume
493156952Sume			do {
494156952Sume				res_sendhookact act;
495156952Sume
496156952Sume				act = (*statp->rhook)(nsap, buf, buflen,
497156952Sume						      ans, anssiz, &resplen);
498156952Sume				switch (act) {
499156952Sume				case res_goahead:
500156952Sume				case res_done:
501156952Sume					done = 1;
502156952Sume					break;
503156952Sume				case res_nextns:
504156952Sume					res_nclose(statp);
505156952Sume					goto next_ns;
506156952Sume				case res_modified:
507156952Sume					/* give the hook another try */
508156952Sume					if (++loops < 42) /*doug adams*/
509156952Sume						break;
510156952Sume					/*FALLTHROUGH*/
511156952Sume				case res_error:
512156952Sume					/*FALLTHROUGH*/
513156952Sume				default:
514156952Sume					goto fail;
515156952Sume				}
516156952Sume			} while (!done);
517156952Sume
518156952Sume		}
519156952Sume		return (resplen);
520156952Sume next_ns: ;
521156952Sume	   } /*foreach ns*/
522156952Sume	} /*foreach retry*/
523156952Sume	res_nclose(statp);
524156952Sume	if (!v_circuit) {
525156952Sume		if (!gotsomewhere)
526156952Sume			errno = ECONNREFUSED;	/* no nameservers found */
527156952Sume		else
528156952Sume			errno = ETIMEDOUT;	/* no answer obtained */
529156952Sume	} else
530156952Sume		errno = terrno;
531156952Sume	return (-1);
532156952Sume fail:
533156952Sume	res_nclose(statp);
534156952Sume	return (-1);
535156952Sume}
536156952Sume
537156952Sume/* Private */
538156952Sume
539156952Sumestatic int
540156952Sumeget_salen(sa)
541156952Sume	const struct sockaddr *sa;
542156952Sume{
543156952Sume
544156952Sume#ifdef HAVE_SA_LEN
545156952Sume	/* There are people do not set sa_len.  Be forgiving to them. */
546156952Sume	if (sa->sa_len)
547156952Sume		return (sa->sa_len);
548156952Sume#endif
549156952Sume
550156952Sume	if (sa->sa_family == AF_INET)
551156952Sume		return (sizeof(struct sockaddr_in));
552156952Sume	else if (sa->sa_family == AF_INET6)
553156952Sume		return (sizeof(struct sockaddr_in6));
554156952Sume	else
555156952Sume		return (0);	/* unknown, die on connect */
556156952Sume}
557156952Sume
558156952Sume/*
559156952Sume * pick appropriate nsaddr_list for use.  see res_init() for initialization.
560156952Sume */
561156952Sumestatic struct sockaddr *
562156952Sumeget_nsaddr(statp, n)
563156952Sume	res_state statp;
564156952Sume	size_t n;
565156952Sume{
566156952Sume
567156952Sume	if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) {
568156952Sume		/*
569156952Sume		 * - EXT(statp).ext->nsaddrs[n] holds an address that is larger
570156952Sume		 *   than struct sockaddr, and
571156952Sume		 * - user code did not update statp->nsaddr_list[n].
572156952Sume		 */
573156952Sume		return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n];
574156952Sume	} else {
575156952Sume		/*
576156952Sume		 * - user code updated statp->nsaddr_list[n], or
577156952Sume		 * - statp->nsaddr_list[n] has the same content as
578156952Sume		 *   EXT(statp).ext->nsaddrs[n].
579156952Sume		 */
580156952Sume		return (struct sockaddr *)(void *)&statp->nsaddr_list[n];
581156952Sume	}
582156952Sume}
583156952Sume
584156952Sumestatic int
585156952Sumesend_vc(res_state statp,
586156952Sume	const u_char *buf, int buflen, u_char *ans, int anssiz,
587156952Sume	int *terrno, int ns)
588156952Sume{
589156952Sume	const HEADER *hp = (const HEADER *) buf;
590156952Sume	HEADER *anhp = (HEADER *) ans;
591156952Sume	struct sockaddr *nsap;
592156952Sume	int nsaplen;
593156952Sume	int truncating, connreset, resplen, n;
594156952Sume	struct iovec iov[2];
595156952Sume	u_short len;
596156952Sume	u_char *cp;
597156952Sume	void *tmp;
598156952Sume
599156952Sume	nsap = get_nsaddr(statp, ns);
600156952Sume	nsaplen = get_salen(nsap);
601156952Sume
602156952Sume	connreset = 0;
603156952Sume same_ns:
604156952Sume	truncating = 0;
605156952Sume
606156952Sume	/* Are we still talking to whom we want to talk to? */
607156952Sume	if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
608156952Sume		struct sockaddr_storage peer;
609156952Sume		ISC_SOCKLEN_T size = sizeof peer;
610156952Sume
611156952Sume		if (getpeername(statp->_vcsock,
612156952Sume				(struct sockaddr *)&peer, &size) < 0 ||
613156952Sume		    !sock_eq((struct sockaddr *)&peer, nsap)) {
614156952Sume			res_nclose(statp);
615156952Sume			statp->_flags &= ~RES_F_VC;
616156952Sume		}
617156952Sume	}
618156952Sume
619156952Sume	if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
620156952Sume		if (statp->_vcsock >= 0)
621156952Sume			res_nclose(statp);
622156952Sume
623156952Sume		statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0);
624156952Sume		if (statp->_vcsock > highestFD) {
625156952Sume			res_nclose(statp);
626156952Sume			errno = ENOTSOCK;
627156952Sume		}
628156952Sume		if (statp->_vcsock < 0) {
629156952Sume			switch (errno) {
630156952Sume			case EPROTONOSUPPORT:
631156952Sume#ifdef EPFNOSUPPORT
632156952Sume			case EPFNOSUPPORT:
633156952Sume#endif
634156952Sume			case EAFNOSUPPORT:
635156952Sume				Perror(statp, stderr, "socket(vc)", errno);
636156952Sume				return (0);
637156952Sume			default:
638156952Sume				*terrno = errno;
639156952Sume				Perror(statp, stderr, "socket(vc)", errno);
640156952Sume				return (-1);
641156952Sume			}
642156952Sume		}
643156952Sume		errno = 0;
644156952Sume		if (connect(statp->_vcsock, nsap, nsaplen) < 0) {
645156952Sume			*terrno = errno;
646156952Sume			Aerror(statp, stderr, "connect/vc", errno, nsap,
647156952Sume			    nsaplen);
648156952Sume			res_nclose(statp);
649156952Sume			return (0);
650156952Sume		}
651156952Sume		statp->_flags |= RES_F_VC;
652156952Sume	}
653156952Sume
654156952Sume	/*
655156952Sume	 * Send length & message
656156952Sume	 */
657156952Sume	ns_put16((u_short)buflen, (u_char*)&len);
658156952Sume	iov[0] = evConsIovec(&len, INT16SZ);
659156952Sume	DE_CONST(buf, tmp);
660156952Sume	iov[1] = evConsIovec(tmp, buflen);
661156952Sume	if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
662156952Sume		*terrno = errno;
663156952Sume		Perror(statp, stderr, "write failed", errno);
664156952Sume		res_nclose(statp);
665156952Sume		return (0);
666156952Sume	}
667156952Sume	/*
668156952Sume	 * Receive length & response
669156952Sume	 */
670156952Sume read_len:
671156952Sume	cp = ans;
672156952Sume	len = INT16SZ;
673156952Sume	while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) {
674156952Sume		cp += n;
675156952Sume		if ((len -= n) == 0)
676156952Sume			break;
677156952Sume	}
678156952Sume	if (n <= 0) {
679156952Sume		*terrno = errno;
680156952Sume		Perror(statp, stderr, "read failed", errno);
681156952Sume		res_nclose(statp);
682156952Sume		/*
683156952Sume		 * A long running process might get its TCP
684156952Sume		 * connection reset if the remote server was
685156952Sume		 * restarted.  Requery the server instead of
686156952Sume		 * trying a new one.  When there is only one
687156952Sume		 * server, this means that a query might work
688156952Sume		 * instead of failing.  We only allow one reset
689156952Sume		 * per query to prevent looping.
690156952Sume		 */
691156952Sume		if (*terrno == ECONNRESET && !connreset) {
692156952Sume			connreset = 1;
693156952Sume			res_nclose(statp);
694156952Sume			goto same_ns;
695156952Sume		}
696156952Sume		res_nclose(statp);
697156952Sume		return (0);
698156952Sume	}
699156952Sume	resplen = ns_get16(ans);
700156952Sume	if (resplen > anssiz) {
701156952Sume		Dprint(statp->options & RES_DEBUG,
702156952Sume		       (stdout, ";; response truncated\n")
703156952Sume		       );
704156952Sume		truncating = 1;
705156952Sume		len = anssiz;
706156952Sume	} else
707156952Sume		len = resplen;
708156952Sume	if (len < HFIXEDSZ) {
709156952Sume		/*
710156952Sume		 * Undersized message.
711156952Sume		 */
712156952Sume		Dprint(statp->options & RES_DEBUG,
713156952Sume		       (stdout, ";; undersized: %d\n", len));
714156952Sume		*terrno = EMSGSIZE;
715156952Sume		res_nclose(statp);
716156952Sume		return (0);
717156952Sume	}
718156952Sume	cp = ans;
719156952Sume	while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
720156952Sume		cp += n;
721156952Sume		len -= n;
722156952Sume	}
723156952Sume	if (n <= 0) {
724156952Sume		*terrno = errno;
725156952Sume		Perror(statp, stderr, "read(vc)", errno);
726156952Sume		res_nclose(statp);
727156952Sume		return (0);
728156952Sume	}
729156952Sume	if (truncating) {
730156952Sume		/*
731156952Sume		 * Flush rest of answer so connection stays in synch.
732156952Sume		 */
733156952Sume		anhp->tc = 1;
734156952Sume		len = resplen - anssiz;
735156952Sume		while (len != 0) {
736156952Sume			char junk[PACKETSZ];
737156952Sume
738156952Sume			n = read(statp->_vcsock, junk,
739156952Sume				 (len > sizeof junk) ? sizeof junk : len);
740156952Sume			if (n > 0)
741156952Sume				len -= n;
742156952Sume			else
743156952Sume				break;
744156952Sume		}
745156952Sume	}
746156952Sume	/*
747156952Sume	 * If the calling applicating has bailed out of
748156952Sume	 * a previous call and failed to arrange to have
749156952Sume	 * the circuit closed or the server has got
750156952Sume	 * itself confused, then drop the packet and
751156952Sume	 * wait for the correct one.
752156952Sume	 */
753156952Sume	if (hp->id != anhp->id) {
754156952Sume		DprintQ((statp->options & RES_DEBUG) ||
755156952Sume			(statp->pfcode & RES_PRF_REPLY),
756156952Sume			(stdout, ";; old answer (unexpected):\n"),
757156952Sume			ans, (resplen > anssiz) ? anssiz: resplen);
758156952Sume		goto read_len;
759156952Sume	}
760156952Sume
761156952Sume	/*
762156952Sume	 * All is well, or the error is fatal.  Signal that the
763156952Sume	 * next nameserver ought not be tried.
764156952Sume	 */
765156952Sume	return (resplen);
766156952Sume}
767156952Sume
768156952Sumestatic int
769156952Sumesend_dg(res_state statp,
770156952Sume	const u_char *buf, int buflen, u_char *ans, int anssiz,
771156952Sume	int *terrno, int ns, int *v_circuit, int *gotsomewhere)
772156952Sume{
773156952Sume	const HEADER *hp = (const HEADER *) buf;
774156952Sume	HEADER *anhp = (HEADER *) ans;
775156952Sume	const struct sockaddr *nsap;
776156952Sume	int nsaplen;
777156952Sume	struct timespec now, timeout, finish;
778156952Sume	struct sockaddr_storage from;
779156952Sume	ISC_SOCKLEN_T fromlen;
780156952Sume	int resplen, seconds, n, s;
781156952Sume#ifdef USE_POLL
782156952Sume	int     polltimeout;
783156952Sume	struct pollfd   pollfd;
784156952Sume#else
785156952Sume	fd_set dsmask;
786156952Sume#endif
787156952Sume
788156952Sume	nsap = get_nsaddr(statp, ns);
789156952Sume	nsaplen = get_salen(nsap);
790156952Sume	if (EXT(statp).nssocks[ns] == -1) {
791156952Sume		EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0);
792156952Sume		if (EXT(statp).nssocks[ns] > highestFD) {
793156952Sume			res_nclose(statp);
794156952Sume			errno = ENOTSOCK;
795156952Sume		}
796156952Sume		if (EXT(statp).nssocks[ns] < 0) {
797156952Sume			switch (errno) {
798156952Sume			case EPROTONOSUPPORT:
799156952Sume#ifdef EPFNOSUPPORT
800156952Sume			case EPFNOSUPPORT:
801156952Sume#endif
802156952Sume			case EAFNOSUPPORT:
803156952Sume				Perror(statp, stderr, "socket(dg)", errno);
804156952Sume				return (0);
805156952Sume			default:
806156952Sume				*terrno = errno;
807156952Sume				Perror(statp, stderr, "socket(dg)", errno);
808156952Sume				return (-1);
809156952Sume			}
810156952Sume		}
811156952Sume#ifndef CANNOT_CONNECT_DGRAM
812156952Sume		/*
813156952Sume		 * On a 4.3BSD+ machine (client and server,
814156952Sume		 * actually), sending to a nameserver datagram
815156952Sume		 * port with no nameserver will cause an
816156952Sume		 * ICMP port unreachable message to be returned.
817156952Sume		 * If our datagram socket is "connected" to the
818156952Sume		 * server, we get an ECONNREFUSED error on the next
819156952Sume		 * socket operation, and select returns if the
820156952Sume		 * error message is received.  We can thus detect
821156952Sume		 * the absence of a nameserver without timing out.
822156952Sume		 */
823156952Sume		if (connect(EXT(statp).nssocks[ns], nsap, nsaplen) < 0) {
824156952Sume			Aerror(statp, stderr, "connect(dg)", errno, nsap,
825156952Sume			    nsaplen);
826156952Sume			res_nclose(statp);
827156952Sume			return (0);
828156952Sume		}
829156952Sume#endif /* !CANNOT_CONNECT_DGRAM */
830156952Sume		Dprint(statp->options & RES_DEBUG,
831156952Sume		       (stdout, ";; new DG socket\n"))
832156952Sume	}
833156952Sume	s = EXT(statp).nssocks[ns];
834156952Sume#ifndef CANNOT_CONNECT_DGRAM
835156952Sume	if (send(s, (const char*)buf, buflen, 0) != buflen) {
836156952Sume		Perror(statp, stderr, "send", errno);
837156952Sume		res_nclose(statp);
838156952Sume		return (0);
839156952Sume	}
840156952Sume#else /* !CANNOT_CONNECT_DGRAM */
841156952Sume	if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen)
842156952Sume	{
843156952Sume		Aerror(statp, stderr, "sendto", errno, nsap, nsaplen);
844156952Sume		res_nclose(statp);
845156952Sume		return (0);
846156952Sume	}
847156952Sume#endif /* !CANNOT_CONNECT_DGRAM */
848156952Sume
849156952Sume	/*
850156952Sume	 * Wait for reply.
851156952Sume	 */
852156952Sume	seconds = (statp->retrans << ns);
853156952Sume	if (ns > 0)
854156952Sume		seconds /= statp->nscount;
855156952Sume	if (seconds <= 0)
856156952Sume		seconds = 1;
857156952Sume	now = evNowTime();
858156952Sume	timeout = evConsTime(seconds, 0);
859156952Sume	finish = evAddTime(now, timeout);
860156952Sume	goto nonow;
861156952Sume wait:
862156952Sume	now = evNowTime();
863156952Sume nonow:
864156952Sume#ifndef USE_POLL
865156952Sume	FD_ZERO(&dsmask);
866156952Sume	FD_SET(s, &dsmask);
867156952Sume	if (evCmpTime(finish, now) > 0)
868156952Sume		timeout = evSubTime(finish, now);
869156952Sume	else
870156952Sume		timeout = evConsTime(0, 0);
871156952Sume	n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
872156952Sume#else
873156952Sume	timeout = evSubTime(finish, now);
874156952Sume	if (timeout.tv_sec < 0)
875156952Sume		timeout = evConsTime(0, 0);
876156952Sume	polltimeout = 1000*timeout.tv_sec +
877156952Sume		timeout.tv_nsec/1000000;
878156952Sume	pollfd.fd = s;
879156952Sume	pollfd.events = POLLRDNORM;
880156952Sume	n = poll(&pollfd, 1, polltimeout);
881156952Sume#endif /* USE_POLL */
882156952Sume
883156952Sume	if (n == 0) {
884156952Sume		Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
885156952Sume		*gotsomewhere = 1;
886156952Sume		return (0);
887156952Sume	}
888156952Sume	if (n < 0) {
889156952Sume		if (errno == EINTR)
890156952Sume			goto wait;
891156952Sume#ifndef USE_POLL
892156952Sume		Perror(statp, stderr, "select", errno);
893156952Sume#else
894156952Sume		Perror(statp, stderr, "poll", errno);
895156952Sume#endif /* USE_POLL */
896156952Sume		res_nclose(statp);
897156952Sume		return (0);
898156952Sume	}
899156952Sume	errno = 0;
900156952Sume	fromlen = sizeof(from);
901156952Sume	resplen = recvfrom(s, (char*)ans, anssiz,0,
902156952Sume			   (struct sockaddr *)&from, &fromlen);
903156952Sume	if (resplen <= 0) {
904156952Sume		Perror(statp, stderr, "recvfrom", errno);
905156952Sume		res_nclose(statp);
906156952Sume		return (0);
907156952Sume	}
908156952Sume	*gotsomewhere = 1;
909156952Sume	if (resplen < HFIXEDSZ) {
910156952Sume		/*
911156952Sume		 * Undersized message.
912156952Sume		 */
913156952Sume		Dprint(statp->options & RES_DEBUG,
914156952Sume		       (stdout, ";; undersized: %d\n",
915156952Sume			resplen));
916156952Sume		*terrno = EMSGSIZE;
917156952Sume		res_nclose(statp);
918156952Sume		return (0);
919156952Sume	}
920156952Sume	if (hp->id != anhp->id) {
921156952Sume		/*
922156952Sume		 * response from old query, ignore it.
923156952Sume		 * XXX - potential security hazard could
924156952Sume		 *	 be detected here.
925156952Sume		 */
926156952Sume		DprintQ((statp->options & RES_DEBUG) ||
927156952Sume			(statp->pfcode & RES_PRF_REPLY),
928156952Sume			(stdout, ";; old answer:\n"),
929156952Sume			ans, (resplen > anssiz) ? anssiz : resplen);
930156952Sume		goto wait;
931156952Sume	}
932156952Sume	if (!(statp->options & RES_INSECURE1) &&
933156952Sume	    !res_ourserver_p(statp, (struct sockaddr *)&from)) {
934156952Sume		/*
935156952Sume		 * response from wrong server? ignore it.
936156952Sume		 * XXX - potential security hazard could
937156952Sume		 *	 be detected here.
938156952Sume		 */
939156952Sume		DprintQ((statp->options & RES_DEBUG) ||
940156952Sume			(statp->pfcode & RES_PRF_REPLY),
941156952Sume			(stdout, ";; not our server:\n"),
942156952Sume			ans, (resplen > anssiz) ? anssiz : resplen);
943156952Sume		goto wait;
944156952Sume	}
945156952Sume#ifdef RES_USE_EDNS0
946156952Sume	if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) {
947156952Sume		/*
948156952Sume		 * Do not retry if the server do not understand EDNS0.
949156952Sume		 * The case has to be captured here, as FORMERR packet do not
950156952Sume		 * carry query section, hence res_queriesmatch() returns 0.
951156952Sume		 */
952156952Sume		DprintQ(statp->options & RES_DEBUG,
953156952Sume			(stdout, "server rejected query with EDNS0:\n"),
954156952Sume			ans, (resplen > anssiz) ? anssiz : resplen);
955156952Sume		/* record the error */
956156952Sume		statp->_flags |= RES_F_EDNS0ERR;
957156952Sume		res_nclose(statp);
958156952Sume		return (0);
959156952Sume	}
960156952Sume#endif
961156952Sume	if (!(statp->options & RES_INSECURE2) &&
962156952Sume	    !res_queriesmatch(buf, buf + buflen,
963156952Sume			      ans, ans + anssiz)) {
964156952Sume		/*
965156952Sume		 * response contains wrong query? ignore it.
966156952Sume		 * XXX - potential security hazard could
967156952Sume		 *	 be detected here.
968156952Sume		 */
969156952Sume		DprintQ((statp->options & RES_DEBUG) ||
970156952Sume			(statp->pfcode & RES_PRF_REPLY),
971156952Sume			(stdout, ";; wrong query name:\n"),
972156952Sume			ans, (resplen > anssiz) ? anssiz : resplen);
973156952Sume		goto wait;
974156952Sume	}
975156952Sume	if (anhp->rcode == SERVFAIL ||
976156952Sume	    anhp->rcode == NOTIMP ||
977156952Sume	    anhp->rcode == REFUSED) {
978156952Sume		DprintQ(statp->options & RES_DEBUG,
979156952Sume			(stdout, "server rejected query:\n"),
980156952Sume			ans, (resplen > anssiz) ? anssiz : resplen);
981156952Sume		res_nclose(statp);
982156952Sume		/* don't retry if called from dig */
983156952Sume		if (!statp->pfcode)
984156952Sume			return (0);
985156952Sume	}
986156952Sume	if (!(statp->options & RES_IGNTC) && anhp->tc) {
987156952Sume		/*
988156952Sume		 * To get the rest of answer,
989156952Sume		 * use TCP with same server.
990156952Sume		 */
991156952Sume		Dprint(statp->options & RES_DEBUG,
992156952Sume		       (stdout, ";; truncated answer\n"));
993156952Sume		*v_circuit = 1;
994156952Sume		res_nclose(statp);
995156952Sume		return (1);
996156952Sume	}
997156952Sume	/*
998156952Sume	 * All is well, or the error is fatal.  Signal that the
999156952Sume	 * next nameserver ought not be tried.
1000156952Sume	 */
1001156952Sume	return (resplen);
1002156952Sume}
1003156952Sume
1004156952Sumestatic void
1005156952SumeAerror(const res_state statp, FILE *file, const char *string, int error,
1006156952Sume       const struct sockaddr *address, int alen)
1007156952Sume{
1008156952Sume	int save = errno;
1009156952Sume	char hbuf[NI_MAXHOST];
1010156952Sume	char sbuf[NI_MAXSERV];
1011156952Sume
1012156952Sume	alen = alen;
1013156952Sume
1014156952Sume	if ((statp->options & RES_DEBUG) != 0U) {
1015156952Sume		if (getnameinfo(address, alen, hbuf, sizeof(hbuf),
1016156952Sume		    sbuf, sizeof(sbuf), niflags)) {
1017156952Sume			strncpy(hbuf, "?", sizeof(hbuf) - 1);
1018156952Sume			hbuf[sizeof(hbuf) - 1] = '\0';
1019156952Sume			strncpy(sbuf, "?", sizeof(sbuf) - 1);
1020156952Sume			sbuf[sizeof(sbuf) - 1] = '\0';
1021156952Sume		}
1022156952Sume		fprintf(file, "res_send: %s ([%s].%s): %s\n",
1023156952Sume			string, hbuf, sbuf, strerror(error));
1024156952Sume	}
1025156952Sume	errno = save;
1026156952Sume}
1027156952Sume
1028156952Sumestatic void
1029156952SumePerror(const res_state statp, FILE *file, const char *string, int error) {
1030156952Sume	int save = errno;
1031156952Sume
1032156952Sume	if ((statp->options & RES_DEBUG) != 0U)
1033156952Sume		fprintf(file, "res_send: %s: %s\n",
1034156952Sume			string, strerror(error));
1035156952Sume	errno = save;
1036156952Sume}
1037156952Sume
1038156952Sumestatic int
1039156952Sumesock_eq(struct sockaddr *a, struct sockaddr *b) {
1040156952Sume	struct sockaddr_in *a4, *b4;
1041156952Sume	struct sockaddr_in6 *a6, *b6;
1042156952Sume
1043156952Sume	if (a->sa_family != b->sa_family)
1044156952Sume		return 0;
1045156952Sume	switch (a->sa_family) {
1046156952Sume	case AF_INET:
1047156952Sume		a4 = (struct sockaddr_in *)a;
1048156952Sume		b4 = (struct sockaddr_in *)b;
1049156952Sume		return a4->sin_port == b4->sin_port &&
1050156952Sume		    a4->sin_addr.s_addr == b4->sin_addr.s_addr;
1051156952Sume	case AF_INET6:
1052156952Sume		a6 = (struct sockaddr_in6 *)a;
1053156952Sume		b6 = (struct sockaddr_in6 *)b;
1054156952Sume		return a6->sin6_port == b6->sin6_port &&
1055156952Sume#ifdef HAVE_SIN6_SCOPE_ID
1056156952Sume		    a6->sin6_scope_id == b6->sin6_scope_id &&
1057156952Sume#endif
1058156952Sume		    IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr);
1059156952Sume	default:
1060156952Sume		return 0;
1061156952Sume	}
1062156952Sume}
1063156952Sume
1064156952Sume#if defined(NEED_PSELECT) && !defined(USE_POLL)
1065156952Sume/* XXX needs to move to the porting library. */
1066156952Sumestatic int
1067156952Sumepselect(int nfds, void *rfds, void *wfds, void *efds,
1068156952Sume	struct timespec *tsp, const sigset_t *sigmask)
1069156952Sume{
1070156952Sume	struct timeval tv, *tvp;
1071156952Sume	sigset_t sigs;
1072156952Sume	int n;
1073156952Sume
1074156952Sume	if (tsp) {
1075156952Sume		tvp = &tv;
1076156952Sume		tv = evTimeVal(*tsp);
1077156952Sume	} else
1078156952Sume		tvp = NULL;
1079156952Sume	if (sigmask)
1080156952Sume		sigprocmask(SIG_SETMASK, sigmask, &sigs);
1081156952Sume	n = select(nfds, rfds, wfds, efds, tvp);
1082156952Sume	if (sigmask)
1083156952Sume		sigprocmask(SIG_SETMASK, &sigs, NULL);
1084156952Sume	if (tsp)
1085156952Sume		*tsp = evTimeSpec(tv);
1086156952Sume	return (n);
1087156952Sume}
1088156952Sume#endif
1089