xref: /illumos-gate/usr/src/lib/libresolv/res_send.c (revision 7c478bd9)
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  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * Send query to name server and wait for reply.
44  */
45 
46 #include "synonyms.h"
47 
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 
60 static int s = -1;	/* socket used for communications */
61 static 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 
82 static 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 
125 res_send(buf, buflen, answer, anslen)
126 	char *buf;
127 	int buflen;
128 	char *answer;
129 	int anslen;
130 {
131 	register int n;
132 	int try, v_circuit, resplen, ns;
133 	int gotsomewhere = 0, connected = 0;
134 	int connreset = 0;
135 	u_short id, len;
136 	char *cp;
137 	fd_set dsmask;
138 	struct timeval timeout;
139 	HEADER *hp = (HEADER *) buf;
140 	HEADER *anhp = (HEADER *) answer;
141 	struct iovec iov[2];
142 	int terrno = ETIMEDOUT;
143 	char junk[512];
144 
145 #ifdef DEBUG
146 	if (_res.options & RES_DEBUG) {
147 		printf("res_send()\n");
148 		p_query(buf);
149 	}
150 #endif DEBUG
151 	if (!(_res.options & RES_INIT))
152 		if (res_init() == -1) {
153 			return (-1);
154 		}
155 
156 	/* 1247019: Check to see if we can bailout quickly. */
157 	if (_confcheck() == -1)
158 	    return(-1);
159 
160 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
161 	id = hp->id;
162 	/*
163 	 * Send request, RETRY times, or until successful
164 	 */
165 	for (try = 0; try < _res.retry; try++) {
166 		for (ns = 0; ns < _res.nscount; ns++) {
167 #ifdef DEBUG
168 			if (_res.options & RES_DEBUG)
169 				printf("Querying server (# %d) address = %s\n",
170 				ns+1, inet_ntoa(_res.nsaddr_list[ns].sin_addr));
171 #endif DEBUG
172 		usevc:
173 			if (v_circuit) {
174 				int truncated = 0;
175 
176 				/*
177 				 * Use virtual circuit;
178 				 * at most one attempt per server.
179 				 */
180 				try = _res.retry;
181 				if (s < 0) {
182 					s = _socket(AF_INET, SOCK_STREAM, 0);
183 					if (s < 0) {
184 						terrno = errno;
185 #ifdef DEBUG
186 						if (_res.options & RES_DEBUG) {
187 						perror("socket (vc) failed");
188 						}
189 #endif DEBUG
190 						continue;
191 					}
192 					if (connect(s, (struct sockaddr *) &_res.nsaddr_list[ns],
193 						sizeof (struct sockaddr)) < 0) {
194 						terrno = errno;
195 #ifdef DEBUG
196 						if (_res.options & RES_DEBUG) {
197 						perror("connect failed");
198 						}
199 #endif DEBUG
200 						(void) close(s);
201 						s = -1;
202 						continue;
203 					}
204 				}
205 				/*
206 				 * Send length & message
207 				 */
208 				len = htons((u_short)buflen);
209 				iov[0].iov_base = (caddr_t)&len;
210 				iov[0].iov_len = sizeof (len);
211 				iov[1].iov_base = buf;
212 				iov[1].iov_len = buflen;
213 				if (writev(s, iov, 2) != sizeof (len) +
214 								buflen) {
215 					terrno = errno;
216 #ifdef DEBUG
217 					if (_res.options & RES_DEBUG)
218 						perror("write failed");
219 #endif DEBUG
220 					(void) close(s);
221 					s = -1;
222 					continue;
223 				}
224 				/*
225 				 * Receive length & response
226 				 */
227 				cp = answer;
228 				len = sizeof (short);
229 				while (len != 0 && (n = read
230 					(s, (char *)cp, (int)len)) > 0) {
231 					cp += n;
232 					len -= n;
233 				}
234 				if (n <= 0) {
235 					terrno = errno;
236 #ifdef DEBUG
237 					if (_res.options & RES_DEBUG)
238 						perror("read failed");
239 #endif DEBUG
240 					(void) close(s);
241 					s = -1;
242 				/*
243 				 * A long running process might get its TCP
244 				 * connection reset if the remote server was
245 				 * restarted.  Requery the server instead of
246 				 * trying a new one.  When there is only one
247 				 * server, this means that a query might work
248 				 * instead of failing.  We only allow one reset
249 				 * per query to prevent looping.
250 				 */
251 					if (terrno == ECONNRESET &&
252 							!connreset) {
253 						connreset = 1;
254 						ns--;
255 					}
256 					continue;
257 				}
258 				cp = answer;
259 				if ((resplen = ntohs(*(u_short *)cp)) >
260 								anslen) {
261 #ifdef DEBUG
262 					if (_res.options & RES_DEBUG)
263 						fprintf(stderr,
264 							"response truncated\n");
265 #endif DEBUG
266 					len = anslen;
267 					truncated = 1;
268 				} else
269 					len = resplen;
270 				while (len != 0 &&
271 					(n = read(s, (char *)cp,
272 							(int)len)) > 0) {
273 					cp += n;
274 					len -= n;
275 				}
276 				if (n <= 0) {
277 					terrno = errno;
278 #ifdef DEBUG
279 					if (_res.options & RES_DEBUG)
280 						perror("read failed");
281 #endif DEBUG
282 					(void) close(s);
283 					s = -1;
284 					continue;
285 				}
286 				if (truncated) {
287 					/*
288 					 * Flush rest of answer
289 					 * so connection stays in synch.
290 					 */
291 					anhp->tc = 1;
292 					len = resplen - anslen;
293 					/*
294 					 * set the value of resplen to anslen,
295 					 * this is done because the caller
296 					 * assumes resplen contains the size of
297 					 * message read into the "answer" buffer
298 					 * passed in.
299 					 */
300 					resplen = anslen;
301 
302 					while (len != 0) {
303 						n = (len > sizeof (junk) ?
304 							sizeof (junk) : len);
305 						if ((n = read(s, junk, n)) > 0)
306 							len -= n;
307 						else
308 							break;
309 					}
310 				}
311 			} else {
312 				/*
313 				 * Use datagrams.
314 				 */
315 				if (s < 0) {
316 					s = _socket(AF_INET, SOCK_DGRAM, 0);
317 					if (s < 0) {
318 						terrno = errno;
319 #ifdef DEBUG
320 						if (_res.options & RES_DEBUG) {
321 						perror("socket (dg) failed");
322 						}
323 #endif DEBUG
324 						continue;
325 					}
326 				}
327 #if	BSD >= 43
328 			/*
329 			 * I'm tired of answering this question, so:
330 			 * On a 4.3BSD+ machine (client and server,
331 			 * actually), sending to a nameserver datagram
332 			 * port with no nameserver will cause an
333 			 * ICMP port unreachable message to be returned.
334 			 * If our datagram socket is "connected" to the
335 			 * server, we get an ECONNREFUSED error on the next
336 			 * socket operation, and select returns if the
337 			 * error message is received.  We can thus detect
338 			 * the absence of a nameserver without timing out.
339 			 * If we have sent queries to at least two servers,
340 			 * however, we don't want to remain connected,
341 			 * as we wish to receive answers from the first
342 			 * server to respond.
343 			 */
344 				if (_res.nscount == 1 ||
345 						(try == 0 && ns == 0)) {
346 					/*
347 					 * Don't use connect if we might
348 					 * still receive a response
349 					 * from another server.
350 					 */
351 					if (connected == 0) {
352 						if (connect(s,
353 						(struct sockaddr *) &_res.nsaddr_list[ns],
354 						sizeof (struct sockaddr)) < 0) {
355 #ifdef DEBUG
356 							if (_res.options &
357 								RES_DEBUG) {
358 							perror("connect");
359 							}
360 #endif DEBUG
361 							continue;
362 						}
363 						connected = 1;
364 					}
365 					if (send(s, buf, buflen, 0) != buflen) {
366 #ifdef DEBUG
367 						if (_res.options & RES_DEBUG)
368 							perror("send");
369 #endif DEBUG
370 						continue;
371 					}
372 				} else {
373 					/*
374 					 * Disconnect if we want to listen for
375 					 * responses from more than one server.
376 					 */
377 					if (connected) {
378 						(void) connect(s, &no_addr,
379 							sizeof (no_addr));
380 						connected = 0;
381 					}
382 #endif BSD
383 					if (sendto(s, buf, buflen, 0,
384 						(struct sockaddr *) &_res.nsaddr_list[ns],
385 					sizeof (struct sockaddr)) != buflen) {
386 #ifdef DEBUG
387 						if (_res.options & RES_DEBUG)
388 							perror("sendto");
389 #endif DEBUG
390 						continue;
391 					}
392 #if	BSD >= 43
393 				}
394 #endif
395 
396 				/*
397 				 * Wait for reply
398 				 */
399 				timeout.tv_sec = (_res.retrans << try);
400 				if (try > 0)
401 					timeout.tv_sec /= _res.nscount;
402 				if (timeout.tv_sec <= 0)
403 					timeout.tv_sec = 1;
404 				timeout.tv_usec = 0;
405 wait:
406 				FD_ZERO(&dsmask);
407 				FD_SET(s, &dsmask);
408 				n = select(s+1, &dsmask, (fd_set *)NULL,
409 						(fd_set *)NULL, &timeout);
410 				if (n < 0) {
411 #ifdef DEBUG
412 					if (_res.options & RES_DEBUG)
413 						perror("select");
414 #endif DEBUG
415 					continue;
416 				}
417 				if (n == 0) {
418 					/*
419 					 * timeout
420 					 */
421 #ifdef DEBUG
422 					if (_res.options & RES_DEBUG)
423 						printf("timeout\n");
424 #endif DEBUG
425 #if BSD >= 43
426 					gotsomewhere = 1;
427 #endif
428 					continue;
429 				}
430 				if ((resplen = recv(s, answer, anslen, 0))
431 									<= 0) {
432 #ifdef DEBUG
433 					if (_res.options & RES_DEBUG)
434 						perror("recvfrom");
435 #endif DEBUG
436 					continue;
437 				}
438 				gotsomewhere = 1;
439 				if (id != anhp->id) {
440 					/*
441 					 * response from old query, ignore it
442 					 */
443 #ifdef DEBUG
444 					if (_res.options & RES_DEBUG) {
445 						printf("old answer:\n");
446 						p_query(answer);
447 					}
448 #endif DEBUG
449 					goto wait;
450 				}
451 				if (!(_res.options & RES_IGNTC) && anhp->tc) {
452 					/*
453 					 * get rest of answer;
454 					 * use TCP with same server.
455 					 */
456 #ifdef DEBUG
457 					if (_res.options & RES_DEBUG)
458 						printf("truncated answer\n");
459 #endif DEBUG
460 					(void) close(s);
461 					s = -1;
462 					v_circuit = 1;
463 					goto usevc;
464 				}
465 			}
466 #ifdef DEBUG
467 			if (_res.options & RES_DEBUG) {
468 				printf("got answer:\n");
469 				p_query(answer);
470 			}
471 #endif DEBUG
472 		/*
473 		 * If using virtual circuits, we assume that the first server
474 		 * is preferred * over the rest (i.e. it is on the local
475 		 * machine) and only keep that one open.
476 		 * If we have temporarily opened a virtual circuit,
477 		 * or if we haven't been asked to keep a socket open,
478 		 * close the socket.
479 		 */
480 			if ((v_circuit &&
481 				((_res.options & RES_USEVC) == 0 || ns != 0)) ||
482 				(_res.options & RES_STAYOPEN) == 0) {
483 				(void) close(s);
484 				s = -1;
485 			}
486 			return (resplen);
487 		}
488 	}
489 	if (s >= 0) {
490 		(void) close(s);
491 		s = -1;
492 	}
493 	if (v_circuit == 0)
494 		if (gotsomewhere == 0)
495 			errno = ECONNREFUSED;	/* no nameservers found */
496 		else
497 			errno = ETIMEDOUT;	/* no answer obtained */
498 	else
499 		errno = terrno;
500 	return (-1);
501 }
502 
503 /*
504  * This routine is for closing the socket if a virtual circuit is used and
505  * the program wants to close it.  This provides support for endhostent()
506  * which expects to close the socket.
507  *
508  * This routine is not expected to be user visible.
509  */
510 _res_close()
511 {
512 	if (s != -1) {
513 		(void) close(s);
514 		s = -1;
515 	}
516 }
517