fix fdset for 4.2bsd sites
[unix-history] / usr / src / lib / libc / net / res_send.c
... / ...
CommitLineData
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)
9static char sccsid[] = "@(#)res_send.c 6.15 (Berkeley) %G%";
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
26extern int errno;
27
28static 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
42res_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;
197wait:
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}