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