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