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