xref: /illumos-gate/usr/src/lib/libresolv/res_send.c (revision 6a1c6faa6f0834799d7de3e77fac2ec32d923f9a)
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 2005 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 int
126 res_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;
406 wait:
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  */
511 void
512 _res_close()
513 {
514 	if (s != -1) {
515 		(void) close(s);
516 		s = -1;
517 	}
518 }
519