X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/e96fe2f73d8746f169976084b3e671c69b363ac6..fad613b52dda8749b9b9e5493557045b54baf496:/usr/src/lib/libc/net/res_send.c diff --git a/usr/src/lib/libc/net/res_send.c b/usr/src/lib/libc/net/res_send.c index 443d733654..36b23fe055 100644 --- a/usr/src/lib/libc/net/res_send.c +++ b/usr/src/lib/libc/net/res_send.c @@ -1,28 +1,50 @@ /* * Copyright (c) 1985 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. + * 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. */ -#ifndef lint -static char sccsid[] = "@(#)res_send.c 5.2 (Berkeley) %G%"; -#endif not lint +#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 +#include #include #include +#include #include #include #include -#include +#include #include 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; @@ -30,48 +52,82 @@ res_send(buf, buflen, answer, anslen) 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; @@ -81,97 +137,228 @@ res_send(buf, buflen, answer, anslen) */ 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); } +#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); - return (resplen); + s = -1; } - (void) close(s); + 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; + } +}