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