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