fix fdset for 4.2bsd sites
[unix-history] / usr / src / lib / libc / net / res_send.c
CommitLineData
0b8992ca 1
b423e985 2/*
8ea4199d
DF
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.
b423e985
RC
6 */
7
2ce81398 8#if defined(LIBC_SCCS) && !defined(lint)
851ee25c 9static char sccsid[] = "@(#)res_send.c 6.15 (Berkeley) %G%";
2ce81398 10#endif LIBC_SCCS and not lint
8ea4199d 11
616a94ba
RC
12/*
13 * Send query to name server and wait for reply.
14 */
15
04a773cb 16#include <sys/param.h>
616a94ba
RC
17#include <sys/time.h>
18#include <sys/socket.h>
679f3274 19#include <sys/uio.h>
616a94ba
RC
20#include <netinet/in.h>
21#include <stdio.h>
22#include <errno.h>
27df2740 23#include <arpa/nameser.h>
f94b2f50 24#include <resolv.h>
616a94ba
RC
25
26extern int errno;
27
3126f4c0 28static int s = -1; /* socket used for communications */
15537e14
KD
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
3126f4c0 39
a5d4a4c0
JB
40#define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
41
31f0ead0 42res_send(buf, buflen, answer, anslen)
616a94ba
RC
43 char *buf;
44 int buflen;
45 char *answer;
46 int anslen;
47{
48 register int n;
a5d4a4c0 49 int retry, v_circuit, resplen, ns;
92a82fe4 50 int gotsomewhere = 0;
616a94ba
RC
51 u_short id, len;
52 char *cp;
ac3ceb42 53 fd_set dsmask;
616a94ba
RC
54 struct timeval timeout;
55 HEADER *hp = (HEADER *) buf;
56 HEADER *anhp = (HEADER *) answer;
679f3274 57 struct iovec iov[2];
1ea504e8 58 int terrno = ETIMEDOUT;
616a94ba 59
ae94a224 60#ifdef DEBUG
616a94ba 61 if (_res.options & RES_DEBUG) {
31f0ead0 62 printf("res_send()\n");
616a94ba
RC
63 p_query(buf);
64 }
0b8992ca 65#endif DEBUG
31f0ead0 66 if (!(_res.options & RES_INIT))
ae94a224
JB
67 if (res_init() == -1) {
68 return(-1);
69 }
616a94ba
RC
70 v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
71 id = hp->id;
72 /*
73 * Send request, RETRY times, or until successful
74 */
09af7f8a 75 for (retry = _res.retry; retry > 0; retry--) {
0b8992ca
KD
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,
747d796e 80 inet_ntoa(_res.nsaddr_list[ns].sin_addr.s_addr));
0b8992ca 81#endif DEBUG
616a94ba
RC
82 if (v_circuit) {
83 /*
84 * Use virtual circuit.
85 */
a5d4a4c0 86 if (s < 0) {
616a94ba 87 s = socket(AF_INET, SOCK_STREAM, 0);
92a82fe4 88 if (s < 0) {
1ea504e8 89 terrno = errno;
92a82fe4
MK
90#ifdef DEBUG
91 if (_res.options & RES_DEBUG)
747d796e 92 perror("socket failed");
92a82fe4
MK
93#endif DEBUG
94 continue;
95 }
747d796e 96 if (connect(s, &(_res.nsaddr_list[ns]),
a5d4a4c0 97 sizeof(struct sockaddr)) < 0) {
1ea504e8 98 terrno = errno;
ae94a224 99#ifdef DEBUG
a5d4a4c0 100 if (_res.options & RES_DEBUG)
747d796e 101 perror("connect failed");
0b8992ca 102#endif DEBUG
a5d4a4c0
JB
103 (void) close(s);
104 s = -1;
105 continue;
106 }
616a94ba
RC
107 }
108 /*
109 * Send length & message
110 */
3126f4c0 111 len = htons((u_short)buflen);
679f3274
JB
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) {
1ea504e8 117 terrno = errno;
ae94a224 118#ifdef DEBUG
616a94ba 119 if (_res.options & RES_DEBUG)
747d796e 120 perror("write failed");
0b8992ca 121#endif DEBUG
616a94ba
RC
122 (void) close(s);
123 s = -1;
124 continue;
125 }
126 /*
127 * Receive length & response
128 */
129 cp = answer;
130 len = sizeof(short);
679f3274 131 while (len != 0 &&
747d796e 132 (n = read(s, (char *)cp, (int)len)) > 0) {
616a94ba
RC
133 cp += n;
134 len -= n;
135 }
136 if (n <= 0) {
1ea504e8 137 terrno = errno;
ae94a224 138#ifdef DEBUG
616a94ba 139 if (_res.options & RES_DEBUG)
747d796e 140 perror("read failed");
0b8992ca 141#endif DEBUG
616a94ba
RC
142 (void) close(s);
143 s = -1;
144 continue;
145 }
146 cp = answer;
3126f4c0 147 resplen = len = ntohs(*(u_short *)cp);
679f3274 148 while (len != 0 &&
747d796e 149 (n = read(s, (char *)cp, (int)len)) > 0) {
616a94ba
RC
150 cp += n;
151 len -= n;
152 }
153 if (n <= 0) {
1ea504e8 154 terrno = errno;
ae94a224 155#ifdef DEBUG
616a94ba 156 if (_res.options & RES_DEBUG)
747d796e 157 perror("read failed");
0b8992ca 158#endif DEBUG
616a94ba
RC
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);
92a82fe4 169#if BSD >= 43
87b12937
MK
170 if (connect(s, &_res.nsaddr_list[ns],
171 sizeof(struct sockaddr)) < 0 ||
172 send(s, buf, buflen, 0) != buflen) {
ae94a224 173#ifdef DEBUG
747d796e
KD
174 if (_res.options & RES_DEBUG)
175 perror("connect");
0b8992ca 176#endif DEBUG
92a82fe4
MK
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
747d796e
KD
183 if (_res.options & RES_DEBUG)
184 perror("sendto");
92a82fe4
MK
185#endif DEBUG
186 continue;
616a94ba 187 }
92a82fe4 188#endif BSD
616a94ba 189 /*
747d796e 190 * Wait for reply
616a94ba 191 */
747d796e
KD
192 timeout.tv_sec = (_res.retrans << (_res.retry - retry))
193 / _res.nscount;
194 if (timeout.tv_sec <= 0)
195 timeout.tv_sec = 1;
616a94ba 196 timeout.tv_usec = 0;
87b12937 197wait:
ac3ceb42
KD
198 FD_ZERO(&dsmask);
199 FD_SET(s, &dsmask);
679f3274
JB
200 n = select(s+1, &dsmask, (fd_set *)NULL,
201 (fd_set *)NULL, &timeout);
616a94ba 202 if (n < 0) {
ae94a224 203#ifdef DEBUG
616a94ba 204 if (_res.options & RES_DEBUG)
747d796e 205 perror("select");
0b8992ca 206#endif DEBUG
616a94ba
RC
207 continue;
208 }
209 if (n == 0) {
210 /*
211 * timeout
212 */
ae94a224 213#ifdef DEBUG
616a94ba
RC
214 if (_res.options & RES_DEBUG)
215 printf("timeout\n");
0b8992ca 216#endif DEBUG
92a82fe4 217 gotsomewhere = 1;
616a94ba
RC
218 continue;
219 }
dd7a6a73 220 if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
ae94a224 221#ifdef DEBUG
616a94ba 222 if (_res.options & RES_DEBUG)
747d796e 223 perror("recvfrom");
0b8992ca 224#endif DEBUG
616a94ba
RC
225 continue;
226 }
92a82fe4 227 gotsomewhere = 1;
616a94ba
RC
228 if (id != anhp->id) {
229 /*
230 * response from old query, ignore it
231 */
ae94a224 232#ifdef DEBUG
616a94ba 233 if (_res.options & RES_DEBUG) {
616a94ba 234 printf("old answer:\n");
616a94ba
RC
235 p_query(answer);
236 }
0b8992ca 237#endif DEBUG
87b12937 238 goto wait;
616a94ba
RC
239 }
240 if (!(_res.options & RES_IGNTC) && anhp->tc) {
241 /*
242 * get rest of answer
243 */
ae94a224 244#ifdef DEBUG
616a94ba
RC
245 if (_res.options & RES_DEBUG)
246 printf("truncated answer\n");
0b8992ca 247#endif DEBUG
616a94ba
RC
248 (void) close(s);
249 s = -1;
09af7f8a
KD
250 /*
251 * retry decremented on continue
252 * to desired starting value
253 */
254 retry = _res.retry + 1;
616a94ba
RC
255 v_circuit = 1;
256 continue;
257 }
258 }
ae94a224 259#ifdef DEBUG
616a94ba
RC
260 if (_res.options & RES_DEBUG) {
261 printf("got answer:\n");
262 p_query(answer);
263 }
0b8992ca 264#endif DEBUG
a5d4a4c0
JB
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 }
0b8992ca 277 }
616a94ba 278 }
1ea504e8
JB
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;
92a82fe4 288 else
1ea504e8 289 errno = terrno;
616a94ba
RC
290 return (-1);
291}
3126f4c0
JB
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}