xref: /csrg-svn/lib/libc/net/res_send.c (revision 33679)
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.19 (Berkeley) 03/07/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 #endif BSD
229 			if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
230 			    sizeof(struct sockaddr)) != buflen) {
231 #ifdef DEBUG
232 				if (_res.options & RES_DEBUG)
233 					perror("sendto");
234 #endif DEBUG
235 				continue;
236 			}
237 
238 			/*
239 			 * Wait for reply
240 			 */
241 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
242 				/ _res.nscount;
243 			if (timeout.tv_sec <= 0)
244 				timeout.tv_sec = 1;
245 			timeout.tv_usec = 0;
246 wait:
247 			FD_ZERO(&dsmask);
248 			FD_SET(s, &dsmask);
249 			n = select(s+1, &dsmask, (fd_set *)NULL,
250 				(fd_set *)NULL, &timeout);
251 			if (n < 0) {
252 #ifdef DEBUG
253 				if (_res.options & RES_DEBUG)
254 					perror("select");
255 #endif DEBUG
256 				continue;
257 			}
258 			if (n == 0) {
259 				/*
260 				 * timeout
261 				 */
262 #ifdef DEBUG
263 				if (_res.options & RES_DEBUG)
264 					printf("timeout\n");
265 #endif DEBUG
266 				/*
267 				 * Disconnect if we want to listen
268 				 * for responses from more than one server.
269 				 */
270 				if (_res.nscount > 1 && connected) {
271 					(void) connect(s, &no_addr,
272 					    sizeof(no_addr));
273 					connected = 0;
274 				}
275 				gotsomewhere = 1;
276 				continue;
277 			}
278 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
279 #ifdef DEBUG
280 				if (_res.options & RES_DEBUG)
281 					perror("recvfrom");
282 #endif DEBUG
283 				continue;
284 			}
285 			gotsomewhere = 1;
286 			if (id != anhp->id) {
287 				/*
288 				 * response from old query, ignore it
289 				 */
290 #ifdef DEBUG
291 				if (_res.options & RES_DEBUG) {
292 					printf("old answer:\n");
293 					p_query(answer);
294 				}
295 #endif DEBUG
296 				goto wait;
297 			}
298 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
299 				/*
300 				 * get rest of answer
301 				 */
302 #ifdef DEBUG
303 				if (_res.options & RES_DEBUG)
304 					printf("truncated answer\n");
305 #endif DEBUG
306 				(void) close(s);
307 				s = -1;
308 				/*
309 				 * retry decremented on continue
310 				 * to desired starting value
311 				 */
312 				retry = _res.retry + 1;
313 				v_circuit = 1;
314 				continue;
315 			}
316 		}
317 #ifdef DEBUG
318 		if (_res.options & RES_DEBUG) {
319 			printf("got answer:\n");
320 			p_query(answer);
321 		}
322 #endif DEBUG
323 		/*
324 		 * We are going to assume that the first server is preferred
325 		 * over the rest (i.e. it is on the local machine) and only
326 		 * keep that one open.
327 		 */
328 		if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
329 			return (resplen);
330 		} else {
331 			(void) close(s);
332 			s = -1;
333 			return (resplen);
334 		}
335 	   }
336 	}
337 	if (s >= 0) {
338 		(void) close(s);
339 		s = -1;
340 	}
341 	if (v_circuit == 0)
342 		if (gotsomewhere == 0)
343 			errno = ECONNREFUSED;
344 		else
345 			errno = ETIMEDOUT;
346 	else
347 		errno = terrno;
348 	return (-1);
349 }
350 
351 /*
352  * This routine is for closing the socket if a virtual circuit is used and
353  * the program wants to close it.  This provides support for endhostent()
354  * which expects to close the socket.
355  *
356  * This routine is not expected to be user visible.
357  */
358 _res_close()
359 {
360 	if (s != -1) {
361 		(void) close(s);
362 		s = -1;
363 	}
364 }
365