summaryrefslogtreecommitdiff
path: root/src/lib/libc/net/res_send.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libc/net/res_send.c')
-rw-r--r--src/lib/libc/net/res_send.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/src/lib/libc/net/res_send.c b/src/lib/libc/net/res_send.c
new file mode 100644
index 0000000000..f7631350bc
--- /dev/null
+++ b/src/lib/libc/net/res_send.c
@@ -0,0 +1,870 @@
1/* $OpenBSD: res_send.c,v 1.19 2005/08/06 20:30:04 espie Exp $ */
2
3/*
4 * ++Copyright++ 1985, 1989, 1993
5 * -
6 * Copyright (c) 1985, 1989, 1993
7 * The Regents of the University of California. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 * -
33 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
34 *
35 * Permission to use, copy, modify, and distribute this software for any
36 * purpose with or without fee is hereby granted, provided that the above
37 * copyright notice and this permission notice appear in all copies, and that
38 * the name of Digital Equipment Corporation not be used in advertising or
39 * publicity pertaining to distribution of the document or software without
40 * specific, written prior permission.
41 *
42 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
43 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
44 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
45 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
46 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
47 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
48 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
49 * SOFTWARE.
50 * -
51 * --Copyright--
52 */
53
54#ifndef INET6
55#define INET6
56#endif
57
58 /* change this to "0"
59 * if you talk to a lot
60 * of multi-homed SunOS
61 * ("broken") name servers.
62 */
63#define CHECK_SRVR_ADDR 1 /* XXX - should be in options.h */
64
65/*
66 * Send query to name server and wait for reply.
67 */
68
69#include <sys/types.h>
70#include <sys/param.h>
71#include <sys/time.h>
72#include <sys/socket.h>
73#include <sys/uio.h>
74#include <netinet/in.h>
75#include <arpa/nameser.h>
76#include <arpa/inet.h>
77
78#include <stdio.h>
79#include <netdb.h>
80#include <errno.h>
81#include <resolv.h>
82#include <stdlib.h>
83#include <string.h>
84#include <unistd.h>
85
86#include "thread_private.h"
87
88static int s = -1; /* socket used for communications */
89static int connected = 0; /* is the socket connected */
90static int vc = 0; /* is the socket a virtual ciruit? */
91static 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) bzero((char *)(p), 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 {}
116static char abuf[NI_MAXHOST];
117static char pbuf[NI_MAXSERV];
118static void Aerror(FILE *, char *, int, struct sockaddr *);
119static void Perror(FILE *, char *, int);
120
121 static void
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
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
152static res_send_qhook Qhook = NULL;
153static res_send_rhook Rhook = NULL;
154
155void
156res_send_setqhook(res_send_qhook hook)
157{
158
159 Qhook = hook;
160}
161
162void
163res_send_setrhook(res_send_rhook hook)
164{
165
166 Rhook = hook;
167}
168
169#ifdef INET6
170static struct sockaddr * get_nsaddr(size_t);
171
172/*
173 * pick appropriate nsaddr_list for use. see res_init() for initialization.
174 */
175static struct sockaddr *
176get_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 */
211int
212res_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 */
266int
267res_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 */
302int
303res_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
327int
328res_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 */
469read_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 */
860void
861res_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}