BSD 4_3_Net_1 release
[unix-history] / lib / libc / net / res_send.c
CommitLineData
b423e985 1/*
8ea4199d 2 * Copyright (c) 1985 Regents of the University of California.
6b2f9dd0
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
f4f66d2c
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
b423e985
RC
16 */
17
2ce81398 18#if defined(LIBC_SCCS) && !defined(lint)
ca67e7b4 19static char sccsid[] = "@(#)res_send.c 6.21 (Berkeley) 6/27/88";
6b2f9dd0 20#endif /* LIBC_SCCS and not lint */
8ea4199d 21
616a94ba
RC
22/*
23 * Send query to name server and wait for reply.
24 */
25
04a773cb 26#include <sys/param.h>
616a94ba
RC
27#include <sys/time.h>
28#include <sys/socket.h>
679f3274 29#include <sys/uio.h>
616a94ba
RC
30#include <netinet/in.h>
31#include <stdio.h>
32#include <errno.h>
27df2740 33#include <arpa/nameser.h>
f94b2f50 34#include <resolv.h>
616a94ba
RC
35
36extern int errno;
37
3126f4c0 38static int s = -1; /* socket used for communications */
9bb3997d 39static struct sockaddr no_addr;
15537e14
KD
40
41
42#ifndef FD_SET
43#define NFDBITS 32
44#define FD_SETSIZE 32
45#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
46#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
47#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
48#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
49#endif
3126f4c0 50
a5d4a4c0
JB
51#define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
52
31f0ead0 53res_send(buf, buflen, answer, anslen)
616a94ba
RC
54 char *buf;
55 int buflen;
56 char *answer;
57 int anslen;
58{
59 register int n;
a5d4a4c0 60 int retry, v_circuit, resplen, ns;
2344735f 61 int gotsomewhere = 0, connected = 0;
616a94ba
RC
62 u_short id, len;
63 char *cp;
ac3ceb42 64 fd_set dsmask;
616a94ba
RC
65 struct timeval timeout;
66 HEADER *hp = (HEADER *) buf;
67 HEADER *anhp = (HEADER *) answer;
679f3274 68 struct iovec iov[2];
1ea504e8 69 int terrno = ETIMEDOUT;
2344735f 70 char junk[512];
616a94ba 71
ae94a224 72#ifdef DEBUG
616a94ba 73 if (_res.options & RES_DEBUG) {
31f0ead0 74 printf("res_send()\n");
616a94ba
RC
75 p_query(buf);
76 }
0b8992ca 77#endif DEBUG
31f0ead0 78 if (!(_res.options & RES_INIT))
ae94a224
JB
79 if (res_init() == -1) {
80 return(-1);
81 }
616a94ba
RC
82 v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
83 id = hp->id;
84 /*
85 * Send request, RETRY times, or until successful
86 */
09af7f8a 87 for (retry = _res.retry; retry > 0; retry--) {
0b8992ca
KD
88 for (ns = 0; ns < _res.nscount; ns++) {
89#ifdef DEBUG
90 if (_res.options & RES_DEBUG)
91 printf("Querying server (# %d) address = %s\n", ns+1,
2344735f 92 inet_ntoa(_res.nsaddr_list[ns].sin_addr));
0b8992ca 93#endif DEBUG
616a94ba 94 if (v_circuit) {
2344735f
MK
95 int truncated = 0;
96
616a94ba
RC
97 /*
98 * Use virtual circuit.
99 */
a5d4a4c0 100 if (s < 0) {
616a94ba 101 s = socket(AF_INET, SOCK_STREAM, 0);
92a82fe4 102 if (s < 0) {
1ea504e8 103 terrno = errno;
92a82fe4
MK
104#ifdef DEBUG
105 if (_res.options & RES_DEBUG)
747d796e 106 perror("socket failed");
92a82fe4
MK
107#endif DEBUG
108 continue;
109 }
747d796e 110 if (connect(s, &(_res.nsaddr_list[ns]),
a5d4a4c0 111 sizeof(struct sockaddr)) < 0) {
1ea504e8 112 terrno = errno;
ae94a224 113#ifdef DEBUG
a5d4a4c0 114 if (_res.options & RES_DEBUG)
747d796e 115 perror("connect failed");
0b8992ca 116#endif DEBUG
a5d4a4c0
JB
117 (void) close(s);
118 s = -1;
119 continue;
120 }
616a94ba
RC
121 }
122 /*
123 * Send length & message
124 */
3126f4c0 125 len = htons((u_short)buflen);
679f3274
JB
126 iov[0].iov_base = (caddr_t)&len;
127 iov[0].iov_len = sizeof(len);
128 iov[1].iov_base = buf;
129 iov[1].iov_len = buflen;
130 if (writev(s, iov, 2) != sizeof(len) + buflen) {
1ea504e8 131 terrno = errno;
ae94a224 132#ifdef DEBUG
616a94ba 133 if (_res.options & RES_DEBUG)
747d796e 134 perror("write failed");
0b8992ca 135#endif DEBUG
616a94ba
RC
136 (void) close(s);
137 s = -1;
138 continue;
139 }
140 /*
141 * Receive length & response
142 */
143 cp = answer;
144 len = sizeof(short);
679f3274 145 while (len != 0 &&
747d796e 146 (n = read(s, (char *)cp, (int)len)) > 0) {
616a94ba
RC
147 cp += n;
148 len -= n;
149 }
150 if (n <= 0) {
1ea504e8 151 terrno = errno;
ae94a224 152#ifdef DEBUG
616a94ba 153 if (_res.options & RES_DEBUG)
747d796e 154 perror("read failed");
0b8992ca 155#endif DEBUG
616a94ba
RC
156 (void) close(s);
157 s = -1;
158 continue;
159 }
160 cp = answer;
2344735f
MK
161 if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
162#ifdef DEBUG
163 if (_res.options & RES_DEBUG)
164 fprintf(stderr, "response truncated\n");
165#endif DEBUG
166 len = anslen;
167 truncated = 1;
168 } else
169 len = resplen;
679f3274 170 while (len != 0 &&
747d796e 171 (n = read(s, (char *)cp, (int)len)) > 0) {
616a94ba
RC
172 cp += n;
173 len -= n;
174 }
175 if (n <= 0) {
1ea504e8 176 terrno = errno;
ae94a224 177#ifdef DEBUG
616a94ba 178 if (_res.options & RES_DEBUG)
747d796e 179 perror("read failed");
0b8992ca 180#endif DEBUG
616a94ba
RC
181 (void) close(s);
182 s = -1;
183 continue;
184 }
2344735f
MK
185 if (truncated) {
186 /*
187 * Flush rest of answer
188 * so connection stays in synch.
189 */
190 anhp->tc = 1;
191 len = resplen - anslen;
192 while (len != 0) {
193 n = (len > sizeof(junk) ?
194 sizeof(junk) : len);
195 if ((n = read(s, junk, n)) > 0)
196 len -= n;
197 else
198 break;
199 }
200 }
616a94ba
RC
201 } else {
202 /*
203 * Use datagrams.
204 */
205 if (s < 0)
206 s = socket(AF_INET, SOCK_DGRAM, 0);
92a82fe4 207#if BSD >= 43
9bb3997d 208 if (_res.nscount == 1 || retry == _res.retry) {
7a8f7802 209 /*
9bb3997d
MK
210 * Don't use connect if we might
211 * still receive a response
212 * from another server.
7a8f7802 213 */
2344735f
MK
214 if (connected == 0) {
215 if (connect(s, &_res.nsaddr_list[ns],
216 sizeof(struct sockaddr)) < 0) {
217#ifdef DEBUG
218 if (_res.options & RES_DEBUG)
219 perror("connect");
220#endif DEBUG
221 continue;
222 }
223 connected = 1;
224 }
225 if (send(s, buf, buflen, 0) != buflen) {
ae94a224 226#ifdef DEBUG
7a8f7802 227 if (_res.options & RES_DEBUG)
2344735f 228 perror("send");
0b8992ca 229#endif DEBUG
7a8f7802
KD
230 continue;
231 }
fee59cfd
MK
232 } else {
233 /*
234 * Disconnect if we want to listen
235 * for responses from more than one server.
236 */
237 if (connected) {
238 (void) connect(s, &no_addr,
239 sizeof(no_addr));
240 connected = 0;
241 }
7a8f7802 242#endif BSD
fee59cfd
MK
243 if (sendto(s, buf, buflen, 0,
244 &_res.nsaddr_list[ns],
245 sizeof(struct sockaddr)) != buflen) {
92a82fe4 246#ifdef DEBUG
fee59cfd
MK
247 if (_res.options & RES_DEBUG)
248 perror("sendto");
92a82fe4 249#endif DEBUG
fee59cfd
MK
250 continue;
251 }
252#if BSD >= 43
616a94ba 253 }
fee59cfd 254#endif
7a8f7802 255
616a94ba 256 /*
747d796e 257 * Wait for reply
616a94ba 258 */
747d796e
KD
259 timeout.tv_sec = (_res.retrans << (_res.retry - retry))
260 / _res.nscount;
261 if (timeout.tv_sec <= 0)
262 timeout.tv_sec = 1;
616a94ba 263 timeout.tv_usec = 0;
87b12937 264wait:
ac3ceb42
KD
265 FD_ZERO(&dsmask);
266 FD_SET(s, &dsmask);
679f3274
JB
267 n = select(s+1, &dsmask, (fd_set *)NULL,
268 (fd_set *)NULL, &timeout);
616a94ba 269 if (n < 0) {
ae94a224 270#ifdef DEBUG
616a94ba 271 if (_res.options & RES_DEBUG)
747d796e 272 perror("select");
0b8992ca 273#endif DEBUG
616a94ba
RC
274 continue;
275 }
276 if (n == 0) {
277 /*
278 * timeout
279 */
ae94a224 280#ifdef DEBUG
616a94ba
RC
281 if (_res.options & RES_DEBUG)
282 printf("timeout\n");
0b8992ca 283#endif DEBUG
92a82fe4 284 gotsomewhere = 1;
616a94ba
RC
285 continue;
286 }
dd7a6a73 287 if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
ae94a224 288#ifdef DEBUG
616a94ba 289 if (_res.options & RES_DEBUG)
747d796e 290 perror("recvfrom");
0b8992ca 291#endif DEBUG
616a94ba
RC
292 continue;
293 }
92a82fe4 294 gotsomewhere = 1;
616a94ba
RC
295 if (id != anhp->id) {
296 /*
297 * response from old query, ignore it
298 */
ae94a224 299#ifdef DEBUG
616a94ba 300 if (_res.options & RES_DEBUG) {
616a94ba 301 printf("old answer:\n");
616a94ba
RC
302 p_query(answer);
303 }
0b8992ca 304#endif DEBUG
87b12937 305 goto wait;
616a94ba
RC
306 }
307 if (!(_res.options & RES_IGNTC) && anhp->tc) {
308 /*
309 * get rest of answer
310 */
ae94a224 311#ifdef DEBUG
616a94ba
RC
312 if (_res.options & RES_DEBUG)
313 printf("truncated answer\n");
0b8992ca 314#endif DEBUG
616a94ba
RC
315 (void) close(s);
316 s = -1;
09af7f8a
KD
317 /*
318 * retry decremented on continue
319 * to desired starting value
320 */
321 retry = _res.retry + 1;
616a94ba
RC
322 v_circuit = 1;
323 continue;
324 }
325 }
ae94a224 326#ifdef DEBUG
616a94ba
RC
327 if (_res.options & RES_DEBUG) {
328 printf("got answer:\n");
329 p_query(answer);
330 }
0b8992ca 331#endif DEBUG
a5d4a4c0
JB
332 /*
333 * We are going to assume that the first server is preferred
334 * over the rest (i.e. it is on the local machine) and only
335 * keep that one open.
336 */
fee59cfd 337 if ((_res.options & KEEPOPEN) == 0 || ns != 0) {
a5d4a4c0
JB
338 (void) close(s);
339 s = -1;
a5d4a4c0 340 }
fee59cfd 341 return (resplen);
0b8992ca 342 }
616a94ba 343 }
1ea504e8
JB
344 if (s >= 0) {
345 (void) close(s);
346 s = -1;
347 }
348 if (v_circuit == 0)
349 if (gotsomewhere == 0)
350 errno = ECONNREFUSED;
351 else
352 errno = ETIMEDOUT;
92a82fe4 353 else
1ea504e8 354 errno = terrno;
616a94ba
RC
355 return (-1);
356}
3126f4c0
JB
357
358/*
359 * This routine is for closing the socket if a virtual circuit is used and
360 * the program wants to close it. This provides support for endhostent()
361 * which expects to close the socket.
362 *
363 * This routine is not expected to be user visible.
364 */
365_res_close()
366{
367 if (s != -1) {
368 (void) close(s);
369 s = -1;
370 }
371}