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