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