xref: /original-bsd/lib/libc/net/res_send.c (revision c502efc5)
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.16 (Berkeley) 01/15/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 
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 (_res.nscount == 1) {
171 				/*
172 				 * Connect/Send is detrimental if you
173 				 * are going to poll more than one server
174 				 */
175 				if (connect(s, &_res.nsaddr_list[ns],
176 				    sizeof(struct sockaddr)) < 0 ||
177 				    send(s, buf, buflen, 0) != buflen) {
178 #ifdef DEBUG
179 					if (_res.options & RES_DEBUG)
180 						perror("connect");
181 #endif DEBUG
182 					continue;
183 				}
184 			} else
185 #endif BSD
186 			if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
187 			    sizeof(struct sockaddr)) != buflen) {
188 #ifdef DEBUG
189 				if (_res.options & RES_DEBUG)
190 					perror("sendto");
191 #endif DEBUG
192 				continue;
193 			}
194 
195 			/*
196 			 * Wait for reply
197 			 */
198 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
199 				/ _res.nscount;
200 			if (timeout.tv_sec <= 0)
201 				timeout.tv_sec = 1;
202 			timeout.tv_usec = 0;
203 wait:
204 			FD_ZERO(&dsmask);
205 			FD_SET(s, &dsmask);
206 			n = select(s+1, &dsmask, (fd_set *)NULL,
207 				(fd_set *)NULL, &timeout);
208 			if (n < 0) {
209 #ifdef DEBUG
210 				if (_res.options & RES_DEBUG)
211 					perror("select");
212 #endif DEBUG
213 				continue;
214 			}
215 			if (n == 0) {
216 				/*
217 				 * timeout
218 				 */
219 #ifdef DEBUG
220 				if (_res.options & RES_DEBUG)
221 					printf("timeout\n");
222 #endif DEBUG
223 				gotsomewhere = 1;
224 				continue;
225 			}
226 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
227 #ifdef DEBUG
228 				if (_res.options & RES_DEBUG)
229 					perror("recvfrom");
230 #endif DEBUG
231 				continue;
232 			}
233 			gotsomewhere = 1;
234 			if (id != anhp->id) {
235 				/*
236 				 * response from old query, ignore it
237 				 */
238 #ifdef DEBUG
239 				if (_res.options & RES_DEBUG) {
240 					printf("old answer:\n");
241 					p_query(answer);
242 				}
243 #endif DEBUG
244 				goto wait;
245 			}
246 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
247 				/*
248 				 * get rest of answer
249 				 */
250 #ifdef DEBUG
251 				if (_res.options & RES_DEBUG)
252 					printf("truncated answer\n");
253 #endif DEBUG
254 				(void) close(s);
255 				s = -1;
256 				/*
257 				 * retry decremented on continue
258 				 * to desired starting value
259 				 */
260 				retry = _res.retry + 1;
261 				v_circuit = 1;
262 				continue;
263 			}
264 		}
265 #ifdef DEBUG
266 		if (_res.options & RES_DEBUG) {
267 			printf("got answer:\n");
268 			p_query(answer);
269 		}
270 #endif DEBUG
271 		/*
272 		 * We are going to assume that the first server is preferred
273 		 * over the rest (i.e. it is on the local machine) and only
274 		 * keep that one open.
275 		 */
276 		if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
277 			return (resplen);
278 		} else {
279 			(void) close(s);
280 			s = -1;
281 			return (resplen);
282 		}
283 	   }
284 	}
285 	if (s >= 0) {
286 		(void) close(s);
287 		s = -1;
288 	}
289 	if (v_circuit == 0)
290 		if (gotsomewhere == 0)
291 			errno = ECONNREFUSED;
292 		else
293 			errno = ETIMEDOUT;
294 	else
295 		errno = terrno;
296 	return (-1);
297 }
298 
299 /*
300  * This routine is for closing the socket if a virtual circuit is used and
301  * the program wants to close it.  This provides support for endhostent()
302  * which expects to close the socket.
303  *
304  * This routine is not expected to be user visible.
305  */
306 _res_close()
307 {
308 	if (s != -1) {
309 		(void) close(s);
310 		s = -1;
311 	}
312 }
313