X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/7a8f7802612b5be9e7caa39b125448f8a349b77b..f7d00d5e72962c65438ba8b6e34237429386a0b9:/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 b45595d912..4d447fed8d 100644 --- a/usr/src/lib/libc/net/res_send.c +++ b/usr/src/lib/libc/net/res_send.c @@ -1,13 +1,23 @@ - /* - * Copyright (c) 1985 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. + * Copyright (c) 1985, 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, 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'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)res_send.c 6.16 (Berkeley) %G%"; -#endif LIBC_SCCS and not lint +static char sccsid[] = "@(#)res_send.c 6.24 (Berkeley) %G%"; +#endif /* LIBC_SCCS and not lint */ /* * Send query to name server and wait for reply. @@ -26,6 +36,7 @@ static char sccsid[] = "@(#)res_send.c 6.16 (Berkeley) %G%"; extern int errno; static int s = -1; /* socket used for communications */ +static struct sockaddr no_addr; #ifndef FD_SET @@ -37,8 +48,6 @@ static int s = -1; /* socket used for communications */ #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; @@ -46,8 +55,9 @@ res_send(buf, buflen, answer, anslen) int anslen; { register int n; - int retry, v_circuit, resplen, ns; - int gotsomewhere = 0; + int try, v_circuit, resplen, ns; + int gotsomewhere = 0, connected = 0; + int connreset = 0; u_short id, len; char *cp; fd_set dsmask; @@ -56,6 +66,7 @@ res_send(buf, buflen, answer, anslen) HEADER *anhp = (HEADER *) answer; struct iovec iov[2]; int terrno = ETIMEDOUT; + char junk[512]; #ifdef DEBUG if (_res.options & RES_DEBUG) { @@ -72,24 +83,29 @@ res_send(buf, buflen, answer, anslen) /* * Send request, RETRY times, or until successful */ - for (retry = _res.retry; retry > 0; retry--) { + for (try = 0; try < _res.retry; try++) { 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.s_addr)); + inet_ntoa(_res.nsaddr_list[ns].sin_addr)); #endif DEBUG + usevc: if (v_circuit) { + int truncated = 0; + /* - * Use virtual circuit. + * Use virtual circuit; + * at most one attempt per server. */ + try = _res.retry; if (s < 0) { s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { terrno = errno; #ifdef DEBUG if (_res.options & RES_DEBUG) - perror("socket failed"); + perror("socket (vc) failed"); #endif DEBUG continue; } @@ -141,10 +157,31 @@ res_send(buf, buflen, answer, anslen) #endif DEBUG (void) close(s); s = -1; + /* + * A long running process might get its TCP + * connection reset if the remote server was + * restarted. Requery the server instead of + * trying a new one. When there is only one + * server, this means that a query might work + * instead of failing. We only allow one reset + * per query to prevent looping. + */ + if (terrno == ECONNRESET && !connreset) { + connreset = 1; + ns--; + } continue; } cp = answer; - resplen = len = ntohs(*(u_short *)cp); + 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; @@ -160,43 +197,108 @@ res_send(buf, buflen, answer, anslen) 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) + if (s < 0) { s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("socket (dg) failed"); +#endif DEBUG + continue; + } + } #if BSD >= 43 - if (_res.nscount == 1) { + /* + * I'm tired of answering this question, so: + * On a 4.3BSD+ machine (client and server, + * actually), sending to a nameserver datagram + * port with no nameserver will cause an + * ICMP port unreachable message to be returned. + * If our datagram socket is "connected" to the + * server, we get an ECONNREFUSED error on the next + * socket operation, and select returns if the + * error message is received. We can thus detect + * the absence of a nameserver without timing out. + * If we have sent queries to at least two servers, + * however, we don't want to remain connected, + * as we wish to receive answers from the first + * server to respond. + */ + if (_res.nscount == 1 || (try == 0 && ns == 0)) { /* - * Connect/Send is detrimental if you - * are going to poll more than one server + * Don't use connect if we might + * still receive a response + * from another server. */ - if (connect(s, &_res.nsaddr_list[ns], - sizeof(struct sockaddr)) < 0 || - send(s, buf, buflen, 0) != buflen) { + 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("connect"); + perror("send"); #endif DEBUG continue; } - } else + } else { + /* + * Disconnect if we want to listen + * for responses from more than one server. + */ + if (connected) { + (void) connect(s, &no_addr, + sizeof(no_addr)); + connected = 0; + } #endif BSD - if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns], - sizeof(struct sockaddr)) != buflen) { + if (sendto(s, buf, buflen, 0, + &_res.nsaddr_list[ns], + sizeof(struct sockaddr)) != buflen) { #ifdef DEBUG - if (_res.options & RES_DEBUG) - perror("sendto"); + if (_res.options & RES_DEBUG) + perror("sendto"); #endif DEBUG - continue; + continue; + } +#if BSD >= 43 } +#endif /* * Wait for reply */ - timeout.tv_sec = (_res.retrans << (_res.retry - retry)) - / _res.nscount; + timeout.tv_sec = (_res.retrans << try); + if (try > 0) + timeout.tv_sec /= _res.nscount; if (timeout.tv_sec <= 0) timeout.tv_sec = 1; timeout.tv_usec = 0; @@ -220,7 +322,9 @@ wait: if (_res.options & RES_DEBUG) printf("timeout\n"); #endif DEBUG +#if BSD >= 43 gotsomewhere = 1; +#endif continue; } if ((resplen = recv(s, answer, anslen, 0)) <= 0) { @@ -245,7 +349,8 @@ wait: } if (!(_res.options & RES_IGNTC) && anhp->tc) { /* - * get rest of answer + * get rest of answer; + * use TCP with same server. */ #ifdef DEBUG if (_res.options & RES_DEBUG) @@ -253,13 +358,8 @@ wait: #endif DEBUG (void) close(s); s = -1; - /* - * retry decremented on continue - * to desired starting value - */ - retry = _res.retry + 1; v_circuit = 1; - continue; + goto usevc; } } #ifdef DEBUG @@ -269,17 +369,20 @@ wait: } #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 using virtual circuits, we 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 we have temporarily opened a virtual circuit, + * or if we haven't been asked to keep a socket open, + * close the socket. */ - if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) { - return (resplen); - } else { + if ((v_circuit && + ((_res.options & RES_USEVC) == 0 || ns != 0)) || + (_res.options & RES_STAYOPEN) == 0) { (void) close(s); s = -1; - return (resplen); } + return (resplen); } } if (s >= 0) { @@ -288,9 +391,9 @@ wait: } if (v_circuit == 0) if (gotsomewhere == 0) - errno = ECONNREFUSED; + errno = ECONNREFUSED; /* no nameservers found */ else - errno = ETIMEDOUT; + errno = ETIMEDOUT; /* no answer obtained */ else errno = terrno; return (-1);