1 /**	$MirOS: src/lib/libc/net/res_send.c,v 1.4 2005/09/22 20:40:03 tg Exp $ */
2 /*	$OpenBSD: res_send.c,v 1.19 2005/08/06 20:30:04 espie Exp $	*/
3 
4 /*
5  * Copyright (c) 1985, 1989, 1993
6  *    The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  * -
32  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
33  *
34  * Permission to use, copy, modify, and distribute this software for any
35  * purpose with or without fee is hereby granted, provided that the above
36  * copyright notice and this permission notice appear in all copies, and that
37  * the name of Digital Equipment Corporation not be used in advertising or
38  * publicity pertaining to distribution of the document or software without
39  * specific, written prior permission.
40  *
41  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
42  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
43  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
44  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
45  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
46  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
47  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
48  * SOFTWARE.
49  * -
50  * --Copyright--
51  */
52 
53 #ifndef INET6
54 #define INET6
55 #endif
56 
57 	/* change this to "0"
58 	 * if you talk to a lot
59 	 * of multi-homed SunOS
60 	 * ("broken") name servers.
61 	 */
62 #define	CHECK_SRVR_ADDR	1	/* XXX - should be in options.h */
63 
64 /*
65  * Send query to name server and wait for reply.
66  */
67 
68 #include <sys/param.h>
69 #include <sys/time.h>
70 #include <sys/socket.h>
71 #include <sys/uio.h>
72 #include <netinet/in.h>
73 #include <arpa/nameser.h>
74 #include <arpa/inet.h>
75 
76 #include <stdio.h>
77 #include <netdb.h>
78 #include <errno.h>
79 #include <resolv.h>
80 #include <stdlib.h>
81 #include <string.h>
82 #include <unistd.h>
83 
84 #include "thread_private.h"
85 
86 __RCSID("$MirOS: src/lib/libc/net/res_send.c,v 1.4 2005/09/22 20:40:03 tg Exp $");
87 
88 static int s = -1;	/* socket used for communications */
89 static int connected = 0;	/* is the socket connected */
90 static int vc = 0;	/* is the socket a virtual ciruit? */
91 static int af = 0;		/* address family of socket */
92 
93 #ifndef FD_SET
94 /* XXX - should be in portability.h */
95 #define	NFDBITS		32
96 #define	FD_SETSIZE	32
97 #define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
98 #define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
99 #define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
100 #define FD_ZERO(p)	memset((char *)(p), 0, sizeof(*(p)))
101 #endif
102 
103 #define CAN_RECONNECT 1
104 
105 #ifndef DEBUG
106 #   define Dprint(cond, args) /*empty*/
107 #   define DprintQ(cond, args, query, size) /*empty*/
108 #   define Aerror(file, string, error, address) /*empty*/
109 #   define Perror(file, string, error) /*empty*/
110 #else
111 #   define Dprint(cond, args) if (cond) {fprintf args;} else {}
112 #   define DprintQ(cond, args, query, size) if (cond) {\
113 			fprintf args;\
114 			__fp_nquery(query, size, stdout);\
115 		} else {}
116 static char abuf[NI_MAXHOST];
117 static char pbuf[NI_MAXSERV];
118 static void Aerror(FILE *, char *, int, struct sockaddr *);
119 static void Perror(FILE *, char *, int);
120 
121     static void
Aerror(FILE * file,char * string,int error,struct sockaddr * address)122     Aerror(FILE *file, char *string, int error, struct sockaddr *address)
123     {
124 	struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
125 	int save = errno;
126 
127 	if (_resp->options & RES_DEBUG) {
128 		if (getnameinfo(address, address->sa_len, abuf, sizeof(abuf),
129 		    pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
130 			strlcpy(abuf, "?", sizeof(abuf));
131 			strlcpy(pbuf, "?", sizeof(pbuf));
132 		}
133 		fprintf(file, "res_send: %s ([%s].%s): %s\n",
134 			string, abuf, pbuf, strerror(error));
135 	}
136 	errno = save;
137     }
138     static void
Perror(FILE * file,char * string,int error)139     Perror(FILE *file, char *string, int error)
140     {
141 	struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
142 	int save = errno;
143 
144 	if (_resp->options & RES_DEBUG) {
145 		fprintf(file, "res_send: %s: %s\n",
146 			string, strerror(error));
147 	}
148 	errno = save;
149     }
150 #endif
151 
152 static res_send_qhook Qhook = NULL;
153 static res_send_rhook Rhook = NULL;
154 
155 void
res_send_setqhook(res_send_qhook hook)156 res_send_setqhook(res_send_qhook hook)
157 {
158 
159 	Qhook = hook;
160 }
161 
162 void
res_send_setrhook(res_send_rhook hook)163 res_send_setrhook(res_send_rhook hook)
164 {
165 
166 	Rhook = hook;
167 }
168 
169 #ifdef INET6
170 static struct sockaddr * get_nsaddr(size_t);
171 
172 /*
173  * pick appropriate nsaddr_list for use.  see res_init() for initialization.
174  */
175 static struct sockaddr *
get_nsaddr(size_t n)176 get_nsaddr(size_t n)
177 {
178 	struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
179 	struct __res_state_ext *_res_extp = _THREAD_PRIVATE(_res_ext, _res_ext,
180 							    &_res_ext);
181 
182 	if (!_resp->nsaddr_list[n].sin_family) {
183 		/*
184 		 * - _res_extp->nsaddr_list[n] holds an address that is larger
185 		 *   than struct sockaddr, and
186 		 * - user code did not update _resp->nsaddr_list[n].
187 		 */
188 		return (struct sockaddr *)&_res_extp->nsaddr_list[n];
189 	} else {
190 		/*
191 		 * - user code updated _res.nsaddr_list[n], or
192 		 * - _resp->nsaddr_list[n] has the same content as
193 		 *   _res_extp->nsaddr_list[n].
194 		 */
195 		return (struct sockaddr *)&_resp->nsaddr_list[n];
196 	}
197 }
198 #else
199 #define get_nsaddr(n)	((struct sockaddr *)&_resp->nsaddr_list[(n)])
200 #endif
201 
202 /* int
203  * res_isourserver(ina)
204  *	looks up "ina" in _resp->ns_addr_list[]
205  * returns:
206  *	0  : not found
207  *	>0 : found
208  * author:
209  *	paul vixie, 29may94
210  */
211 int
res_isourserver(const struct sockaddr_in * inp)212 res_isourserver(const struct sockaddr_in *inp)
213 {
214 	struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
215 #ifdef INET6
216 	const struct sockaddr_in6 *in6p = (const struct sockaddr_in6 *)inp;
217 	const struct sockaddr_in6 *srv6;
218 #endif
219 	const struct sockaddr_in *srv;
220 	int ns, ret;
221 
222 	ret = 0;
223 	switch (inp->sin_family) {
224 #ifdef INET6
225 	case AF_INET6:
226 		for (ns = 0; ns < _resp->nscount; ns++) {
227 			srv6 = (struct sockaddr_in6 *)get_nsaddr(ns);
228 			if (srv6->sin6_family == in6p->sin6_family &&
229 			    srv6->sin6_port == in6p->sin6_port &&
230 			    srv6->sin6_scope_id == in6p->sin6_scope_id &&
231 			    (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) ||
232 			     IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr,
233 			         &in6p->sin6_addr))) {
234 				ret++;
235 				break;
236 			}
237 		}
238 		break;
239 #endif
240 	case AF_INET:
241 		for (ns = 0; ns < _resp->nscount; ns++) {
242 			srv = (struct sockaddr_in *)get_nsaddr(ns);
243 			if (srv->sin_family == inp->sin_family &&
244 			    srv->sin_port == inp->sin_port &&
245 			    (srv->sin_addr.s_addr == INADDR_ANY ||
246 			     srv->sin_addr.s_addr == inp->sin_addr.s_addr)) {
247 				ret++;
248 				break;
249 			}
250 		}
251 		break;
252 	}
253 	return (ret);
254 }
255 
256 /* int
257  * res_nameinquery(name, type, class, buf, eom)
258  *	look for (name,type,class) in the query section of packet (buf,eom)
259  * returns:
260  *	-1 : format error
261  *	0  : not found
262  *	>0 : found
263  * author:
264  *	paul vixie, 29may94
265  */
266 int
res_nameinquery(const char * name,int type,int class,const u_char * buf,const u_char * eom)267 res_nameinquery(const char *name, int type, int class, const u_char *buf,
268     const u_char *eom)
269 {
270 	const u_char *cp = buf + HFIXEDSZ;
271 	int qdcount = ntohs(((HEADER*)buf)->qdcount);
272 
273 	while (qdcount-- > 0) {
274 		char tname[MAXDNAME+1];
275 		int n, ttype, tclass;
276 
277 		n = dn_expand(buf, eom, cp, tname, sizeof tname);
278 		if (n < 0)
279 			return (-1);
280 		cp += n;
281 		ttype = _getshort(cp); cp += INT16SZ;
282 		tclass = _getshort(cp); cp += INT16SZ;
283 		if (ttype == type &&
284 		    tclass == class &&
285 		    strcasecmp(tname, name) == 0)
286 			return (1);
287 	}
288 	return (0);
289 }
290 
291 /* int
292  * res_queriesmatch(buf1, eom1, buf2, eom2)
293  *	is there a 1:1 mapping of (name,type,class)
294  *	in (buf1,eom1) and (buf2,eom2)?
295  * returns:
296  *	-1 : format error
297  *	0  : not a 1:1 mapping
298  *	>0 : is a 1:1 mapping
299  * author:
300  *	paul vixie, 29may94
301  */
302 int
res_queriesmatch(const u_char * buf1,const u_char * eom1,const u_char * buf2,const u_char * eom2)303 res_queriesmatch(const u_char *buf1, const u_char *eom1, const u_char *buf2,
304     const u_char *eom2)
305 {
306 	const u_char *cp = buf1 + HFIXEDSZ;
307 	int qdcount = ntohs(((HEADER*)buf1)->qdcount);
308 
309 	if (qdcount != ntohs(((HEADER*)buf2)->qdcount))
310 		return (0);
311 	while (qdcount-- > 0) {
312 		char tname[MAXDNAME+1];
313 		int n, ttype, tclass;
314 
315 		n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
316 		if (n < 0)
317 			return (-1);
318 		cp += n;
319 		ttype = _getshort(cp);	cp += INT16SZ;
320 		tclass = _getshort(cp); cp += INT16SZ;
321 		if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
322 			return (0);
323 	}
324 	return (1);
325 }
326 
327 int
res_send(const u_char * buf,int buflen,u_char * ans,int anssiz)328 res_send(const u_char *buf, int buflen, u_char *ans, int anssiz)
329 {
330 	struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
331 	HEADER *hp = (HEADER *) buf;
332 	HEADER *anhp = (HEADER *) ans;
333 	int gotsomewhere, connreset, terrno, try, v_circuit, resplen, ns;
334 	int n;
335 	u_int badns;	/* XXX NSMAX can't exceed #/bits in this var */
336 
337 	if (_res_init(0) == -1) {
338 		/* errno should have been set by res_init() in this case. */
339 		return (-1);
340 	}
341 	DprintQ((_resp->options & RES_DEBUG) || (_resp->pfcode & RES_PRF_QUERY),
342 		(stdout, ";; res_send()\n"), buf, buflen);
343 	v_circuit = (_resp->options & RES_USEVC) || buflen > PACKETSZ;
344 	gotsomewhere = 0;
345 	connreset = 0;
346 	terrno = ETIMEDOUT;
347 	badns = 0;
348 
349 	/*
350 	 * Send request, RETRY times, or until successful
351 	 */
352 	for (try = 0; try < _resp->retry; try++) {
353 	    for (ns = 0; ns < _resp->nscount; ns++) {
354 		struct sockaddr *nsap = get_nsaddr(ns);
355 		socklen_t salen;
356 
357 		if (nsap->sa_len)
358 			salen = nsap->sa_len;
359 #ifdef INET6
360 		else if (nsap->sa_family == AF_INET6)
361 			salen = sizeof(struct sockaddr_in6);
362 #endif
363 		else if (nsap->sa_family == AF_INET)
364 			salen = sizeof(struct sockaddr_in);
365 		else
366 			salen = 0;	/*unknown, die on connect*/
367 
368     same_ns:
369 		if (badns & (1 << ns)) {
370 			res_close();
371 			goto next_ns;
372 		}
373 
374 		if (Qhook) {
375 			int done = 0, loops = 0;
376 
377 			do {
378 				res_sendhookact act;
379 
380 				act = (*Qhook)((struct sockaddr_in **)&nsap,
381 					       &buf, &buflen,
382 					       ans, anssiz, &resplen);
383 				switch (act) {
384 				case res_goahead:
385 					done = 1;
386 					break;
387 				case res_nextns:
388 					res_close();
389 					goto next_ns;
390 				case res_done:
391 					return (resplen);
392 				case res_modified:
393 					/* give the hook another try */
394 					if (++loops < 42) /*doug adams*/
395 						break;
396 					/*FALLTHROUGH*/
397 				case res_error:
398 					/*FALLTHROUGH*/
399 				default:
400 					return (-1);
401 				}
402 			} while (!done);
403 		}
404 
405 		Dprint((_resp->options & RES_DEBUG) &&
406 		       getnameinfo(nsap, salen, abuf, sizeof(abuf),
407 			   NULL, 0, NI_NUMERICHOST) == 0,
408 		       (stdout, ";; Querying server (# %d) address = %s\n",
409 			ns + 1, abuf));
410 
411 		if (v_circuit) {
412 			int truncated;
413 			struct iovec iov[2];
414 			u_short len;
415 			u_char *cp;
416 
417 			/*
418 			 * Use virtual circuit;
419 			 * at most one attempt per server.
420 			 */
421 			try = _resp->retry;
422 			truncated = 0;
423 			if ((s < 0) || (!vc) || (af != nsap->sa_family)) {
424 				if (s >= 0)
425 					res_close();
426 
427 				af = nsap->sa_family;
428 				s = socket(af, SOCK_STREAM, 0);
429 				if (s < 0) {
430 					terrno = errno;
431 					Perror(stderr, "socket(vc)", errno);
432 #if 0
433 					return (-1);
434 #else
435 					badns |= (1 << ns);
436 					res_close();
437 					goto next_ns;
438 #endif
439 				}
440 				errno = 0;
441 				if (connect(s, nsap, salen) < 0) {
442 					terrno = errno;
443 					Aerror(stderr, "connect/vc",
444 					       errno, nsap);
445 					badns |= (1 << ns);
446 					res_close();
447 					goto next_ns;
448 				}
449 				vc = 1;
450 			}
451 			/*
452 			 * Send length & message
453 			 */
454 			putshort((u_short)buflen, (u_char*)&len);
455 			iov[0].iov_base = (caddr_t)&len;
456 			iov[0].iov_len = INT16SZ;
457 			iov[1].iov_base = (caddr_t)buf;
458 			iov[1].iov_len = buflen;
459 			if (writev(s, iov, 2) != (INT16SZ + buflen)) {
460 				terrno = errno;
461 				Perror(stderr, "write failed", errno);
462 				badns |= (1 << ns);
463 				res_close();
464 				goto next_ns;
465 			}
466 			/*
467 			 * Receive length & response
468 			 */
469 read_len:
470 			cp = ans;
471 			len = INT16SZ;
472 			while ((n = read(s, (char *)cp, (int)len)) > 0) {
473 				cp += n;
474 				if ((len -= n) <= 0)
475 					break;
476 			}
477 			if (n <= 0) {
478 				terrno = errno;
479 				Perror(stderr, "read failed", errno);
480 				res_close();
481 				/*
482 				 * A long running process might get its TCP
483 				 * connection reset if the remote server was
484 				 * restarted.  Requery the server instead of
485 				 * trying a new one.  When there is only one
486 				 * server, this means that a query might work
487 				 * instead of failing.  We only allow one reset
488 				 * per query to prevent looping.
489 				 */
490 				if (terrno == ECONNRESET && !connreset) {
491 					connreset = 1;
492 					res_close();
493 					goto same_ns;
494 				}
495 				res_close();
496 				goto next_ns;
497 			}
498 			resplen = _getshort(ans);
499 			if (resplen > anssiz) {
500 				Dprint(_resp->options & RES_DEBUG,
501 				       (stdout, ";; response truncated\n")
502 				       );
503 				truncated = 1;
504 				len = anssiz;
505 			} else
506 				len = resplen;
507 			cp = ans;
508 			while (len != 0 &&
509 			       (n = read(s, (char *)cp, (int)len)) > 0) {
510 				cp += n;
511 				len -= n;
512 			}
513 			if (n <= 0) {
514 				terrno = errno;
515 				Perror(stderr, "read(vc)", errno);
516 				res_close();
517 				goto next_ns;
518 			}
519 			if (truncated) {
520 				/*
521 				 * Flush rest of answer
522 				 * so connection stays in synch.
523 				 */
524 				anhp->tc = 1;
525 				len = resplen - anssiz;
526 				while (len != 0) {
527 					char junk[PACKETSZ];
528 
529 					n = (len > sizeof(junk)
530 					     ? sizeof(junk)
531 					     : len);
532 					if ((n = read(s, junk, n)) > 0)
533 						len -= n;
534 					else
535 						break;
536 				}
537 			}
538 			/*
539 			 * The calling applicating has bailed out of
540 			 * a previous call and failed to arrange to have
541 			 * the circuit closed or the server has got
542 			 * itself confused. Anyway drop the packet and
543 			 * wait for the correct one.
544 			 */
545 			if (hp->id != anhp->id) {
546 				DprintQ((_resp->options & RES_DEBUG) ||
547 					(_resp->pfcode & RES_PRF_REPLY),
548 					(stdout, ";; old answer (unexpected):\n"),
549 					ans, (resplen>anssiz)?anssiz:resplen);
550 				goto read_len;
551 			}
552 		} else {
553 			/*
554 			 * Use datagrams.
555 			 */
556 			struct timeval timeout;
557 			fd_set *dsmaskp;
558 			struct sockaddr_storage from;
559 			socklen_t fromlen;
560 
561 			if ((s < 0) || vc || (af != nsap->sa_family)) {
562 				if (vc)
563 					res_close();
564 				af = nsap->sa_family;
565 				s = socket(af, SOCK_DGRAM, 0);
566 				if (s < 0) {
567 #if !CAN_RECONNECT
568  bad_dg_sock:
569 #endif
570 					terrno = errno;
571 					Perror(stderr, "socket(dg)", errno);
572 #if 0
573 					return (-1);
574 #else
575 					badns |= (1 << ns);
576 					res_close();
577 					goto next_ns;
578 #endif
579 				}
580 #ifdef IPV6_MINMTU
581 				if (af == AF_INET6) {
582 					const int yes = 1;
583 					(void)setsockopt(s, IPPROTO_IPV6,
584 					    IPV6_USE_MIN_MTU, &yes,
585 					    sizeof(yes));
586 				}
587 #endif
588 				connected = 0;
589 			}
590 			/*
591 			 * On a 4.3BSD+ machine (client and server,
592 			 * actually), sending to a nameserver datagram
593 			 * port with no nameserver will cause an
594 			 * ICMP port unreachable message to be returned.
595 			 * If our datagram socket is "connected" to the
596 			 * server, we get an ECONNREFUSED error on the next
597 			 * socket operation, and select returns if the
598 			 * error message is received.  We can thus detect
599 			 * the absence of a nameserver without timing out.
600 			 * If we have sent queries to at least two servers,
601 			 * however, we don't want to remain connected,
602 			 * as we wish to receive answers from the first
603 			 * server to respond.
604 			 */
605 			if (!(_resp->options & RES_INSECURE1) &&
606 			    (_resp->nscount == 1 || (try == 0 && ns == 0))) {
607 				/*
608 				 * Connect only if we are sure we won't
609 				 * receive a response from another server.
610 				 */
611 				if (!connected) {
612 					if (connect(s, nsap, salen) < 0) {
613 						Aerror(stderr,
614 						       "connect(dg)",
615 						       errno, nsap);
616 						badns |= (1 << ns);
617 						res_close();
618 						goto next_ns;
619 					}
620 					connected = 1;
621 				}
622 				if (send(s, (char*)buf, buflen, 0) != buflen) {
623 					Perror(stderr, "send", errno);
624 					badns |= (1 << ns);
625 					res_close();
626 					goto next_ns;
627 				}
628 			} else {
629 				/*
630 				 * Disconnect if we want to listen
631 				 * for responses from more than one server.
632 				 */
633 				if (connected) {
634 #if CAN_RECONNECT
635 #ifdef INET6
636 					/* XXX: any errornous address */
637 #endif /* INET6 */
638 					struct sockaddr_in no_addr;
639 
640 					no_addr.sin_family = AF_INET;
641 					no_addr.sin_addr.s_addr = INADDR_ANY;
642 					no_addr.sin_port = 0;
643 					(void) connect(s,
644 						       (struct sockaddr *)
645 						        &no_addr,
646 						       sizeof(no_addr));
647 #else
648 					int s1 = socket(af, SOCK_DGRAM,0);
649 					if (s1 < 0)
650 						goto bad_dg_sock;
651 					(void) dup2(s1, s);
652 					(void) close(s1);
653 					Dprint(_resp->options & RES_DEBUG,
654 					       (stdout, ";; new DG socket\n"))
655 #endif
656 #ifdef IPV6_MINMTU
657 					if (af == AF_INET6) {
658 						const int yes = 1;
659 						(void)setsockopt(s, IPPROTO_IPV6,
660 						    IPV6_USE_MIN_MTU, &yes,
661 						    sizeof(yes));
662 					}
663 #endif
664 					connected = 0;
665 					errno = 0;
666 				}
667 				if (sendto(s, (char*)buf, buflen, 0,
668 					   nsap, salen) != buflen) {
669 					Aerror(stderr, "sendto", errno, nsap);
670 					badns |= (1 << ns);
671 					res_close();
672 					goto next_ns;
673 				}
674 			}
675 
676 			/*
677 			 * Wait for reply
678 			 */
679 			timeout.tv_sec = (_resp->retrans << try);
680 			if (try > 0)
681 				timeout.tv_sec /= _resp->nscount;
682 			if ((long) timeout.tv_sec <= 0)
683 				timeout.tv_sec = 1;
684 			timeout.tv_usec = 0;
685     wait:
686 			dsmaskp = (fd_set *)calloc(howmany(s+1, NFDBITS),
687 						   sizeof(fd_mask));
688 			if (dsmaskp == NULL) {
689 				res_close();
690 				goto next_ns;
691 			}
692 			FD_SET(s, dsmaskp);
693 			n = select(s+1, dsmaskp, (fd_set *)NULL,
694 				   (fd_set *)NULL, &timeout);
695 			free(dsmaskp);
696 			if (n < 0) {
697 				if (errno == EINTR)
698 					goto wait;
699 				Perror(stderr, "select", errno);
700 				res_close();
701 				goto next_ns;
702 			}
703 			if (n == 0) {
704 				/*
705 				 * timeout
706 				 */
707 				Dprint(_resp->options & RES_DEBUG,
708 				       (stdout, ";; timeout\n"));
709 				gotsomewhere = 1;
710 				res_close();
711 				goto next_ns;
712 			}
713 			errno = 0;
714 			fromlen = sizeof(from);
715 			resplen = recvfrom(s, (char*)ans, anssiz, 0,
716 					   (struct sockaddr *)&from, &fromlen);
717 			if (resplen <= 0) {
718 				Perror(stderr, "recvfrom", errno);
719 				res_close();
720 				goto next_ns;
721 			}
722 			gotsomewhere = 1;
723 			if (hp->id != anhp->id) {
724 				/*
725 				 * response from old query, ignore it.
726 				 * XXX - potential security hazard could
727 				 *	 be detected here.
728 				 */
729 				DprintQ((_resp->options & RES_DEBUG) ||
730 					(_resp->pfcode & RES_PRF_REPLY),
731 					(stdout, ";; old answer:\n"),
732 					ans, (resplen>anssiz)?anssiz:resplen);
733 				goto wait;
734 			}
735 #if CHECK_SRVR_ADDR
736 			if (!(_resp->options & RES_INSECURE1) &&
737 			    !res_isourserver((struct sockaddr_in *)&from)) {
738 				/*
739 				 * response from wrong server? ignore it.
740 				 * XXX - potential security hazard could
741 				 *	 be detected here.
742 				 */
743 				DprintQ((_resp->options & RES_DEBUG) ||
744 					(_resp->pfcode & RES_PRF_REPLY),
745 					(stdout, ";; not our server:\n"),
746 					ans, (resplen>anssiz)?anssiz:resplen);
747 				goto wait;
748 			}
749 #endif
750 			if (!(_resp->options & RES_INSECURE2) &&
751 			    !res_queriesmatch(buf, buf + buflen,
752 					      ans, ans + anssiz)) {
753 				/*
754 				 * response contains wrong query? ignore it.
755 				 * XXX - potential security hazard could
756 				 *	 be detected here.
757 				 */
758 				DprintQ((_resp->options & RES_DEBUG) ||
759 					(_resp->pfcode & RES_PRF_REPLY),
760 					(stdout, ";; wrong query name:\n"),
761 					ans, (resplen>anssiz)?anssiz:resplen);
762 				goto wait;
763 			}
764 			if (anhp->rcode == SERVFAIL ||
765 			    anhp->rcode == NOTIMP ||
766 			    anhp->rcode == REFUSED) {
767 				DprintQ(_resp->options & RES_DEBUG,
768 					(stdout, "server rejected query:\n"),
769 					ans, (resplen>anssiz)?anssiz:resplen);
770 				badns |= (1 << ns);
771 				res_close();
772 				/* don't retry if called from dig */
773 				if (!_resp->pfcode)
774 					goto next_ns;
775 			}
776 			if (!(_resp->options & RES_IGNTC) && anhp->tc) {
777 				/*
778 				 * get rest of answer;
779 				 * use TCP with same server.
780 				 */
781 				Dprint(_resp->options & RES_DEBUG,
782 				       (stdout, ";; truncated answer\n"));
783 				v_circuit = 1;
784 				res_close();
785 				goto same_ns;
786 			}
787 		} /*if vc/dg*/
788 		Dprint((_resp->options & RES_DEBUG) ||
789 		       ((_resp->pfcode & RES_PRF_REPLY) &&
790 			(_resp->pfcode & RES_PRF_HEAD1)),
791 		       (stdout, ";; got answer:\n"));
792 		DprintQ((_resp->options & RES_DEBUG) ||
793 			(_resp->pfcode & RES_PRF_REPLY),
794 			(stdout, "%s", ""),
795 			ans, (resplen>anssiz)?anssiz:resplen);
796 		/*
797 		 * If using virtual circuits, we assume that the first server
798 		 * is preferred over the rest (i.e. it is on the local
799 		 * machine) and only keep that one open.
800 		 * If we have temporarily opened a virtual circuit,
801 		 * or if we haven't been asked to keep a socket open,
802 		 * close the socket.
803 		 */
804 		if ((v_circuit && (!(_resp->options & RES_USEVC) || ns != 0)) ||
805 		    !(_resp->options & RES_STAYOPEN)) {
806 			res_close();
807 		}
808 		if (Rhook) {
809 			int done = 0, loops = 0;
810 
811 			do {
812 				res_sendhookact act;
813 
814 				act = (*Rhook)((struct sockaddr_in *)nsap,
815 					       buf, buflen,
816 					       ans, anssiz, &resplen);
817 				switch (act) {
818 				case res_goahead:
819 				case res_done:
820 					done = 1;
821 					break;
822 				case res_nextns:
823 					res_close();
824 					goto next_ns;
825 				case res_modified:
826 					/* give the hook another try */
827 					if (++loops < 42) /*doug adams*/
828 						break;
829 					/*FALLTHROUGH*/
830 				case res_error:
831 					/*FALLTHROUGH*/
832 				default:
833 					return (-1);
834 				}
835 			} while (!done);
836 
837 		}
838 		return (resplen);
839     next_ns: ;
840 	   } /*foreach ns*/
841 	} /*foreach retry*/
842 	res_close();
843 	if (!v_circuit) {
844 		if (!gotsomewhere)
845 			errno = ECONNREFUSED;	/* no nameservers found */
846 		else
847 			errno = ETIMEDOUT;	/* no answer obtained */
848 	} else
849 		errno = terrno;
850 	return (-1);
851 }
852 
853 /*
854  * This routine is for closing the socket if a virtual circuit is used and
855  * the program wants to close it.  This provides support for endhostent()
856  * which expects to close the socket.
857  *
858  * This routine is not expected to be user visible.
859  */
860 void
res_close(void)861 res_close(void)
862 {
863 	if (s >= 0) {
864 		(void) close(s);
865 		s = -1;
866 		connected = 0;
867 		vc = 0;
868 		af = 0;
869 	}
870 }
871