xref: /csrg-svn/lib/libc/net/res_send.c (revision 34341)
1 /*
2  * Copyright (c) 1985 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 this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #if defined(LIBC_SCCS) && !defined(lint)
14 static char sccsid[] = "@(#)res_send.c	6.20 (Berkeley) 05/19/88";
15 #endif /* LIBC_SCCS and not lint */
16 
17 /*
18  * Send query to name server and wait for reply.
19  */
20 
21 #include <sys/param.h>
22 #include <sys/time.h>
23 #include <sys/socket.h>
24 #include <sys/uio.h>
25 #include <netinet/in.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <arpa/nameser.h>
29 #include <resolv.h>
30 
31 extern int errno;
32 
33 static int s = -1;	/* socket used for communications */
34 static struct sockaddr no_addr;
35 
36 
37 #ifndef FD_SET
38 #define	NFDBITS		32
39 #define	FD_SETSIZE	32
40 #define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
41 #define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
42 #define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
43 #define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
44 #endif
45 
46 #define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
47 
48 res_send(buf, buflen, answer, anslen)
49 	char *buf;
50 	int buflen;
51 	char *answer;
52 	int anslen;
53 {
54 	register int n;
55 	int retry, v_circuit, resplen, ns;
56 	int gotsomewhere = 0, connected = 0;
57 	u_short id, len;
58 	char *cp;
59 	fd_set dsmask;
60 	struct timeval timeout;
61 	HEADER *hp = (HEADER *) buf;
62 	HEADER *anhp = (HEADER *) answer;
63 	struct iovec iov[2];
64 	int terrno = ETIMEDOUT;
65 	char junk[512];
66 
67 #ifdef DEBUG
68 	if (_res.options & RES_DEBUG) {
69 		printf("res_send()\n");
70 		p_query(buf);
71 	}
72 #endif DEBUG
73 	if (!(_res.options & RES_INIT))
74 		if (res_init() == -1) {
75 			return(-1);
76 		}
77 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
78 	id = hp->id;
79 	/*
80 	 * Send request, RETRY times, or until successful
81 	 */
82 	for (retry = _res.retry; retry > 0; retry--) {
83 	   for (ns = 0; ns < _res.nscount; ns++) {
84 #ifdef DEBUG
85 		if (_res.options & RES_DEBUG)
86 			printf("Querying server (# %d) address = %s\n", ns+1,
87 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr));
88 #endif DEBUG
89 		if (v_circuit) {
90 			int truncated = 0;
91 
92 			/*
93 			 * Use virtual circuit.
94 			 */
95 			if (s < 0) {
96 				s = socket(AF_INET, SOCK_STREAM, 0);
97 				if (s < 0) {
98 					terrno = errno;
99 #ifdef DEBUG
100 					if (_res.options & RES_DEBUG)
101 					    perror("socket failed");
102 #endif DEBUG
103 					continue;
104 				}
105 				if (connect(s, &(_res.nsaddr_list[ns]),
106 				   sizeof(struct sockaddr)) < 0) {
107 					terrno = errno;
108 #ifdef DEBUG
109 					if (_res.options & RES_DEBUG)
110 					    perror("connect failed");
111 #endif DEBUG
112 					(void) close(s);
113 					s = -1;
114 					continue;
115 				}
116 			}
117 			/*
118 			 * Send length & message
119 			 */
120 			len = htons((u_short)buflen);
121 			iov[0].iov_base = (caddr_t)&len;
122 			iov[0].iov_len = sizeof(len);
123 			iov[1].iov_base = buf;
124 			iov[1].iov_len = buflen;
125 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
126 				terrno = errno;
127 #ifdef DEBUG
128 				if (_res.options & RES_DEBUG)
129 					perror("write failed");
130 #endif DEBUG
131 				(void) close(s);
132 				s = -1;
133 				continue;
134 			}
135 			/*
136 			 * Receive length & response
137 			 */
138 			cp = answer;
139 			len = sizeof(short);
140 			while (len != 0 &&
141 			    (n = read(s, (char *)cp, (int)len)) > 0) {
142 				cp += n;
143 				len -= n;
144 			}
145 			if (n <= 0) {
146 				terrno = errno;
147 #ifdef DEBUG
148 				if (_res.options & RES_DEBUG)
149 					perror("read failed");
150 #endif DEBUG
151 				(void) close(s);
152 				s = -1;
153 				continue;
154 			}
155 			cp = answer;
156 			if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
157 #ifdef DEBUG
158 				if (_res.options & RES_DEBUG)
159 					fprintf(stderr, "response truncated\n");
160 #endif DEBUG
161 				len = anslen;
162 				truncated = 1;
163 			} else
164 				len = resplen;
165 			while (len != 0 &&
166 			   (n = read(s, (char *)cp, (int)len)) > 0) {
167 				cp += n;
168 				len -= n;
169 			}
170 			if (n <= 0) {
171 				terrno = errno;
172 #ifdef DEBUG
173 				if (_res.options & RES_DEBUG)
174 					perror("read failed");
175 #endif DEBUG
176 				(void) close(s);
177 				s = -1;
178 				continue;
179 			}
180 			if (truncated) {
181 				/*
182 				 * Flush rest of answer
183 				 * so connection stays in synch.
184 				 */
185 				anhp->tc = 1;
186 				len = resplen - anslen;
187 				while (len != 0) {
188 					n = (len > sizeof(junk) ?
189 					    sizeof(junk) : len);
190 					if ((n = read(s, junk, n)) > 0)
191 						len -= n;
192 					else
193 						break;
194 				}
195 			}
196 		} else {
197 			/*
198 			 * Use datagrams.
199 			 */
200 			if (s < 0)
201 				s = socket(AF_INET, SOCK_DGRAM, 0);
202 #if	BSD >= 43
203 			if (_res.nscount == 1 || retry == _res.retry) {
204 				/*
205 				 * Don't use connect if we might
206 				 * still receive a response
207 				 * from another server.
208 				 */
209 				if (connected == 0) {
210 					if (connect(s, &_res.nsaddr_list[ns],
211 					    sizeof(struct sockaddr)) < 0) {
212 #ifdef DEBUG
213 						if (_res.options & RES_DEBUG)
214 							perror("connect");
215 #endif DEBUG
216 						continue;
217 					}
218 					connected = 1;
219 				}
220 				if (send(s, buf, buflen, 0) != buflen) {
221 #ifdef DEBUG
222 					if (_res.options & RES_DEBUG)
223 						perror("send");
224 #endif DEBUG
225 					continue;
226 				}
227 			} else {
228 				/*
229 				 * Disconnect if we want to listen
230 				 * for responses from more than one server.
231 				 */
232 				if (connected) {
233 					(void) connect(s, &no_addr,
234 					    sizeof(no_addr));
235 					connected = 0;
236 				}
237 #endif BSD
238 				if (sendto(s, buf, buflen, 0,
239 				    &_res.nsaddr_list[ns],
240 				    sizeof(struct sockaddr)) != buflen) {
241 #ifdef DEBUG
242 					if (_res.options & RES_DEBUG)
243 						perror("sendto");
244 #endif DEBUG
245 					continue;
246 				}
247 #if	BSD >= 43
248 			}
249 #endif
250 
251 			/*
252 			 * Wait for reply
253 			 */
254 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
255 				/ _res.nscount;
256 			if (timeout.tv_sec <= 0)
257 				timeout.tv_sec = 1;
258 			timeout.tv_usec = 0;
259 wait:
260 			FD_ZERO(&dsmask);
261 			FD_SET(s, &dsmask);
262 			n = select(s+1, &dsmask, (fd_set *)NULL,
263 				(fd_set *)NULL, &timeout);
264 			if (n < 0) {
265 #ifdef DEBUG
266 				if (_res.options & RES_DEBUG)
267 					perror("select");
268 #endif DEBUG
269 				continue;
270 			}
271 			if (n == 0) {
272 				/*
273 				 * timeout
274 				 */
275 #ifdef DEBUG
276 				if (_res.options & RES_DEBUG)
277 					printf("timeout\n");
278 #endif DEBUG
279 				gotsomewhere = 1;
280 				continue;
281 			}
282 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
283 #ifdef DEBUG
284 				if (_res.options & RES_DEBUG)
285 					perror("recvfrom");
286 #endif DEBUG
287 				continue;
288 			}
289 			gotsomewhere = 1;
290 			if (id != anhp->id) {
291 				/*
292 				 * response from old query, ignore it
293 				 */
294 #ifdef DEBUG
295 				if (_res.options & RES_DEBUG) {
296 					printf("old answer:\n");
297 					p_query(answer);
298 				}
299 #endif DEBUG
300 				goto wait;
301 			}
302 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
303 				/*
304 				 * get rest of answer
305 				 */
306 #ifdef DEBUG
307 				if (_res.options & RES_DEBUG)
308 					printf("truncated answer\n");
309 #endif DEBUG
310 				(void) close(s);
311 				s = -1;
312 				/*
313 				 * retry decremented on continue
314 				 * to desired starting value
315 				 */
316 				retry = _res.retry + 1;
317 				v_circuit = 1;
318 				continue;
319 			}
320 		}
321 #ifdef DEBUG
322 		if (_res.options & RES_DEBUG) {
323 			printf("got answer:\n");
324 			p_query(answer);
325 		}
326 #endif DEBUG
327 		/*
328 		 * We are going to assume that the first server is preferred
329 		 * over the rest (i.e. it is on the local machine) and only
330 		 * keep that one open.
331 		 */
332 		if ((_res.options & KEEPOPEN) == 0 || ns != 0) {
333 			(void) close(s);
334 			s = -1;
335 		}
336 		return (resplen);
337 	   }
338 	}
339 	if (s >= 0) {
340 		(void) close(s);
341 		s = -1;
342 	}
343 	if (v_circuit == 0)
344 		if (gotsomewhere == 0)
345 			errno = ECONNREFUSED;
346 		else
347 			errno = ETIMEDOUT;
348 	else
349 		errno = terrno;
350 	return (-1);
351 }
352 
353 /*
354  * This routine is for closing the socket if a virtual circuit is used and
355  * the program wants to close it.  This provides support for endhostent()
356  * which expects to close the socket.
357  *
358  * This routine is not expected to be user visible.
359  */
360 _res_close()
361 {
362 	if (s != -1) {
363 		(void) close(s);
364 		s = -1;
365 	}
366 }
367