/* * Copyright (c) 1985 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)res_send.c 6.13 (Berkeley) 5/7/86"; #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 */ #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; u_short id, len; char *cp; fd_set dsmask; struct timeval timeout; HEADER *hp = (HEADER *) buf; HEADER *anhp = (HEADER *) answer; struct iovec iov[2]; #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.s_addr)); #endif DEBUG if (v_circuit) { /* * Use virtual circuit. */ if (s < 0) { s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { #ifdef DEBUG if (_res.options & RES_DEBUG) perror("socket failed"); #endif DEBUG continue; } if (connect(s, &(_res.nsaddr_list[ns]), sizeof(struct sockaddr)) < 0) { #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) { #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) { #ifdef DEBUG if (_res.options & RES_DEBUG) perror("read failed"); #endif DEBUG (void) close(s); s = -1; continue; } cp = answer; resplen = len = ntohs(*(u_short *)cp); while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) { cp += n; len -= n; } if (n <= 0) { #ifdef DEBUG if (_res.options & RES_DEBUG) perror("read failed"); #endif DEBUG (void) close(s); s = -1; continue; } } else { /* * Use datagrams. */ if (s < 0) s = socket(AF_INET, SOCK_DGRAM, 0); #if BSD >= 43 if (connect(s, &_res.nsaddr_list[ns], sizeof(struct sockaddr)) < 0 || send(s, buf, buflen, 0) != buflen) { #ifdef DEBUG if (_res.options & RES_DEBUG) perror("connect"); #endif DEBUG continue; } #else 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; } #endif BSD /* * 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 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); } } } (void) close(s); s = -1; if (v_circuit == 0 && gotsomewhere == 0) errno = ECONNREFUSED; else errno = ETIMEDOUT; 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; } }