/* * 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.1 (Berkeley) 6/27/94"; #endif /* LIBC_SCCS and not lint */ /* * Send query to name server and wait for reply. */ #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; char *answer; int anslen; { register int n; int retry, v_circuit, resplen, ns; int gotsomewhere = 0, connected = 0; u_short id, len; char *cp; fd_set dsmask; struct timeval timeout; HEADER *hp = (HEADER *) buf; HEADER *anhp = (HEADER *) answer; struct iovec iov[2]; int terrno = ETIMEDOUT; char junk[16]; #ifdef DEBUG if (_res.options & RES_DEBUG) { printf("res_send()\n"); p_query(buf); } #endif DEBUG if (!(_res.options & RES_INIT)) 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; 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) { s = socket(AF_INET, SOCK_STREAM, 0); 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((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) perror("write failed"); #endif DEBUG (void) close(s); s = -1; continue; } /* * Receive length & response */ cp = answer; len = sizeof(short); 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) perror("read failed"); #endif DEBUG (void) close(s); s = -1; continue; } cp = answer; 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) 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 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) perror("sendto"); #endif DEBUG continue; } /* * Wait for reply */ timeout.tv_sec = (_res.retrans << (_res.retry - retry)) / _res.nscount; if (timeout.tv_sec <= 0) timeout.tv_sec = 1; timeout.tv_usec = 0; 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) 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 = recv(s, answer, anslen, 0)) <= 0) { #ifdef DEBUG if (_res.options & RES_DEBUG) 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); } #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 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); 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; } }