xref: /illumos-gate/usr/src/lib/libresolv/res_send.c (revision bd0e95e6)
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 2015 Gary Mills
24  * Copyright 2008 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 /*
42  * Send query to name server and wait for reply.
43  */
44 
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"
59 
60 /*
61  * Undocumented external function in libsocket
62  */
63 extern int
64 _socket(int, int, int);
65 
66 static int s = -1;	/* socket used for communications */
67 #if	BSD >= 43
68 static struct sockaddr no_addr;
69 #endif /* BSD */
70 
71 
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
84 
85 /*
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.
88  */
89 
90 static int _confcheck()
91 {
92 	int ns;
93 	struct stat rc_stat;
94 	struct sockaddr_in ns_sin;
95 
96 
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.
100 	 */
101 	if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) {
102 
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.
106 		 */
107 		if (_res.nsaddr.sin_addr.S_un.S_addr == htonl(INADDR_LOOPBACK)) {
108 
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.
112 			 */
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);
120 			}
121 			else {
122 				close(ns);
123 				return(0);
124 			}
125 		}
126 
127 		return(0);
128 	}
129 
130 	return (0);
131 }
132 
133 int
134 res_send(buf, buflen, answer, anslen)
135 	char *buf;
136 	int buflen;
137 	char *answer;
138 	int anslen;
139 {
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];
156 
157 #ifdef DEBUG
158 	if (_res.options & RES_DEBUG) {
159 		printf("res_send()\n");
160 		p_query(buf);
161 	}
162 #endif
163 	if (!(_res.options & RES_INIT))
164 		if (res_init() == -1) {
165 			return (-1);
166 		}
167 
168 	/* 1247019: Check to see if we can bailout quickly. */
169 	if (_confcheck() == -1)
170 	    return(-1);
171 
172 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
173 	id = hp->id;
174 	/*
175 	 * Send request, RETRY times, or until successful
176 	 */
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;
187 
188 				/*
189 				 * Use virtual circuit;
190 				 * at most one attempt per server.
191 				 */
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");
200 						}
201 #endif
202 						continue;
203 					}
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");
210 						}
211 #endif
212 						(void) close(s);
213 						s = -1;
214 						continue;
215 					}
216 				}
217 				/*
218 				 * Send length & message
219 				 */
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;
235 				}
236 				/*
237 				 * Receive length & response
238 				 */
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;
245 				}
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;
254 				/*
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.
262 				 */
263 					if (terrno == ECONNRESET &&
264 							!connreset) {
265 						connreset = 1;
266 						ns--;
267 					}
268 					continue;
269 				}
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;
287 				}
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;
297 				}
298 				if (truncated) {
299 					/*
300 					 * Flush rest of answer
301 					 * so connection stays in synch.
302 					 */
303 					anhp->tc = 1;
304 					len = resplen - anslen;
305 					/*
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.
311 					 */
312 					resplen = anslen;
313 
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;
321 					}
322 				}
323 			} else {
324 				/*
325 				 * Use datagrams.
326 				 */
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");
334 						}
335 #endif
336 						continue;
337 					}
338 				}
339 #if	BSD >= 43
340 			/*
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.
355 			 */
356 				if (_res.nscount == 1 ||
357 						(try == 0 && ns == 0)) {
358 					/*
359 					 * Don't use connect if we might
360 					 * still receive a response
361 					 * from another server.
362 					 */
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");
371 							}
372 #endif
373 							continue;
374 						}
375 						connected = 1;
376 					}
377 					if (send(s, buf, buflen, 0) != buflen) {
378 #ifdef DEBUG
379 						if (_res.options & RES_DEBUG)
380 							perror("send");
381 #endif
382 						continue;
383 					}
384 				} else {
385 					/*
386 					 * Disconnect if we want to listen for
387 					 * responses from more than one server.
388 					 */
389 					if (connected) {
390 						(void) connect(s, &no_addr,
391 							sizeof (no_addr));
392 						connected = 0;
393 					}
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;
403 					}
404 #if	BSD >= 43
405 				}
406 #endif
407 
408 				/*
409 				 * Wait for reply
410 				 */
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;
428 				}
429 				if (n == 0) {
430 					/*
431 					 * timeout
432 					 */
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;
441 				}
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;
449 				}
450 				gotsomewhere = 1;
451 				if (id != anhp->id) {
452 					/*
453 					 * response from old query, ignore it
454 					 */
455 #ifdef DEBUG
456 					if (_res.options & RES_DEBUG) {
457 						printf("old answer:\n");
458 						p_query(answer);
459 					}
460 #endif
461 					goto wait;
462 				}
463 				if (!(_res.options & RES_IGNTC) && anhp->tc) {
464 					/*
465 					 * get rest of answer;
466 					 * use TCP with same server.
467 					 */
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;
476 				}
477 			}
478 #ifdef DEBUG
479 			if (_res.options & RES_DEBUG) {
480 				printf("got answer:\n");
481 				p_query(answer);
482 			}
483 #endif
484 		/*
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.
491 		 */
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;
497 			}
498 			return (resplen);
499 		}
500 	}
501 	if (s >= 0) {
502 		(void) close(s);
503 		s = -1;
504 	}
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);
513 }
514 
515 /*
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.
519  *
520  * This routine is not expected to be user visible.
521  */
522 void
523 _res_close()
524 {
525 	if (s != -1) {
526 		(void) close(s);
527 		s = -1;
528 	}
529 }
530