entered into RCS
[kopensolaris-gnu/glibc.git] / resolv / res_send.c
1 /*
2  * ++Copyright++ 1985, 1989
3  * -
4  * Copyright (c) 1985, 1989 Regents of the University of California.
5  * 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  6.27 (Berkeley) 2/24/91";
58 static char rcsid[] = "$Id$";
59 #endif /* LIBC_SCCS and not lint */
60
61 /*
62  * Send query to name server and wait for reply.
63  */
64
65 #include <sys/param.h>
66 #include <sys/time.h>
67 #include <sys/socket.h>
68 #include <sys/uio.h>
69 #include <netinet/in.h>
70 #include <arpa/nameser.h>
71 #include <arpa/inet.h>
72 #include <stdio.h>
73 #include <errno.h>
74 #include <resolv.h>
75 #include "../conf/portability.h"
76
77 static int s = -1;      /* socket used for communications */
78 static struct sockaddr no_addr;
79
80 #ifndef FD_SET
81 #define NFDBITS         32
82 #define FD_SETSIZE      32
83 #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
84 #define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
85 #define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
86 #define FD_ZERO(p)      bzero((char *)(p), sizeof(*(p)))
87 #endif
88
89 res_send(buf, buflen, answer, anslen)
90         const char *buf;
91         int buflen;
92         char *answer;
93         int anslen;
94 {
95         register int n;
96         int try, v_circuit, resplen, ns;
97         int gotsomewhere = 0, connected = 0;
98         int connreset = 0;
99         u_short id, len;
100         char *cp;
101         fd_set dsmask;
102         struct timeval timeout;
103         HEADER *hp = (HEADER *) buf;
104         HEADER *anhp = (HEADER *) answer;
105         u_int badns;            /* XXX NSMAX can't exceed #/bits per this */
106         struct iovec iov[2];
107         int terrno = ETIMEDOUT;
108         char junk[512];
109
110 #ifdef DEBUG
111         if ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY)) {
112                 printf(";; res_send()\n");
113                 __p_query(buf);
114         }
115 #endif
116         if (!(_res.options & RES_INIT))
117                 if (res_init() == -1) {
118                         return(-1);
119                 }
120         v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
121         id = hp->id;
122         badns = 0;
123         /*
124          * Send request, RETRY times, or until successful
125          */
126         for (try = 0; try < _res.retry; try++) {
127             for (ns = 0; ns < _res.nscount; ns++) {
128                 if (badns & (1<<ns))
129                         continue;
130 #ifdef DEBUG
131                 if (_res.options & RES_DEBUG)
132                         printf(";; Querying server (# %d) address = %s\n",
133                                ns+1,
134                                inet_ntoa(_res.nsaddr_list[ns].sin_addr));
135 #endif
136         usevc:
137                 if (v_circuit) {
138                         int truncated = 0;
139
140                         /*
141                          * Use virtual circuit;
142                          * at most one attempt per server.
143                          */
144                         try = _res.retry;
145                         if (s < 0) {
146                                 s = socket(AF_INET, SOCK_STREAM, 0);
147                                 if (s < 0) {
148                                         terrno = errno;
149 #ifdef DEBUG
150                                         if (_res.options & RES_DEBUG)
151                                             perror("socket (vc) failed");
152 #endif
153                                         continue;
154                                 }
155                                 if (connect(s,
156                                     (struct sockaddr *)&(_res.nsaddr_list[ns]),
157                                     sizeof(struct sockaddr)) < 0) {
158                                         terrno = errno;
159 #ifdef DEBUG
160                                         if (_res.options & RES_DEBUG)
161                                             perror("connect failed");
162 #endif
163                                         (void) close(s);
164                                         s = -1;
165                                         continue;
166                                 }
167                         }
168                         /*
169                          * Send length & message
170                          */
171                         len = htons((u_short)buflen);
172                         iov[0].iov_base = (caddr_t)&len;
173                         iov[0].iov_len = sizeof(len);
174                         iov[1].iov_base = (char *)buf;
175                         iov[1].iov_len = buflen;
176                         if (writev(s, iov, 2) != sizeof(len) + buflen) {
177                                 terrno = errno;
178 #ifdef DEBUG
179                                 if (_res.options & RES_DEBUG)
180                                         perror("write failed");
181 #endif
182                                 (void) close(s);
183                                 s = -1;
184                                 continue;
185                         }
186                         /*
187                          * Receive length & response
188                          */
189                         cp = answer;
190                         len = sizeof(short);
191                         while (len != 0 &&
192                             (n = read(s, (char *)cp, (int)len)) > 0) {
193                                 cp += n;
194                                 len -= n;
195                         }
196                         if (n <= 0) {
197                                 terrno = errno;
198 #ifdef DEBUG
199                                 if (_res.options & RES_DEBUG)
200                                         perror("read failed");
201 #endif
202                                 (void) close(s);
203                                 s = -1;
204                                 /*
205                                  * A long running process might get its TCP
206                                  * connection reset if the remote server was
207                                  * restarted.  Requery the server instead of
208                                  * trying a new one.  When there is only one
209                                  * server, this means that a query might work
210                                  * instead of failing.  We only allow one reset
211                                  * per query to prevent looping.
212                                  */
213                                 if (terrno == ECONNRESET && !connreset) {
214                                         connreset = 1;
215                                         ns--;
216                                 }
217                                 continue;
218                         }
219                         cp = answer;
220                         if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
221 #ifdef DEBUG
222                                 if (_res.options & RES_DEBUG)
223                                         fprintf(stderr,
224                                                 ";; response truncated\n");
225 #endif
226                                 len = anslen;
227                                 truncated = 1;
228                         } else
229                                 len = resplen;
230                         while (len != 0 &&
231                            (n = read(s, (char *)cp, (int)len)) > 0) {
232                                 cp += n;
233                                 len -= n;
234                         }
235                         if (n <= 0) {
236                                 terrno = errno;
237 #ifdef DEBUG
238                                 if (_res.options & RES_DEBUG)
239                                         perror("read failed");
240 #endif
241                                 (void) close(s);
242                                 s = -1;
243                                 continue;
244                         }
245                         if (truncated) {
246                                 /*
247                                  * Flush rest of answer
248                                  * so connection stays in synch.
249                                  */
250                                 anhp->tc = 1;
251                                 len = resplen - anslen;
252                                 while (len != 0) {
253                                         n = (len > sizeof(junk) ?
254                                             sizeof(junk) : len);
255                                         if ((n = read(s, junk, n)) > 0)
256                                                 len -= n;
257                                         else
258                                                 break;
259                                 }
260                         }
261                 } else {
262                         /*
263                          * Use datagrams.
264                          */
265                         if (s < 0) {
266                                 s = socket(AF_INET, SOCK_DGRAM, 0);
267                                 if (s < 0) {
268                                         terrno = errno;
269 #ifdef DEBUG
270                                         if (_res.options & RES_DEBUG)
271                                             perror("socket (dg) failed");
272 #endif
273                                         continue;
274                                 }
275                         }
276 #if     BSD >= 43
277                         /*
278                          * I'm tired of answering this question, so:
279                          * On a 4.3BSD+ machine (client and server,
280                          * actually), sending to a nameserver datagram
281                          * port with no nameserver will cause an
282                          * ICMP port unreachable message to be returned.
283                          * If our datagram socket is "connected" to the
284                          * server, we get an ECONNREFUSED error on the next
285                          * socket operation, and select returns if the
286                          * error message is received.  We can thus detect
287                          * the absence of a nameserver without timing out.
288                          * If we have sent queries to at least two servers,
289                          * however, we don't want to remain connected,
290                          * as we wish to receive answers from the first
291                          * server to respond.
292                          */
293                         if (_res.nscount == 1 || (try == 0 && ns == 0)) {
294                                 /*
295                                  * Don't use connect if we might
296                                  * still receive a response
297                                  * from another server.
298                                  */
299                                 if (connected == 0) {
300                                         if (connect(s,
301                                                     (struct sockaddr *)
302                                                          &_res.nsaddr_list[ns],
303                                                     sizeof(struct sockaddr)
304                                                     ) < 0) {
305 #ifdef DEBUG
306                                                 if (_res.options & RES_DEBUG)
307                                                         perror("connect");
308 #endif
309                                                 continue;
310                                         }
311                                         connected = 1;
312                                 }
313                                 if (send(s, buf, buflen, 0) != buflen) {
314 #ifdef DEBUG
315                                         if (_res.options & RES_DEBUG)
316                                                 perror("send");
317 #endif
318                                         continue;
319                                 }
320                         } else {
321                                 /*
322                                  * Disconnect if we want to listen
323                                  * for responses from more than one server.
324                                  */
325                                 if (connected) {
326                                         (void) connect(s, &no_addr,
327                                             sizeof(no_addr));
328                                         connected = 0;
329                                 }
330 #endif /* BSD */
331                                 if (sendto(s, buf, buflen, 0,
332                                     (struct sockaddr *)&_res.nsaddr_list[ns],
333                                     sizeof(struct sockaddr)) != buflen) {
334 #ifdef DEBUG
335                                         if (_res.options & RES_DEBUG)
336                                                 perror("sendto");
337 #endif
338                                         continue;
339                                 }
340 #if     BSD >= 43
341                         }
342 #endif
343
344                         /*
345                          * Wait for reply
346                          */
347                         timeout.tv_sec = (_res.retrans << try);
348                         if (try > 0)
349                                 timeout.tv_sec /= _res.nscount;
350                         if ((long) timeout.tv_sec <= 0)
351                                 timeout.tv_sec = 1;
352                         timeout.tv_usec = 0;
353 wait:
354                         FD_ZERO(&dsmask);
355                         FD_SET(s, &dsmask);
356                         n = select(s+1, &dsmask, (fd_set *)NULL,
357                                 (fd_set *)NULL, &timeout);
358                         if (n < 0) {
359 #ifdef DEBUG
360                                 if (_res.options & RES_DEBUG)
361                                         perror("select");
362 #endif
363                                 continue;
364                         }
365                         if (n == 0) {
366                                 /*
367                                  * timeout
368                                  */
369 #ifdef DEBUG
370                                 if (_res.options & RES_DEBUG)
371                                         printf(";; timeout\n");
372 #endif
373 #if BSD >= 43
374                                 gotsomewhere = 1;
375 #endif
376                                 continue;
377                         }
378                         if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
379 #ifdef DEBUG
380                                 if (_res.options & RES_DEBUG)
381                                         perror("recvfrom");
382 #endif
383                                 continue;
384                         }
385                         gotsomewhere = 1;
386                         if (id != anhp->id) {
387                                 /*
388                                  * response from old query, ignore it
389                                  */
390 #ifdef DEBUG
391                                 if ((_res.options & RES_DEBUG) ||
392                                     (_res.pfcode & RES_PRF_REPLY)) {
393                                         printf(";; old answer:\n");
394                                         __p_query(answer);
395                                 }
396 #endif
397                                 goto wait;
398                         }
399                         if (anhp->rcode == SERVFAIL
400                             || anhp->rcode == NOTIMP
401                             || anhp->rcode == REFUSED) {
402 #ifdef DEBUG
403                                 if (_res.options & RES_DEBUG) {
404                                         printf("server rejected query:\n");
405                                         __p_query(answer);
406                                 }
407 #endif DEBUG
408                                 badns |= (1<<ns);
409                                 continue;
410                         }
411                         if (!(_res.options & RES_IGNTC) && anhp->tc) {
412                                 /*
413                                  * get rest of answer;
414                                  * use TCP with same server.
415                                  */
416 #ifdef DEBUG
417                                 if (_res.options & RES_DEBUG)
418                                         printf(";; truncated answer\n");
419 #endif
420                                 (void) close(s);
421                                 s = -1;
422                                 v_circuit = 1;
423                                 goto usevc;
424                         }
425                 }
426 #ifdef DEBUG
427                 if (_res.options & RES_DEBUG)
428                         printf(";; got answer:\n");
429                 if ((_res.options & RES_DEBUG)
430                     || (_res.pfcode & RES_PRF_REPLY)) {
431                         __p_query(answer);
432                 }
433 #endif
434                 /*
435                  * If using virtual circuits, we assume that the first server
436                  * is preferred * over the rest (i.e. it is on the local
437                  * machine) and only keep that one open.
438                  * If we have temporarily opened a virtual circuit,
439                  * or if we haven't been asked to keep a socket open,
440                  * close the socket.
441                  */
442                 if ((v_circuit &&
443                     ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
444                     (_res.options & RES_STAYOPEN) == 0) {
445                         (void) close(s);
446                         s = -1;
447                 }
448                 return (resplen);
449            }
450         }
451         if (s >= 0) {
452                 (void) close(s);
453                 s = -1;
454         }
455         if (v_circuit == 0)
456                 if (gotsomewhere == 0)
457                         errno = ECONNREFUSED;   /* no nameservers found */
458                 else
459                         errno = ETIMEDOUT;      /* no answer obtained */
460         else
461                 errno = terrno;
462         return (-1);
463 }
464
465 /*
466  * This routine is for closing the socket if a virtual circuit is used and
467  * the program wants to close it.  This provides support for endhostent()
468  * which expects to close the socket.
469  *
470  * This routine is not expected to be user visible.
471  */
472 _res_close()
473 {
474         if (s != -1) {
475                 (void) close(s);
476                 s = -1;
477         }
478 }