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