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