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