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