16826 vi: dangling pointer 'dname' to 'funkey' may be used
[illumos-gate.git] / usr / src / lib / libresolv / res_send.c
blobf64a0c7aa0dd8dd366748dfe233bad26e35b5451
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2015 Gary Mills
24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
42 * Send query to name server and wait for reply.
45 #include <sys/param.h>
46 #include <sys/time.h>
47 #include <sys/socket.h>
48 #include <sys/uio.h>
49 #include <sys/stat.h>
50 #include <netinet/in.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <arpa/nameser.h>
56 #include <arpa/inet.h>
57 #include <resolv.h>
58 #include "crossl.h"
61 * Undocumented external function in libsocket
63 extern int
64 _socket(int, int, int);
66 static int s = -1; /* socket used for communications */
67 #if BSD >= 43
68 static struct sockaddr no_addr;
69 #endif /* BSD */
72 #ifndef FD_SET
73 #define NFDBITS 32
74 #define FD_SETSIZE 32
75 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
76 #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
77 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
78 #ifdef SYSV
79 #define FD_ZERO(p) (void) memset((void *)(p), 0, sizeof (*(p)))
80 #else
81 #define FD_ZERO(p) bzero((char *)(p), sizeof (*(p)))
82 #endif
83 #endif
86 * 1247019: Kludge to time out quickly if there is no /etc/resolv.conf
87 * and a TCP connection to the local DNS server fails.
90 static int _confcheck()
92 int ns;
93 struct stat rc_stat;
94 struct sockaddr_in ns_sin;
97 /* First, we check to see if /etc/resolv.conf exists.
98 * If it doesn't, then localhost is mostlikely to be
99 * the nameserver.
101 if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) {
103 /* Next, we check to see if _res.nsaddr is set to loopback.
104 * If it isn't, it has been altered by the application
105 * explicitly and we then want to bail with success.
107 if (_res.nsaddr.sin_addr.S_un.S_addr == htonl(INADDR_LOOPBACK)) {
109 /* Lastly, we try to connect to the TCP port of the
110 * nameserver. If this fails, then we know that
111 * DNS is misconfigured and we can quickly exit.
113 ns = socket(AF_INET, SOCK_STREAM, 0);
114 IN_SET_LOOPBACK_ADDR(&ns_sin);
115 ns_sin.sin_port = htons(NAMESERVER_PORT);
116 if (connect(ns, (struct sockaddr *) &ns_sin,
117 sizeof ns_sin) == -1) {
118 close(ns);
119 return(-1);
121 else {
122 close(ns);
123 return(0);
127 return(0);
130 return (0);
134 res_send(buf, buflen, answer, anslen)
135 char *buf;
136 int buflen;
137 char *answer;
138 int anslen;
140 register int n;
141 int try, v_circuit, resplen, ns;
142 int gotsomewhere = 0;
143 #if BSD >= 43
144 int connected = 0;
145 #endif /* BSD */
146 int connreset = 0;
147 u_short id, len;
148 char *cp;
149 fd_set dsmask;
150 struct timeval timeout;
151 HEADER *hp = (HEADER *) buf;
152 HEADER *anhp = (HEADER *) answer;
153 struct iovec iov[2];
154 int terrno = ETIMEDOUT;
155 char junk[512];
157 #ifdef DEBUG
158 if (_res.options & RES_DEBUG) {
159 printf("res_send()\n");
160 p_query(buf);
162 #endif
163 if (!(_res.options & RES_INIT))
164 if (res_init() == -1) {
165 return (-1);
168 /* 1247019: Check to see if we can bailout quickly. */
169 if (_confcheck() == -1)
170 return(-1);
172 v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
173 id = hp->id;
175 * Send request, RETRY times, or until successful
177 for (try = 0; try < _res.retry; try++) {
178 for (ns = 0; ns < _res.nscount; ns++) {
179 #ifdef DEBUG
180 if (_res.options & RES_DEBUG)
181 printf("Querying server (# %d) address = %s\n",
182 ns+1, inet_ntoa(_res.nsaddr_list[ns].sin_addr));
183 #endif
184 usevc:
185 if (v_circuit) {
186 int truncated = 0;
189 * Use virtual circuit;
190 * at most one attempt per server.
192 try = _res.retry;
193 if (s < 0) {
194 s = _socket(AF_INET, SOCK_STREAM, 0);
195 if (s < 0) {
196 terrno = errno;
197 #ifdef DEBUG
198 if (_res.options & RES_DEBUG) {
199 perror("socket (vc) failed");
201 #endif
202 continue;
204 if (connect(s, (struct sockaddr *) &_res.nsaddr_list[ns],
205 sizeof (struct sockaddr)) < 0) {
206 terrno = errno;
207 #ifdef DEBUG
208 if (_res.options & RES_DEBUG) {
209 perror("connect failed");
211 #endif
212 (void) close(s);
213 s = -1;
214 continue;
218 * Send length & message
220 len = htons((u_short)buflen);
221 iov[0].iov_base = (caddr_t)&len;
222 iov[0].iov_len = sizeof (len);
223 iov[1].iov_base = buf;
224 iov[1].iov_len = buflen;
225 if (writev(s, iov, 2) != sizeof (len) +
226 buflen) {
227 terrno = errno;
228 #ifdef DEBUG
229 if (_res.options & RES_DEBUG)
230 perror("write failed");
231 #endif
232 (void) close(s);
233 s = -1;
234 continue;
237 * Receive length & response
239 cp = answer;
240 len = sizeof (short);
241 while (len != 0 && (n = read
242 (s, (char *)cp, (int)len)) > 0) {
243 cp += n;
244 len -= n;
246 if (n <= 0) {
247 terrno = errno;
248 #ifdef DEBUG
249 if (_res.options & RES_DEBUG)
250 perror("read failed");
251 #endif
252 (void) close(s);
253 s = -1;
255 * A long running process might get its TCP
256 * connection reset if the remote server was
257 * restarted. Requery the server instead of
258 * trying a new one. When there is only one
259 * server, this means that a query might work
260 * instead of failing. We only allow one reset
261 * per query to prevent looping.
263 if (terrno == ECONNRESET &&
264 !connreset) {
265 connreset = 1;
266 ns--;
268 continue;
270 cp = answer;
271 if ((resplen = ntohs(*(u_short *)cp)) >
272 anslen) {
273 #ifdef DEBUG
274 if (_res.options & RES_DEBUG)
275 fprintf(stderr,
276 "response truncated\n");
277 #endif
278 len = anslen;
279 truncated = 1;
280 } else
281 len = resplen;
282 while (len != 0 &&
283 (n = read(s, (char *)cp,
284 (int)len)) > 0) {
285 cp += n;
286 len -= n;
288 if (n <= 0) {
289 terrno = errno;
290 #ifdef DEBUG
291 if (_res.options & RES_DEBUG)
292 perror("read failed");
293 #endif
294 (void) close(s);
295 s = -1;
296 continue;
298 if (truncated) {
300 * Flush rest of answer
301 * so connection stays in synch.
303 anhp->tc = 1;
304 len = resplen - anslen;
306 * set the value of resplen to anslen,
307 * this is done because the caller
308 * assumes resplen contains the size of
309 * message read into the "answer" buffer
310 * passed in.
312 resplen = anslen;
314 while (len != 0) {
315 n = (len > sizeof (junk) ?
316 sizeof (junk) : len);
317 if ((n = read(s, junk, n)) > 0)
318 len -= n;
319 else
320 break;
323 } else {
325 * Use datagrams.
327 if (s < 0) {
328 s = _socket(AF_INET, SOCK_DGRAM, 0);
329 if (s < 0) {
330 terrno = errno;
331 #ifdef DEBUG
332 if (_res.options & RES_DEBUG) {
333 perror("socket (dg) failed");
335 #endif
336 continue;
339 #if BSD >= 43
341 * I'm tired of answering this question, so:
342 * On a 4.3BSD+ machine (client and server,
343 * actually), sending to a nameserver datagram
344 * port with no nameserver will cause an
345 * ICMP port unreachable message to be returned.
346 * If our datagram socket is "connected" to the
347 * server, we get an ECONNREFUSED error on the next
348 * socket operation, and select returns if the
349 * error message is received. We can thus detect
350 * the absence of a nameserver without timing out.
351 * If we have sent queries to at least two servers,
352 * however, we don't want to remain connected,
353 * as we wish to receive answers from the first
354 * server to respond.
356 if (_res.nscount == 1 ||
357 (try == 0 && ns == 0)) {
359 * Don't use connect if we might
360 * still receive a response
361 * from another server.
363 if (connected == 0) {
364 if (connect(s,
365 (struct sockaddr *) &_res.nsaddr_list[ns],
366 sizeof (struct sockaddr)) < 0) {
367 #ifdef DEBUG
368 if (_res.options &
369 RES_DEBUG) {
370 perror("connect");
372 #endif
373 continue;
375 connected = 1;
377 if (send(s, buf, buflen, 0) != buflen) {
378 #ifdef DEBUG
379 if (_res.options & RES_DEBUG)
380 perror("send");
381 #endif
382 continue;
384 } else {
386 * Disconnect if we want to listen for
387 * responses from more than one server.
389 if (connected) {
390 (void) connect(s, &no_addr,
391 sizeof (no_addr));
392 connected = 0;
394 #endif /* BSD */
395 if (sendto(s, buf, buflen, 0,
396 (struct sockaddr *) &_res.nsaddr_list[ns],
397 sizeof (struct sockaddr)) != buflen) {
398 #ifdef DEBUG
399 if (_res.options & RES_DEBUG)
400 perror("sendto");
401 #endif
402 continue;
404 #if BSD >= 43
406 #endif
409 * Wait for reply
411 timeout.tv_sec = (_res.retrans << try);
412 if (try > 0)
413 timeout.tv_sec /= _res.nscount;
414 if (timeout.tv_sec <= 0)
415 timeout.tv_sec = 1;
416 timeout.tv_usec = 0;
417 wait:
418 FD_ZERO(&dsmask);
419 FD_SET(s, &dsmask);
420 n = select(s+1, &dsmask, (fd_set *)NULL,
421 (fd_set *)NULL, &timeout);
422 if (n < 0) {
423 #ifdef DEBUG
424 if (_res.options & RES_DEBUG)
425 perror("select");
426 #endif
427 continue;
429 if (n == 0) {
431 * timeout
433 #ifdef DEBUG
434 if (_res.options & RES_DEBUG)
435 printf("timeout\n");
436 #endif
437 #if BSD >= 43
438 gotsomewhere = 1;
439 #endif
440 continue;
442 if ((resplen = recv(s, answer, anslen, 0))
443 <= 0) {
444 #ifdef DEBUG
445 if (_res.options & RES_DEBUG)
446 perror("recvfrom");
447 #endif
448 continue;
450 gotsomewhere = 1;
451 if (id != anhp->id) {
453 * response from old query, ignore it
455 #ifdef DEBUG
456 if (_res.options & RES_DEBUG) {
457 printf("old answer:\n");
458 p_query(answer);
460 #endif
461 goto wait;
463 if (!(_res.options & RES_IGNTC) && anhp->tc) {
465 * get rest of answer;
466 * use TCP with same server.
468 #ifdef DEBUG
469 if (_res.options & RES_DEBUG)
470 printf("truncated answer\n");
471 #endif
472 (void) close(s);
473 s = -1;
474 v_circuit = 1;
475 goto usevc;
478 #ifdef DEBUG
479 if (_res.options & RES_DEBUG) {
480 printf("got answer:\n");
481 p_query(answer);
483 #endif
485 * If using virtual circuits, we assume that the first server
486 * is preferred * over the rest (i.e. it is on the local
487 * machine) and only keep that one open.
488 * If we have temporarily opened a virtual circuit,
489 * or if we haven't been asked to keep a socket open,
490 * close the socket.
492 if ((v_circuit &&
493 ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
494 (_res.options & RES_STAYOPEN) == 0) {
495 (void) close(s);
496 s = -1;
498 return (resplen);
501 if (s >= 0) {
502 (void) close(s);
503 s = -1;
505 if (v_circuit == 0)
506 if (gotsomewhere == 0)
507 errno = ECONNREFUSED; /* no nameservers found */
508 else
509 errno = ETIMEDOUT; /* no answer obtained */
510 else
511 errno = terrno;
512 return (-1);
516 * This routine is for closing the socket if a virtual circuit is used and
517 * the program wants to close it. This provides support for endhostent()
518 * which expects to close the socket.
520 * This routine is not expected to be user visible.
522 void
523 _res_close()
525 if (s != -1) {
526 (void) close(s);
527 s = -1;