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