-#ifndef lint
-static char sccsid[] = "@(#)res_send.c 4.3 (Berkeley) %G%";
-#endif
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)res_send.c 6.19 (Berkeley) %G%";
+#endif /* LIBC_SCCS and not lint */
/*
* Send query to name server and wait for reply.
*/
-#include <sys/types.h>
+#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
+#include <sys/uio.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>
-#include <nameser.h>
+#include <arpa/nameser.h>
#include <resolv.h>
extern int errno;
+static int s = -1; /* socket used for communications */
+static struct sockaddr no_addr;
+
+
+#ifndef FD_SET
+#define NFDBITS 32
+#define FD_SETSIZE 32
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+#endif
+
+#define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
+
res_send(buf, buflen, answer, anslen)
char *buf;
int buflen;
int anslen;
{
register int n;
- int s, retry, v_circuit, resplen;
+ int retry, v_circuit, resplen, ns;
+ int gotsomewhere = 0, connected = 0;
u_short id, len;
char *cp;
- int dsmask;
+ fd_set dsmask;
struct timeval timeout;
HEADER *hp = (HEADER *) buf;
HEADER *anhp = (HEADER *) answer;
+ struct iovec iov[2];
+ int terrno = ETIMEDOUT;
+ char junk[512];
+#ifdef DEBUG
if (_res.options & RES_DEBUG) {
printf("res_send()\n");
p_query(buf);
}
+#endif DEBUG
if (!(_res.options & RES_INIT))
- res_init();
- s = -1;
+ if (res_init() == -1) {
+ return(-1);
+ }
v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
id = hp->id;
/*
* Send request, RETRY times, or until successful
*/
- for (retry = _res.retry; --retry >= 0; ) {
+ for (retry = _res.retry; retry > 0; retry--) {
+ for (ns = 0; ns < _res.nscount; ns++) {
+#ifdef DEBUG
+ if (_res.options & RES_DEBUG)
+ printf("Querying server (# %d) address = %s\n", ns+1,
+ inet_ntoa(_res.nsaddr_list[ns].sin_addr));
+#endif DEBUG
if (v_circuit) {
+ int truncated = 0;
+
/*
* Use virtual circuit.
*/
- if (s < 0)
+ if (s < 0) {
s = socket(AF_INET, SOCK_STREAM, 0);
- if (connect(s, &_res.nsaddr, sizeof(_res.nsaddr)) < 0) {
- if (_res.options & RES_DEBUG)
- printf("connect failed %d\n", errno);
- (void) close(s);
- s = -1;
- continue;
+ if (s < 0) {
+ terrno = errno;
+#ifdef DEBUG
+ if (_res.options & RES_DEBUG)
+ perror("socket failed");
+#endif DEBUG
+ continue;
+ }
+ if (connect(s, &(_res.nsaddr_list[ns]),
+ sizeof(struct sockaddr)) < 0) {
+ terrno = errno;
+#ifdef DEBUG
+ if (_res.options & RES_DEBUG)
+ perror("connect failed");
+#endif DEBUG
+ (void) close(s);
+ s = -1;
+ continue;
+ }
}
/*
* Send length & message
*/
- len = htons(buflen);
- if (write(s, &len, sizeof(len)) != sizeof(len) ||
- write(s, buf, buflen) != buflen) {
+ len = htons((u_short)buflen);
+ iov[0].iov_base = (caddr_t)&len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = buflen;
+ if (writev(s, iov, 2) != sizeof(len) + buflen) {
+ terrno = errno;
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
- printf("write failed %d\n", errno);
+ perror("write failed");
+#endif DEBUG
(void) close(s);
s = -1;
continue;
*/
cp = answer;
len = sizeof(short);
- while (len > 0 && (n = read(s, cp, len)) > 0) {
+ while (len != 0 &&
+ (n = read(s, (char *)cp, (int)len)) > 0) {
cp += n;
len -= n;
}
if (n <= 0) {
+ terrno = errno;
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
- printf("read failed %d\n", errno);
+ perror("read failed");
+#endif DEBUG
(void) close(s);
s = -1;
continue;
}
cp = answer;
- resplen = len = ntohs(*(short *)cp);
- while (len > 0 && (n = read(s, cp, len)) > 0) {
+ if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
+#ifdef DEBUG
+ if (_res.options & RES_DEBUG)
+ fprintf(stderr, "response truncated\n");
+#endif DEBUG
+ len = anslen;
+ truncated = 1;
+ } else
+ len = resplen;
+ while (len != 0 &&
+ (n = read(s, (char *)cp, (int)len)) > 0) {
cp += n;
len -= n;
}
if (n <= 0) {
+ terrno = errno;
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
- printf("read failed %d\n", errno);
+ perror("read failed");
+#endif DEBUG
(void) close(s);
s = -1;
continue;
}
+ if (truncated) {
+ /*
+ * Flush rest of answer
+ * so connection stays in synch.
+ */
+ anhp->tc = 1;
+ len = resplen - anslen;
+ while (len != 0) {
+ n = (len > sizeof(junk) ?
+ sizeof(junk) : len);
+ if ((n = read(s, junk, n)) > 0)
+ len -= n;
+ else
+ break;
+ }
+ }
} else {
/*
* Use datagrams.
*/
if (s < 0)
s = socket(AF_INET, SOCK_DGRAM, 0);
- if (sendto(s, buf, buflen, 0, &_res.nsaddr,
- sizeof(_res.nsaddr)) != buflen) {
+#if BSD >= 43
+ if (_res.nscount == 1 || retry == _res.retry) {
+ /*
+ * Don't use connect if we might
+ * still receive a response
+ * from another server.
+ */
+ if (connected == 0) {
+ if (connect(s, &_res.nsaddr_list[ns],
+ sizeof(struct sockaddr)) < 0) {
+#ifdef DEBUG
+ if (_res.options & RES_DEBUG)
+ perror("connect");
+#endif DEBUG
+ continue;
+ }
+ connected = 1;
+ }
+ if (send(s, buf, buflen, 0) != buflen) {
+#ifdef DEBUG
+ if (_res.options & RES_DEBUG)
+ perror("send");
+#endif DEBUG
+ continue;
+ }
+ } else
+#endif BSD
+ if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
+ sizeof(struct sockaddr)) != buflen) {
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
- printf("sendto errno = %d\n", errno);
+ perror("sendto");
+#endif DEBUG
+ continue;
}
+
/*
- * Wait for reply
+ * Wait for reply
*/
- timeout.tv_sec = _res.retrans;
+ timeout.tv_sec = (_res.retrans << (_res.retry - retry))
+ / _res.nscount;
+ if (timeout.tv_sec <= 0)
+ timeout.tv_sec = 1;
timeout.tv_usec = 0;
- dsmask = 1 << s;
- n = select(s+1, &dsmask, 0, 0, &timeout);
+wait:
+ FD_ZERO(&dsmask);
+ FD_SET(s, &dsmask);
+ n = select(s+1, &dsmask, (fd_set *)NULL,
+ (fd_set *)NULL, &timeout);
if (n < 0) {
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
- printf("select errno = %d\n", errno);
+ perror("select");
+#endif DEBUG
continue;
}
if (n == 0) {
/*
* timeout
*/
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
printf("timeout\n");
+#endif DEBUG
+ /*
+ * Disconnect if we want to listen
+ * for responses from more than one server.
+ */
+ if (_res.nscount > 1 && connected) {
+ (void) connect(s, &no_addr,
+ sizeof(no_addr));
+ connected = 0;
+ }
+ gotsomewhere = 1;
continue;
}
- if ((resplen = recvfrom(s, answer, anslen,
- 0, 0, 0)) <= 0) {
+ if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
- printf("recvfrom, errno=%d\n", errno);
+ perror("recvfrom");
+#endif DEBUG
continue;
}
+ gotsomewhere = 1;
if (id != anhp->id) {
/*
* response from old query, ignore it
*/
+#ifdef DEBUG
if (_res.options & RES_DEBUG) {
printf("old answer:\n");
p_query(answer);
}
- continue;
+#endif DEBUG
+ goto wait;
}
if (!(_res.options & RES_IGNTC) && anhp->tc) {
/*
* get rest of answer
*/
+#ifdef DEBUG
if (_res.options & RES_DEBUG)
printf("truncated answer\n");
+#endif DEBUG
(void) close(s);
s = -1;
- retry = _res.retry;
+ /*
+ * retry decremented on continue
+ * to desired starting value
+ */
+ retry = _res.retry + 1;
v_circuit = 1;
continue;
}
}
+#ifdef DEBUG
if (_res.options & RES_DEBUG) {
printf("got answer:\n");
p_query(answer);
}
- return (resplen);
+#endif DEBUG
+ /*
+ * We are going to assume that the first server is preferred
+ * over the rest (i.e. it is on the local machine) and only
+ * keep that one open.
+ */
+ if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
+ return (resplen);
+ } else {
+ (void) close(s);
+ s = -1;
+ return (resplen);
+ }
+ }
}
+ if (s >= 0) {
+ (void) close(s);
+ s = -1;
+ }
+ if (v_circuit == 0)
+ if (gotsomewhere == 0)
+ errno = ECONNREFUSED;
+ else
+ errno = ETIMEDOUT;
+ else
+ errno = terrno;
return (-1);
}
+
+/*
+ * This routine is for closing the socket if a virtual circuit is used and
+ * the program wants to close it. This provides support for endhostent()
+ * which expects to close the socket.
+ *
+ * This routine is not expected to be user visible.
+ */
+_res_close()
+{
+ if (s != -1) {
+ (void) close(s);
+ s = -1;
+ }
+}