xref: /original-bsd/lib/libc/net/res_send.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1985, 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  * -
7  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies, and that
12  * the name of Digital Equipment Corporation not be used in advertising or
13  * publicity pertaining to distribution of the document or software without
14  * specific, written prior permission.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
17  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
19  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
20  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
21  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
22  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23  * SOFTWARE.
24  * -
25  * --Copyright--
26  */
27 
28 #if defined(LIBC_SCCS) && !defined(lint)
29 static char sccsid[] = "@(#)res_send.c	8.1 (Berkeley) 06/04/93";
30 static char rcsid[] = "$Id: res_send.c,v 4.9.1.1 1993/05/02 22:43:03 vixie Rel $";
31 #endif /* LIBC_SCCS and not lint */
32 
33 /*
34  * Send query to name server and wait for reply.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/time.h>
39 #include <sys/socket.h>
40 #include <sys/uio.h>
41 #include <netinet/in.h>
42 #include <arpa/nameser.h>
43 #include <arpa/inet.h>
44 #include <stdio.h>
45 #include <errno.h>
46 #include <resolv.h>
47 #include <unistd.h>
48 #include <string.h>
49 
50 static int s = -1;	/* socket used for communications */
51 static struct sockaddr no_addr;
52 
53 #ifndef FD_SET
54 #define	NFDBITS		32
55 #define	FD_SETSIZE	32
56 #define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
57 #define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
58 #define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
59 #define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
60 #endif
61 
62 res_send(buf, buflen, answer, anslen)
63 	const char *buf;
64 	int buflen;
65 	char *answer;
66 	int anslen;
67 {
68 	register int n;
69 	int try, v_circuit, resplen, ns;
70 	int gotsomewhere = 0, connected = 0;
71 	int connreset = 0;
72 	u_short id, len;
73 	char *cp;
74 	fd_set dsmask;
75 	struct timeval timeout;
76 	HEADER *hp = (HEADER *) buf;
77 	HEADER *anhp = (HEADER *) answer;
78 	u_int badns;		/* XXX NSMAX can't exceed #/bits per this */
79 	struct iovec iov[2];
80 	int terrno = ETIMEDOUT;
81 	char junk[512];
82 
83 #ifdef DEBUG
84 	if ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY)) {
85 		printf(";; res_send()\n");
86 		__p_query(buf);
87 	}
88 #endif
89 	if (!(_res.options & RES_INIT))
90 		if (res_init() == -1) {
91 			return(-1);
92 		}
93 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
94 	id = hp->id;
95 	badns = 0;
96 	/*
97 	 * Send request, RETRY times, or until successful
98 	 */
99 	for (try = 0; try < _res.retry; try++) {
100 	    for (ns = 0; ns < _res.nscount; ns++) {
101 		if (badns & (1<<ns))
102 			continue;
103 #ifdef DEBUG
104 		if (_res.options & RES_DEBUG)
105 			printf(";; Querying server (# %d) address = %s\n",
106 			       ns+1,
107 			       inet_ntoa(_res.nsaddr_list[ns].sin_addr));
108 #endif
109 	usevc:
110 		if (v_circuit) {
111 			int truncated = 0;
112 
113 			/*
114 			 * Use virtual circuit;
115 			 * at most one attempt per server.
116 			 */
117 			try = _res.retry;
118 			if (s < 0) {
119 				s = socket(AF_INET, SOCK_STREAM, 0);
120 				if (s < 0) {
121 					terrno = errno;
122 #ifdef DEBUG
123 					if (_res.options & RES_DEBUG)
124 					    perror("socket (vc) failed");
125 #endif
126 					continue;
127 				}
128 				if (connect(s,
129 				    (struct sockaddr *)&(_res.nsaddr_list[ns]),
130 				    sizeof(struct sockaddr)) < 0) {
131 					terrno = errno;
132 #ifdef DEBUG
133 					if (_res.options & RES_DEBUG)
134 					    perror("connect failed");
135 #endif
136 					(void) close(s);
137 					s = -1;
138 					continue;
139 				}
140 			}
141 			/*
142 			 * Send length & message
143 			 */
144 			len = htons((u_short)buflen);
145 			iov[0].iov_base = (caddr_t)&len;
146 			iov[0].iov_len = sizeof(len);
147 			iov[1].iov_base = (char *)buf;
148 			iov[1].iov_len = buflen;
149 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
150 				terrno = errno;
151 #ifdef DEBUG
152 				if (_res.options & RES_DEBUG)
153 					perror("write failed");
154 #endif
155 				(void) close(s);
156 				s = -1;
157 				continue;
158 			}
159 			/*
160 			 * Receive length & response
161 			 */
162 			cp = answer;
163 			len = sizeof(short);
164 			while (len != 0 &&
165 			    (n = read(s, (char *)cp, (int)len)) > 0) {
166 				cp += n;
167 				len -= n;
168 			}
169 			if (n <= 0) {
170 				terrno = errno;
171 #ifdef DEBUG
172 				if (_res.options & RES_DEBUG)
173 					perror("read failed");
174 #endif
175 				(void) close(s);
176 				s = -1;
177 				/*
178 				 * A long running process might get its TCP
179 				 * connection reset if the remote server was
180 				 * restarted.  Requery the server instead of
181 				 * trying a new one.  When there is only one
182 				 * server, this means that a query might work
183 				 * instead of failing.  We only allow one reset
184 				 * per query to prevent looping.
185 				 */
186 				if (terrno == ECONNRESET && !connreset) {
187 					connreset = 1;
188 					ns--;
189 				}
190 				continue;
191 			}
192 			cp = answer;
193 			if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
194 #ifdef DEBUG
195 				if (_res.options & RES_DEBUG)
196 					fprintf(stderr,
197 						";; response truncated\n");
198 #endif
199 				len = anslen;
200 				truncated = 1;
201 			} else
202 				len = resplen;
203 			while (len != 0 &&
204 			   (n = read(s, (char *)cp, (int)len)) > 0) {
205 				cp += n;
206 				len -= n;
207 			}
208 			if (n <= 0) {
209 				terrno = errno;
210 #ifdef DEBUG
211 				if (_res.options & RES_DEBUG)
212 					perror("read failed");
213 #endif
214 				(void) close(s);
215 				s = -1;
216 				continue;
217 			}
218 			if (truncated) {
219 				/*
220 				 * Flush rest of answer
221 				 * so connection stays in synch.
222 				 */
223 				anhp->tc = 1;
224 				len = resplen - anslen;
225 				while (len != 0) {
226 					n = (len > sizeof(junk) ?
227 					    sizeof(junk) : len);
228 					if ((n = read(s, junk, n)) > 0)
229 						len -= n;
230 					else
231 						break;
232 				}
233 			}
234 		} else {
235 			/*
236 			 * Use datagrams.
237 			 */
238 			if (s < 0) {
239 				s = socket(AF_INET, SOCK_DGRAM, 0);
240 				if (s < 0) {
241 					terrno = errno;
242 #ifdef DEBUG
243 					if (_res.options & RES_DEBUG)
244 					    perror("socket (dg) failed");
245 #endif
246 					continue;
247 				}
248 			}
249 			/*
250 			 * I'm tired of answering this question, so:
251 			 * On a 4.3BSD+ machine (client and server,
252 			 * actually), sending to a nameserver datagram
253 			 * port with no nameserver will cause an
254 			 * ICMP port unreachable message to be returned.
255 			 * If our datagram socket is "connected" to the
256 			 * server, we get an ECONNREFUSED error on the next
257 			 * socket operation, and select returns if the
258 			 * error message is received.  We can thus detect
259 			 * the absence of a nameserver without timing out.
260 			 * If we have sent queries to at least two servers,
261 			 * however, we don't want to remain connected,
262 			 * as we wish to receive answers from the first
263 			 * server to respond.
264 			 */
265 			if (_res.nscount == 1 || (try == 0 && ns == 0)) {
266 				/*
267 				 * Don't use connect if we might
268 				 * still receive a response
269 				 * from another server.
270 				 */
271 				if (connected == 0) {
272 					if (connect(s,
273 					    (struct sockaddr *)
274 					    &_res.nsaddr_list[ns],
275 					    sizeof(struct sockaddr)) < 0) {
276 #ifdef DEBUG
277 						if (_res.options & RES_DEBUG)
278 							perror("connect");
279 #endif
280 						continue;
281 					}
282 					connected = 1;
283 				}
284 				if (send(s, buf, buflen, 0) != buflen) {
285 #ifdef DEBUG
286 					if (_res.options & RES_DEBUG)
287 						perror("send");
288 #endif
289 					continue;
290 				}
291 			} else {
292 				/*
293 				 * Disconnect if we want to listen
294 				 * for responses from more than one server.
295 				 */
296 				if (connected) {
297 					(void) connect(s, &no_addr,
298 					    sizeof(no_addr));
299 					connected = 0;
300 				}
301 				if (sendto(s, buf, buflen, 0,
302 				    (struct sockaddr *)&_res.nsaddr_list[ns],
303 				    sizeof(struct sockaddr)) != buflen) {
304 #ifdef DEBUG
305 					if (_res.options & RES_DEBUG)
306 						perror("sendto");
307 #endif
308 					continue;
309 				}
310 			}
311 
312 			/*
313 			 * Wait for reply
314 			 */
315 			timeout.tv_sec = (_res.retrans << try);
316 			if (try > 0)
317 				timeout.tv_sec /= _res.nscount;
318 			if ((long) timeout.tv_sec <= 0)
319 				timeout.tv_sec = 1;
320 			timeout.tv_usec = 0;
321 wait:
322 			FD_ZERO(&dsmask);
323 			FD_SET(s, &dsmask);
324 			n = select(s+1, &dsmask, (fd_set *)NULL,
325 				(fd_set *)NULL, &timeout);
326 			if (n < 0) {
327 #ifdef DEBUG
328 				if (_res.options & RES_DEBUG)
329 					perror("select");
330 #endif
331 				continue;
332 			}
333 			if (n == 0) {
334 				/*
335 				 * timeout
336 				 */
337 #ifdef DEBUG
338 				if (_res.options & RES_DEBUG)
339 					printf(";; timeout\n");
340 #endif
341 				gotsomewhere = 1;
342 				continue;
343 			}
344 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
345 #ifdef DEBUG
346 				if (_res.options & RES_DEBUG)
347 					perror("recvfrom");
348 #endif
349 				continue;
350 			}
351 			gotsomewhere = 1;
352 			if (id != anhp->id) {
353 				/*
354 				 * response from old query, ignore it
355 				 */
356 #ifdef DEBUG
357 				if ((_res.options & RES_DEBUG) ||
358 				    (_res.pfcode & RES_PRF_REPLY)) {
359 					printf(";; old answer:\n");
360 					__p_query(answer);
361 				}
362 #endif
363 				goto wait;
364 			}
365 			if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP ||
366 			    anhp->rcode == REFUSED) {
367 #ifdef DEBUG
368 				if (_res.options & RES_DEBUG) {
369 					printf("server rejected query:\n");
370 					__p_query(answer);
371 				}
372 #endif
373 				badns |= (1<<ns);
374 				continue;
375 			}
376 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
377 				/*
378 				 * get rest of answer;
379 				 * use TCP with same server.
380 				 */
381 #ifdef DEBUG
382 				if (_res.options & RES_DEBUG)
383 					printf(";; truncated answer\n");
384 #endif
385 				(void) close(s);
386 				s = -1;
387 				v_circuit = 1;
388 				goto usevc;
389 			}
390 		}
391 #ifdef DEBUG
392 		if (_res.options & RES_DEBUG)
393 			printf(";; got answer:\n");
394 		if ((_res.options & RES_DEBUG) ||
395 		    (_res.pfcode & RES_PRF_REPLY))
396 			__p_query(answer);
397 #endif
398 		/*
399 		 * If using virtual circuits, we assume that the first server
400 		 * is preferred * over the rest (i.e. it is on the local
401 		 * machine) and only keep that one open.
402 		 * If we have temporarily opened a virtual circuit,
403 		 * or if we haven't been asked to keep a socket open,
404 		 * close the socket.
405 		 */
406 		if ((v_circuit &&
407 		    ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
408 		    (_res.options & RES_STAYOPEN) == 0) {
409 			(void) close(s);
410 			s = -1;
411 		}
412 		return (resplen);
413 	   }
414 	}
415 	if (s >= 0) {
416 		(void) close(s);
417 		s = -1;
418 	}
419 	if (v_circuit == 0)
420 		if (gotsomewhere == 0)
421 			errno = ECONNREFUSED;	/* no nameservers found */
422 		else
423 			errno = ETIMEDOUT;	/* no answer obtained */
424 	else
425 		errno = terrno;
426 	return (-1);
427 }
428 
429 /*
430  * This routine is for closing the socket if a virtual circuit is used and
431  * the program wants to close it.  This provides support for endhostent()
432  * which expects to close the socket.
433  *
434  * This routine is not expected to be user visible.
435  */
436 _res_close()
437 {
438 	if (s != -1) {
439 		(void) close(s);
440 		s = -1;
441 	}
442 }
443