summaryrefslogtreecommitdiff
path: root/src/lib/libc/net/res_send.c
diff options
context:
space:
mode:
authorderaadt <>1995-10-18 08:42:23 +0000
committerderaadt <>1995-10-18 08:42:23 +0000
commit0527d29da443886d92e9a418180c5b25a5f8d270 (patch)
tree86b3a64928451a669cefa27900e5884036b4e349 /src/lib/libc/net/res_send.c
downloadopenbsd-0527d29da443886d92e9a418180c5b25a5f8d270.tar.gz
openbsd-0527d29da443886d92e9a418180c5b25a5f8d270.tar.bz2
openbsd-0527d29da443886d92e9a418180c5b25a5f8d270.zip
initial import of NetBSD tree
Diffstat (limited to 'src/lib/libc/net/res_send.c')
-rw-r--r--src/lib/libc/net/res_send.c474
1 files changed, 474 insertions, 0 deletions
diff --git a/src/lib/libc/net/res_send.c b/src/lib/libc/net/res_send.c
new file mode 100644
index 0000000000..e608358180
--- /dev/null
+++ b/src/lib/libc/net/res_send.c
@@ -0,0 +1,474 @@
1/* $NetBSD: res_send.c,v 1.4 1995/02/25 06:21:01 cgd Exp $ */
2
3/*-
4 * Copyright (c) 1985, 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 * -
35 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
36 *
37 * Permission to use, copy, modify, and distribute this software for any
38 * purpose with or without fee is hereby granted, provided that the above
39 * copyright notice and this permission notice appear in all copies, and that
40 * the name of Digital Equipment Corporation not be used in advertising or
41 * publicity pertaining to distribution of the document or software without
42 * specific, written prior permission.
43 *
44 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
45 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
46 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
47 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
48 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
49 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
50 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51 * SOFTWARE.
52 * -
53 * --Copyright--
54 */
55
56#if defined(LIBC_SCCS) && !defined(lint)
57#if 0
58static char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93";
59static char rcsid[] = "$Id: res_send.c,v 4.9.1.1 1993/05/02 22:43:03 vixie Rel ";
60#else
61static char rcsid[] = "$NetBSD: res_send.c,v 1.4 1995/02/25 06:21:01 cgd Exp $";
62#endif
63#endif /* LIBC_SCCS and not lint */
64
65/*
66 * Send query to name server and wait for reply.
67 */
68
69#include <sys/param.h>
70#include <sys/time.h>
71#include <sys/socket.h>
72#include <sys/uio.h>
73#include <netinet/in.h>
74#include <arpa/nameser.h>
75#include <arpa/inet.h>
76#include <stdio.h>
77#include <errno.h>
78#include <resolv.h>
79#include <unistd.h>
80#include <string.h>
81
82static int s = -1; /* socket used for communications */
83static struct sockaddr no_addr;
84
85#ifndef FD_SET
86#define NFDBITS 32
87#define FD_SETSIZE 32
88#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
89#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
90#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
91#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
92#endif
93
94res_send(buf, buflen, answer, anslen)
95 const char *buf;
96 int buflen;
97 char *answer;
98 int anslen;
99{
100 register int n;
101 int try, v_circuit, resplen, ns;
102 int gotsomewhere = 0, connected = 0;
103 int connreset = 0;
104 u_short id, len;
105 char *cp;
106 fd_set dsmask;
107 struct timeval timeout;
108 HEADER *hp = (HEADER *) buf;
109 HEADER *anhp = (HEADER *) answer;
110 u_int badns; /* XXX NSMAX can't exceed #/bits per this */
111 struct iovec iov[2];
112 int terrno = ETIMEDOUT;
113 char junk[512];
114
115#ifdef DEBUG
116 if ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY)) {
117 printf(";; res_send()\n");
118 __p_query(buf);
119 }
120#endif
121 if (!(_res.options & RES_INIT))
122 if (res_init() == -1) {
123 return(-1);
124 }
125 v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
126 id = hp->id;
127 badns = 0;
128 /*
129 * Send request, RETRY times, or until successful
130 */
131 for (try = 0; try < _res.retry; try++) {
132 for (ns = 0; ns < _res.nscount; ns++) {
133 if (badns & (1<<ns))
134 continue;
135#ifdef DEBUG
136 if (_res.options & RES_DEBUG)
137 printf(";; Querying server (# %d) address = %s\n",
138 ns+1,
139 inet_ntoa(_res.nsaddr_list[ns].sin_addr));
140#endif
141 usevc:
142 if (v_circuit) {
143 int truncated = 0;
144
145 /*
146 * Use virtual circuit;
147 * at most one attempt per server.
148 */
149 try = _res.retry;
150 if (s < 0) {
151 s = socket(AF_INET, SOCK_STREAM, 0);
152 if (s < 0) {
153 terrno = errno;
154#ifdef DEBUG
155 if (_res.options & RES_DEBUG)
156 perror("socket (vc) failed");
157#endif
158 continue;
159 }
160 if (connect(s,
161 (struct sockaddr *)&(_res.nsaddr_list[ns]),
162 sizeof(struct sockaddr)) < 0) {
163 terrno = errno;
164#ifdef DEBUG
165 if (_res.options & RES_DEBUG)
166 perror("connect failed");
167#endif
168 (void) close(s);
169 s = -1;
170 continue;
171 }
172 }
173 /*
174 * Send length & message
175 */
176 len = htons((u_short)buflen);
177 iov[0].iov_base = (caddr_t)&len;
178 iov[0].iov_len = sizeof(len);
179 iov[1].iov_base = (char *)buf;
180 iov[1].iov_len = buflen;
181 if (writev(s, iov, 2) != sizeof(len) + buflen) {
182 terrno = errno;
183#ifdef DEBUG
184 if (_res.options & RES_DEBUG)
185 perror("write failed");
186#endif
187 (void) close(s);
188 s = -1;
189 continue;
190 }
191 /*
192 * Receive length & response
193 */
194 cp = answer;
195 len = sizeof(short);
196 while (len != 0 &&
197 (n = read(s, (char *)cp, (int)len)) > 0) {
198 cp += n;
199 len -= n;
200 }
201 if (n <= 0) {
202 terrno = errno;
203#ifdef DEBUG
204 if (_res.options & RES_DEBUG)
205 perror("read failed");
206#endif
207 (void) close(s);
208 s = -1;
209 /*
210 * A long running process might get its TCP
211 * connection reset if the remote server was
212 * restarted. Requery the server instead of
213 * trying a new one. When there is only one
214 * server, this means that a query might work
215 * instead of failing. We only allow one reset
216 * per query to prevent looping.
217 */
218 if (terrno == ECONNRESET && !connreset) {
219 connreset = 1;
220 ns--;
221 }
222 continue;
223 }
224 cp = answer;
225 if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
226#ifdef DEBUG
227 if (_res.options & RES_DEBUG)
228 fprintf(stderr,
229 ";; response truncated\n");
230#endif
231 len = anslen;
232 truncated = 1;
233 } else
234 len = resplen;
235 while (len != 0 &&
236 (n = read(s, (char *)cp, (int)len)) > 0) {
237 cp += n;
238 len -= n;
239 }
240 if (n <= 0) {
241 terrno = errno;
242#ifdef DEBUG
243 if (_res.options & RES_DEBUG)
244 perror("read failed");
245#endif
246 (void) close(s);
247 s = -1;
248 continue;
249 }
250 if (truncated) {
251 /*
252 * Flush rest of answer
253 * so connection stays in synch.
254 */
255 anhp->tc = 1;
256 len = resplen - anslen;
257 while (len != 0) {
258 n = (len > sizeof(junk) ?
259 sizeof(junk) : len);
260 if ((n = read(s, junk, n)) > 0)
261 len -= n;
262 else
263 break;
264 }
265 }
266 } else {
267 /*
268 * Use datagrams.
269 */
270 if (s < 0) {
271 s = socket(AF_INET, SOCK_DGRAM, 0);
272 if (s < 0) {
273 terrno = errno;
274#ifdef DEBUG
275 if (_res.options & RES_DEBUG)
276 perror("socket (dg) failed");
277#endif
278 continue;
279 }
280 }
281 /*
282 * I'm tired of answering this question, so:
283 * On a 4.3BSD+ machine (client and server,
284 * actually), sending to a nameserver datagram
285 * port with no nameserver will cause an
286 * ICMP port unreachable message to be returned.
287 * If our datagram socket is "connected" to the
288 * server, we get an ECONNREFUSED error on the next
289 * socket operation, and select returns if the
290 * error message is received. We can thus detect
291 * the absence of a nameserver without timing out.
292 * If we have sent queries to at least two servers,
293 * however, we don't want to remain connected,
294 * as we wish to receive answers from the first
295 * server to respond.
296 */
297 if (_res.nscount == 1 || (try == 0 && ns == 0)) {
298 /*
299 * Don't use connect if we might
300 * still receive a response
301 * from another server.
302 */
303 if (connected == 0) {
304 if (connect(s,
305 (struct sockaddr *)
306 &_res.nsaddr_list[ns],
307 sizeof(struct sockaddr)) < 0) {
308#ifdef DEBUG
309 if (_res.options & RES_DEBUG)
310 perror("connect");
311#endif
312 continue;
313 }
314 connected = 1;
315 }
316 if (send(s, buf, buflen, 0) != buflen) {
317#ifdef DEBUG
318 if (_res.options & RES_DEBUG)
319 perror("send");
320#endif
321 continue;
322 }
323 } else {
324 /*
325 * Disconnect if we want to listen
326 * for responses from more than one server.
327 */
328 if (connected) {
329 (void) connect(s, &no_addr,
330 sizeof(no_addr));
331 connected = 0;
332 }
333 if (sendto(s, buf, buflen, 0,
334 (struct sockaddr *)&_res.nsaddr_list[ns],
335 sizeof(struct sockaddr)) != buflen) {
336#ifdef DEBUG
337 if (_res.options & RES_DEBUG)
338 perror("sendto");
339#endif
340 continue;
341 }
342 }
343
344 /*
345 * Wait for reply
346 */
347 timeout.tv_sec = (_res.retrans << try);
348 if (try > 0)
349 timeout.tv_sec /= _res.nscount;
350 if ((long) timeout.tv_sec <= 0)
351 timeout.tv_sec = 1;
352 timeout.tv_usec = 0;
353wait:
354 FD_ZERO(&dsmask);
355 FD_SET(s, &dsmask);
356 n = select(s+1, &dsmask, (fd_set *)NULL,
357 (fd_set *)NULL, &timeout);
358 if (n < 0) {
359#ifdef DEBUG
360 if (_res.options & RES_DEBUG)
361 perror("select");
362#endif
363 continue;
364 }
365 if (n == 0) {
366 /*
367 * timeout
368 */
369#ifdef DEBUG
370 if (_res.options & RES_DEBUG)
371 printf(";; timeout\n");
372#endif
373 gotsomewhere = 1;
374 continue;
375 }
376 if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
377#ifdef DEBUG
378 if (_res.options & RES_DEBUG)
379 perror("recvfrom");
380#endif
381 continue;
382 }
383 gotsomewhere = 1;
384 if (id != anhp->id) {
385 /*
386 * response from old query, ignore it
387 */
388#ifdef DEBUG
389 if ((_res.options & RES_DEBUG) ||
390 (_res.pfcode & RES_PRF_REPLY)) {
391 printf(";; old answer:\n");
392 __p_query(answer);
393 }
394#endif
395 goto wait;
396 }
397 if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP ||
398 anhp->rcode == REFUSED) {
399#ifdef DEBUG
400 if (_res.options & RES_DEBUG) {
401 printf("server rejected query:\n");
402 __p_query(answer);
403 }
404#endif
405 badns |= (1<<ns);
406 continue;
407 }
408 if (!(_res.options & RES_IGNTC) && anhp->tc) {
409 /*
410 * get rest of answer;
411 * use TCP with same server.
412 */
413#ifdef DEBUG
414 if (_res.options & RES_DEBUG)
415 printf(";; truncated answer\n");
416#endif
417 (void) close(s);
418 s = -1;
419 v_circuit = 1;
420 goto usevc;
421 }
422 }
423#ifdef DEBUG
424 if (_res.options & RES_DEBUG)
425 printf(";; got answer:\n");
426 if ((_res.options & RES_DEBUG) ||
427 (_res.pfcode & RES_PRF_REPLY))
428 __p_query(answer);
429#endif
430 /*
431 * If using virtual circuits, we assume that the first server
432 * is preferred * over the rest (i.e. it is on the local
433 * machine) and only keep that one open.
434 * If we have temporarily opened a virtual circuit,
435 * or if we haven't been asked to keep a socket open,
436 * close the socket.
437 */
438 if ((v_circuit &&
439 ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
440 (_res.options & RES_STAYOPEN) == 0) {
441 (void) close(s);
442 s = -1;
443 }
444 return (resplen);
445 }
446 }
447 if (s >= 0) {
448 (void) close(s);
449 s = -1;
450 }
451 if (v_circuit == 0)
452 if (gotsomewhere == 0)
453 errno = ECONNREFUSED; /* no nameservers found */
454 else
455 errno = ETIMEDOUT; /* no answer obtained */
456 else
457 errno = terrno;
458 return (-1);
459}
460
461/*
462 * This routine is for closing the socket if a virtual circuit is used and
463 * the program wants to close it. This provides support for endhostent()
464 * which expects to close the socket.
465 *
466 * This routine is not expected to be user visible.
467 */
468_res_close()
469{
470 if (s != -1) {
471 (void) close(s);
472 s = -1;
473 }
474}