Initial revision
[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].%d): %s\n",
132                         string,
133                         inet_ntoa(address.sin_addr),
134                         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         Qhook = hook;
163 }
164
165 void
166 res_send_setrhook(hook)
167         res_send_rhook hook;
168 {
169         Rhook = hook;
170 }
171
172 /* int
173  * our_server(ina)
174  *      looks up "ina" in _res.ns_addr_list[]
175  * returns:
176  *      0  : not found
177  *      >0 : found
178  * author:
179  *      paul vixie, 29may94
180  */
181 static int
182 our_server(inp)
183         const struct sockaddr_in *inp;
184 {
185         struct sockaddr_in ina;
186         register int ns, ret;
187
188         ina = *inp;
189         ret = 0;
190         for (ns = 0;  ns < _res.nscount;  ns++) {
191                 register const struct sockaddr_in *srv = &_res.nsaddr_list[ns];
192
193                 if (srv->sin_family == ina.sin_family &&
194                     srv->sin_port == ina.sin_port &&
195                     (srv->sin_addr.s_addr == INADDR_ANY ||
196                      srv->sin_addr.s_addr == ina.sin_addr.s_addr)) {
197                         ret++;
198                         break;
199                 }
200         }
201         return (ret);
202 }
203
204 /* int
205  * name_in_query(name, type, class, buf, eom)
206  *      look for (name,type,class) in the query section of packet (buf,eom)
207  * returns:
208  *      -1 : format error
209  *      0  : not found
210  *      >0 : found
211  */
212 static int
213 name_in_query(name, type, class, buf, eom)
214         const char *name;
215         register int type, class;
216         const u_char *buf, *eom;
217 {
218         register const u_char *cp = buf + HFIXEDSZ;
219         int qdcount = ntohs(((HEADER*)buf)->qdcount);
220
221         while (qdcount-- > 0) {
222                 char tname[MAXDNAME+1];
223                 register int n, ttype, tclass;
224
225                 n = dn_expand(buf, eom, cp, tname, sizeof tname);
226                 if (n < 0)
227                         return (-1);
228                 cp += n;
229                 ttype = _getshort(cp);  cp += INT16SZ;
230                 tclass = _getshort(cp); cp += INT16SZ;
231                 if (ttype == type &&
232                     tclass == class &&
233                     strcasecmp(tname, name) == 0)
234                         return (1);
235         }
236         return (0);
237 }
238
239 /* int
240  * queries_match(buf1, eom1, buf2, eom2)
241  *      is there a 1:1 mapping of (name,type,class)
242  *      in (buf1,eom1) and (buf2,eom2)?
243  * returns:
244  *      -1 : format error
245  *      0  : not a 1:1 mapping
246  *      >0 : is a 1:1 mapping
247  */
248 static int
249 queries_match(buf1, eom1, buf2, eom2)
250         const u_char *buf1, *eom1;
251         const u_char *buf2, *eom2;
252 {
253         register const u_char *cp = buf1 + HFIXEDSZ;
254         int qdcount = ntohs(((HEADER*)buf1)->qdcount);
255
256         if (qdcount != ntohs(((HEADER*)buf2)->qdcount))
257                 return (0);
258         while (qdcount-- > 0) {
259                 char tname[MAXDNAME+1];
260                 register int n, ttype, tclass;
261
262                 n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
263                 if (n < 0)
264                         return (-1);
265                 cp += n;
266                 ttype = _getshort(cp);  cp += INT16SZ;
267                 tclass = _getshort(cp); cp += INT16SZ;
268                 if (!name_in_query(tname, ttype, tclass, buf2, eom2))
269                         return (0);
270         }
271         return (1);
272 }
273
274 int
275 res_send(buf, buflen, ans, anssiz)
276         const u_char    *buf;
277         int             buflen;
278         u_char          *ans;
279         int             anssiz;
280 {
281         HEADER          *hp = (HEADER *) buf;
282         HEADER          *anhp = (HEADER *) ans;
283         int             gotsomewhere = 0,
284                         connreset = 0,
285                         terrno = ETIMEDOUT;
286
287         register int    n;
288         int             try, v_circuit, resplen, ns;
289         u_int           badns;  /* XXX NSMAX can't exceed #/bits in this var */
290
291         DprintQ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY),
292                 (stdout, ";; res_send()\n"), buf);
293         if (!(_res.options & RES_INIT)) {
294                 if (res_init() == -1)
295                         return (-1);
296         }
297         v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
298         badns = 0;
299
300         /*
301          * Send request, RETRY times, or until successful
302          */
303         for (try = 0; try < _res.retry; try++) {
304             for (ns = 0; ns < _res.nscount; ns++) {
305                 struct sockaddr_in *nsap = &_res.nsaddr_list[ns];
306     same_ns:
307                 if (badns & (1<<ns)) {
308                         _res_close();
309                         goto next_ns;
310                 }
311
312                 if (Qhook) {
313                         int done = 0, loops = 0;
314
315                         do {
316                                 res_sendhookact act;
317
318                                 act = (*Qhook)(&nsap,
319                                                &buf,
320                                                &buflen,
321                                                ans,
322                                                anssiz,
323                                                &resplen);
324                                 switch (act) {
325                                 case res_goahead:
326                                         done = 1;
327                                         break;
328                                 case res_nextns:
329                                         _res_close();
330                                         goto next_ns;
331                                 case res_done:
332                                         return (resplen);
333                                 case res_modified:
334                                         /* give the hook another try */
335                                         if (++loops < 42) /*doug adams*/
336                                                 break;
337                                         /*FALLTHROUGH*/
338                                 case res_error:
339                                         /*FALLTHROUGH*/
340                                 default:
341                                         return (-1);
342                                 }
343                         } while (!done);
344                 }
345
346                 Dprint(_res.options & RES_DEBUG,
347                        (stdout, ";; Querying server (# %d) address = %s\n",
348                         ns+1, inet_ntoa(nsap->sin_addr)));
349
350                 if (v_circuit) {
351                         int             truncated;
352                         struct iovec    iov[2];
353                         u_short         len;
354                         u_char          *cp;
355
356                         /*
357                          * Use virtual circuit;
358                          * at most one attempt per server.
359                          */
360                         try = _res.retry;
361                         truncated = 0;
362                         if ((s < 0) || (!vc)) {
363                                 if (s >= 0)
364                                         _res_close();
365
366                                 s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
367                                 if (s < 0) {
368                                         terrno = errno;
369                                         Perror(stderr, "socket(vc)", errno);
370                                         return (-1);
371                                 }
372                                 if (connect(s,
373                                             (struct sockaddr *)nsap,
374                                             sizeof(struct sockaddr))
375                                     < 0) {
376                                         terrno = errno;
377                                         Aerror(stderr, "connect/vc",
378                                                errno, *nsap);
379                                         badns |= (1<<ns);
380                                         _res_close();
381                                         goto next_ns;
382                                 }
383                                 vc = 1;
384                         }
385                         /*
386                          * Send length & message
387                          */
388                         putshort((u_short)buflen, (u_char*)&len);
389                         iov[0].iov_base = (caddr_t)&len;
390                         iov[0].iov_len = INT16SZ;
391                         iov[1].iov_base = (caddr_t)buf;
392                         iov[1].iov_len = buflen;
393                         if (writev(s, iov, 2) != (INT16SZ + buflen)) {
394                                 terrno = errno;
395                                 Perror(stderr, "write failed", errno);
396                                 badns |= (1<<ns);
397                                 _res_close();
398                                 goto next_ns;
399                         }
400                         /*
401                          * Receive length & response
402                          */
403                         cp = ans;
404                         len = INT16SZ;
405                         while ((n = read(s, (char *)cp, (int)len)) > 0) {
406                                 cp += n;
407                                 if ((len -= n) <= 0)
408                                         break;
409                         }
410                         if (n <= 0) {
411                                 terrno = errno;
412                                 Perror(stderr, "read failed", errno);
413                                 _res_close();
414                                 /*
415                                  * A long running process might get its TCP
416                                  * connection reset if the remote server was
417                                  * restarted.  Requery the server instead of
418                                  * trying a new one.  When there is only one
419                                  * server, this means that a query might work
420                                  * instead of failing.  We only allow one reset
421                                  * per query to prevent looping.
422                                  */
423                                 if (terrno == ECONNRESET && !connreset) {
424                                         connreset = 1;
425                                         _res_close();
426                                         goto same_ns;
427                                 }
428                                 _res_close();
429                                 goto next_ns;
430                         }
431                         resplen = _getshort(ans);
432                         if (resplen > anssiz) {
433                                 Dprint(_res.options & RES_DEBUG,
434                                        (stdout, ";; response truncated\n")
435                                        );
436                                 truncated = 1;
437                                 len = anssiz;
438                         } else
439                                 len = resplen;
440                         cp = ans;
441                         while (len != 0 &&
442                                (n = read(s, (char *)cp, (int)len)) > 0
443                                ) {
444                                 cp += n;
445                                 len -= n;
446                         }
447                         if (n <= 0) {
448                                 terrno = errno;
449                                 Perror(stderr, "read(vc)", errno);
450                                 _res_close();
451                                 goto next_ns;
452                         }
453                         if (truncated) {
454                                 /*
455                                  * Flush rest of answer
456                                  * so connection stays in synch.
457                                  */
458                                 anhp->tc = 1;
459                                 len = resplen - anssiz;
460                                 while (len != 0) {
461                                         char junk[512];
462
463                                         n = (len > sizeof(junk)
464                                              ? sizeof(junk)
465                                              : len);
466                                         if ((n = read(s, junk, n)) > 0)
467                                                 len -= n;
468                                         else
469                                                 break;
470                                 }
471                         }
472                 } else {
473                         /*
474                          * Use datagrams.
475                          */
476                         struct timeval  timeout;
477                         fd_set          dsmask;
478                         struct sockaddr_in from;
479                         int             fromlen;
480
481                         if ((s < 0) || vc) {
482                                 if (vc)
483                                         _res_close();
484                                 s = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC);
485                                 if (s < 0) {
486  bad_dg_sock:                           terrno = errno;
487                                         Perror(stderr, "socket(dg)", errno);
488                                         return (-1);
489                                 }
490                                 connected = 0;
491                         }
492                         /*
493                          * On a 4.3BSD+ machine (client and server,
494                          * actually), sending to a nameserver datagram
495                          * port with no nameserver will cause an
496                          * ICMP port unreachable message to be returned.
497                          * If our datagram socket is "connected" to the
498                          * server, we get an ECONNREFUSED error on the next
499                          * socket operation, and select returns if the
500                          * error message is received.  We can thus detect
501                          * the absence of a nameserver without timing out.
502                          * If we have sent queries to at least two servers,
503                          * however, we don't want to remain connected,
504                          * as we wish to receive answers from the first
505                          * server to respond.
506                          */
507                         if (_res.nscount == 1 || (try == 0 && ns == 0)) {
508                                 /*
509                                  * Connect only if we are sure we won't
510                                  * receive a response from another server.
511                                  */
512                                 if (!connected) {
513                                         if (connect(s,
514                                                     (struct sockaddr *)nsap,
515                                                     sizeof(struct sockaddr)
516                                                     ) < 0
517                                             ) {
518                                                 Aerror(stderr,
519                                                        "connect(dg)",
520                                                        errno, *nsap);
521                                                 badns |= (1<<ns);
522                                                 _res_close();
523                                                 goto next_ns;
524                                         }
525                                         connected = 1;
526                                 }
527                                 if (send(s, buf, buflen, 0) != buflen) {
528                                         Perror(stderr, "send", errno);
529                                         badns |= (1<<ns);
530                                         _res_close();
531                                         goto next_ns;
532                                 }
533                         } else {
534                                 /*
535                                  * Disconnect if we want to listen
536                                  * for responses from more than one server.
537                                  */
538                                 if (connected) {
539 #if defined(BSD) && (BSD >= 199103)
540                                         struct sockaddr_in no_addr;
541
542                                         no_addr.sin_family = AF_INET;
543                                         no_addr.sin_addr.s_addr = INADDR_ANY;
544                                         no_addr.sin_port = 0;
545                                         (void) connect(s,
546                                                        (struct sockaddr *)
547                                                         &no_addr,
548                                                        sizeof(no_addr));
549 #else
550                                         int s1 = socket(AF_INET, SOCK_DGRAM,
551                                                         PF_UNSPEC);
552                                         if (s1 < 0)
553                                                 goto bad_dg_sock;
554                                         (void) dup2(s1, s);
555                                         (void) close(s1);
556                                         Dprint(_res.options & RES_DEBUG,
557                                                (stdout, ";; new DG socket\n"))
558 #endif
559                                         connected = 0;
560                                         errno = 0;
561                                 }
562                                 if (sendto(s, buf, buflen, 0,
563                                            (struct sockaddr *)nsap,
564                                            sizeof(struct sockaddr))
565                                     != buflen) {
566                                         Aerror(stderr, "sendto", errno, *nsap);
567                                         badns |= (1<<ns);
568                                         _res_close();
569                                         goto next_ns;
570                                 }
571                         }
572
573                         /*
574                          * Wait for reply
575                          */
576                         timeout.tv_sec = (_res.retrans << try);
577                         if (try > 0)
578                                 timeout.tv_sec /= _res.nscount;
579                         if ((long) timeout.tv_sec <= 0)
580                                 timeout.tv_sec = 1;
581                         timeout.tv_usec = 0;
582     wait:
583                         FD_ZERO(&dsmask);
584                         FD_SET(s, &dsmask);
585                         n = select(s+1, &dsmask, (fd_set *)NULL,
586                                    (fd_set *)NULL, &timeout);
587                         if (n < 0) {
588                                 Perror(stderr, "select", errno);
589                                 _res_close();
590                                 goto next_ns;
591                         }
592                         if (n == 0) {
593                                 /*
594                                  * timeout
595                                  */
596                                 Dprint(_res.options & RES_DEBUG,
597                                        (stdout, ";; timeout\n")
598                                        );
599                                 gotsomewhere = 1;
600                                 _res_close();
601                                 goto next_ns;
602                         }
603                         fromlen = sizeof(struct sockaddr_in);
604                         resplen = recvfrom(s, ans, anssiz, 0,
605                                            (struct sockaddr *)&from, &fromlen);
606                         if (resplen <= 0) {
607                                 Perror(stderr, "recvfrom", errno);
608                                 _res_close();
609                                 goto next_ns;
610                         }
611                         gotsomewhere = 1;
612                         if (hp->id != anhp->id) {
613                                 /*
614                                  * response from old query, ignore it.
615                                  * XXX - potential security hazard could
616                                  *       be detected here.
617                                  */
618                                 DprintQ((_res.options & RES_DEBUG) ||
619                                         (_res.pfcode & RES_PRF_REPLY),
620                                         (stdout, ";; old answer:\n"),
621                                         ans);
622                                 goto wait;
623                         }
624 #if CHECK_SRVR_ADDR
625                         if (!(_res.options & RES_INSECURE1) &&
626                             !our_server(&from)) {
627                                 /*
628                                  * response from wrong server? ignore it.
629                                  * XXX - potential security hazard could
630                                  *       be detected here.
631                                  */
632                                 DprintQ((_res.options & RES_DEBUG) ||
633                                         (_res.pfcode & RES_PRF_REPLY),
634                                         (stdout, ";; not our server:\n"),
635                                         ans);
636                                 goto wait;
637                         }
638 #endif
639                         if (!(_res.options & RES_INSECURE2) &&
640                             !queries_match(buf, buf + buflen,
641                                            ans, ans + anssiz)) {
642                                 /*
643                                  * response contains wrong query? ignore it.
644                                  * XXX - potential security hazard could
645                                  *       be detected here.
646                                  */
647                                 DprintQ((_res.options & RES_DEBUG) ||
648                                         (_res.pfcode & RES_PRF_REPLY),
649                                         (stdout, ";; wrong query name:\n"),
650                                         ans);
651                                 goto wait;
652                         }
653                         if (anhp->rcode == SERVFAIL ||
654                             anhp->rcode == NOTIMP ||
655                             anhp->rcode == REFUSED) {
656                                 DprintQ(_res.options & RES_DEBUG,
657                                         (stdout, "server rejected query:\n"),
658                                         ans);
659                                 badns |= (1<<ns);
660                                 _res_close();
661                                 goto next_ns;
662                         }
663                         if (!(_res.options & RES_IGNTC) && anhp->tc) {
664                                 /*
665                                  * get rest of answer;
666                                  * use TCP with same server.
667                                  */
668                                 Dprint(_res.options & RES_DEBUG,
669                                        (stdout, ";; truncated answer\n")
670                                        );
671                                 v_circuit = 1;
672                                 _res_close();
673                                 goto same_ns;
674                         }
675                 } /*if vc/dg*/
676                 DprintQ((_res.options & RES_DEBUG) ||
677                         (_res.pfcode & RES_PRF_REPLY),
678                         (stdout, ";; got answer:\n"),
679                         ans);
680                 /*
681                  * If using virtual circuits, we assume that the first server
682                  * is preferred over the rest (i.e. it is on the local
683                  * machine) and only keep that one open.
684                  * If we have temporarily opened a virtual circuit,
685                  * or if we haven't been asked to keep a socket open,
686                  * close the socket.
687                  */
688                 if ((v_circuit && (!(_res.options & RES_USEVC) || ns != 0)) ||
689                     !(_res.options & RES_STAYOPEN)) {
690                         _res_close();
691                 }
692                 if (Rhook) {
693                         int done = 0, loops = 0;
694
695                         do {
696                                 res_sendhookact act;
697
698                                 act = (*Rhook)(nsap,
699                                                buf,
700                                                buflen,
701                                                ans,
702                                                anssiz,
703                                                &resplen);
704                                 switch (act) {
705                                 case res_goahead:
706                                 case res_done:
707                                         done = 1;
708                                         break;
709                                 case res_nextns:
710                                         _res_close();
711                                         goto next_ns;
712                                 case res_modified:
713                                         /* give the hook another try */
714                                         if (++loops < 42) /*doug adams*/
715                                                 break;
716                                         /*FALLTHROUGH*/
717                                 case res_error:
718                                         /*FALLTHROUGH*/
719                                 default:
720                                         return (-1);
721                                 }
722                         } while (!done);
723
724                 }
725                 return (resplen);
726     next_ns: ;
727            } /*foreach ns*/
728         } /*foreach retry*/
729         _res_close();
730         if (!v_circuit) {
731                 if (!gotsomewhere)
732                         errno = ECONNREFUSED;   /* no nameservers found */
733                 else
734                         errno = ETIMEDOUT;      /* no answer obtained */
735         } else {
736                 errno = terrno;
737         }
738         return (-1);
739 }
740
741 /*
742  * This routine is for closing the socket if a virtual circuit is used and
743  * the program wants to close it.  This provides support for endhostent()
744  * which expects to close the socket.
745  *
746  * This routine is not expected to be user visible.
747  */
748 void
749 _res_close()
750 {
751         if (s >= 0) {
752                 (void) close(s);
753                 s = -1;
754                 connected = 0;
755                 vc = 0;
756         }
757 }