118144Sralph /* 221388Sdist * Copyright (c) 1985 Regents of the University of California. 333679Sbostic * All rights reserved. 433679Sbostic * 533679Sbostic * Redistribution and use in source and binary forms are permitted 6*34817Sbostic * provided that the above copyright notice and this paragraph are 7*34817Sbostic * duplicated in all such forms and that any documentation, 8*34817Sbostic * advertising materials, and other materials related to such 9*34817Sbostic * distribution and use acknowledge that the software was developed 10*34817Sbostic * by the University of California, Berkeley. The name of the 11*34817Sbostic * University may not be used to endorse or promote products derived 12*34817Sbostic * from this software without specific prior written permission. 13*34817Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34817Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34817Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1618548Sralph */ 1718548Sralph 1826635Sdonn #if defined(LIBC_SCCS) && !defined(lint) 19*34817Sbostic static char sccsid[] = "@(#)res_send.c 6.21 (Berkeley) 06/27/88"; 2033679Sbostic #endif /* LIBC_SCCS and not lint */ 2121388Sdist 2218548Sralph /* 2318144Sralph * Send query to name server and wait for reply. 2418144Sralph */ 2518144Sralph 2626886Skjd #include <sys/param.h> 2718144Sralph #include <sys/time.h> 2818144Sralph #include <sys/socket.h> 2927664Sbloom #include <sys/uio.h> 3018144Sralph #include <netinet/in.h> 3118144Sralph #include <stdio.h> 3218144Sralph #include <errno.h> 3324083Skjd #include <arpa/nameser.h> 3426896Skjd #include <resolv.h> 3518144Sralph 3618144Sralph extern int errno; 3718144Sralph 3827025Sbloom static int s = -1; /* socket used for communications */ 3931113Skarels static struct sockaddr no_addr; 4030100Skjd 4127025Sbloom 4230100Skjd #ifndef FD_SET 4330100Skjd #define NFDBITS 32 4430100Skjd #define FD_SETSIZE 32 4530100Skjd #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) 4630100Skjd #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) 4730100Skjd #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) 4830100Skjd #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) 4930100Skjd #endif 5030100Skjd 5126322Sbloom #define KEEPOPEN (RES_USEVC|RES_STAYOPEN) 5226322Sbloom 5318531Sralph res_send(buf, buflen, answer, anslen) 5418144Sralph char *buf; 5518144Sralph int buflen; 5618144Sralph char *answer; 5718144Sralph int anslen; 5818144Sralph { 5918144Sralph register int n; 6026322Sbloom int retry, v_circuit, resplen, ns; 6132603Skarels int gotsomewhere = 0, connected = 0; 6218144Sralph u_short id, len; 6318144Sralph char *cp; 6427796Skjd fd_set dsmask; 6518144Sralph struct timeval timeout; 6618144Sralph HEADER *hp = (HEADER *) buf; 6718144Sralph HEADER *anhp = (HEADER *) answer; 6827664Sbloom struct iovec iov[2]; 6929434Sbloom int terrno = ETIMEDOUT; 7032603Skarels char junk[512]; 7118144Sralph 7224734Sbloom #ifdef DEBUG 7318144Sralph if (_res.options & RES_DEBUG) { 7418531Sralph printf("res_send()\n"); 7518144Sralph p_query(buf); 7618144Sralph } 7725243Skjd #endif DEBUG 7818531Sralph if (!(_res.options & RES_INIT)) 7924734Sbloom if (res_init() == -1) { 8024734Sbloom return(-1); 8124734Sbloom } 8218144Sralph v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; 8318144Sralph id = hp->id; 8418144Sralph /* 8518144Sralph * Send request, RETRY times, or until successful 8618144Sralph */ 8727040Skjd for (retry = _res.retry; retry > 0; retry--) { 8825243Skjd for (ns = 0; ns < _res.nscount; ns++) { 8925243Skjd #ifdef DEBUG 9025243Skjd if (_res.options & RES_DEBUG) 9125243Skjd printf("Querying server (# %d) address = %s\n", ns+1, 9232603Skarels inet_ntoa(_res.nsaddr_list[ns].sin_addr)); 9325243Skjd #endif DEBUG 9418144Sralph if (v_circuit) { 9532603Skarels int truncated = 0; 9632603Skarels 9718144Sralph /* 9818144Sralph * Use virtual circuit. 9918144Sralph */ 10026322Sbloom if (s < 0) { 10118144Sralph s = socket(AF_INET, SOCK_STREAM, 0); 10226483Skarels if (s < 0) { 10329434Sbloom terrno = errno; 10426483Skarels #ifdef DEBUG 10526483Skarels if (_res.options & RES_DEBUG) 10627031Skjd perror("socket failed"); 10726483Skarels #endif DEBUG 10826483Skarels continue; 10926483Skarels } 11027031Skjd if (connect(s, &(_res.nsaddr_list[ns]), 11126322Sbloom sizeof(struct sockaddr)) < 0) { 11229434Sbloom terrno = errno; 11324734Sbloom #ifdef DEBUG 11426322Sbloom if (_res.options & RES_DEBUG) 11527031Skjd perror("connect failed"); 11625243Skjd #endif DEBUG 11726322Sbloom (void) close(s); 11826322Sbloom s = -1; 11926322Sbloom continue; 12026322Sbloom } 12118144Sralph } 12218144Sralph /* 12318144Sralph * Send length & message 12418144Sralph */ 12527025Sbloom len = htons((u_short)buflen); 12627664Sbloom iov[0].iov_base = (caddr_t)&len; 12727664Sbloom iov[0].iov_len = sizeof(len); 12827664Sbloom iov[1].iov_base = buf; 12927664Sbloom iov[1].iov_len = buflen; 13027664Sbloom if (writev(s, iov, 2) != sizeof(len) + buflen) { 13129434Sbloom terrno = errno; 13224734Sbloom #ifdef DEBUG 13318144Sralph if (_res.options & RES_DEBUG) 13427031Skjd perror("write failed"); 13525243Skjd #endif DEBUG 13618144Sralph (void) close(s); 13718144Sralph s = -1; 13818144Sralph continue; 13918144Sralph } 14018144Sralph /* 14118144Sralph * Receive length & response 14218144Sralph */ 14318144Sralph cp = answer; 14418144Sralph len = sizeof(short); 14527664Sbloom while (len != 0 && 14627031Skjd (n = read(s, (char *)cp, (int)len)) > 0) { 14718144Sralph cp += n; 14818144Sralph len -= n; 14918144Sralph } 15018144Sralph if (n <= 0) { 15129434Sbloom terrno = errno; 15224734Sbloom #ifdef DEBUG 15318144Sralph if (_res.options & RES_DEBUG) 15427031Skjd perror("read failed"); 15525243Skjd #endif DEBUG 15618144Sralph (void) close(s); 15718144Sralph s = -1; 15818144Sralph continue; 15918144Sralph } 16018144Sralph cp = answer; 16132603Skarels if ((resplen = ntohs(*(u_short *)cp)) > anslen) { 16232603Skarels #ifdef DEBUG 16332603Skarels if (_res.options & RES_DEBUG) 16432603Skarels fprintf(stderr, "response truncated\n"); 16532603Skarels #endif DEBUG 16632603Skarels len = anslen; 16732603Skarels truncated = 1; 16832603Skarels } else 16932603Skarels len = resplen; 17027664Sbloom while (len != 0 && 17127031Skjd (n = read(s, (char *)cp, (int)len)) > 0) { 17218144Sralph cp += n; 17318144Sralph len -= n; 17418144Sralph } 17518144Sralph if (n <= 0) { 17629434Sbloom terrno = errno; 17724734Sbloom #ifdef DEBUG 17818144Sralph if (_res.options & RES_DEBUG) 17927031Skjd perror("read failed"); 18025243Skjd #endif DEBUG 18118144Sralph (void) close(s); 18218144Sralph s = -1; 18318144Sralph continue; 18418144Sralph } 18532603Skarels if (truncated) { 18632603Skarels /* 18732603Skarels * Flush rest of answer 18832603Skarels * so connection stays in synch. 18932603Skarels */ 19032603Skarels anhp->tc = 1; 19132603Skarels len = resplen - anslen; 19232603Skarels while (len != 0) { 19332603Skarels n = (len > sizeof(junk) ? 19432603Skarels sizeof(junk) : len); 19532603Skarels if ((n = read(s, junk, n)) > 0) 19632603Skarels len -= n; 19732603Skarels else 19832603Skarels break; 19932603Skarels } 20032603Skarels } 20118144Sralph } else { 20218144Sralph /* 20318144Sralph * Use datagrams. 20418144Sralph */ 20518144Sralph if (s < 0) 20618144Sralph s = socket(AF_INET, SOCK_DGRAM, 0); 20726483Skarels #if BSD >= 43 20831113Skarels if (_res.nscount == 1 || retry == _res.retry) { 20930394Skjd /* 21031113Skarels * Don't use connect if we might 21131113Skarels * still receive a response 21231113Skarels * from another server. 21330394Skjd */ 21432603Skarels if (connected == 0) { 21532603Skarels if (connect(s, &_res.nsaddr_list[ns], 21632603Skarels sizeof(struct sockaddr)) < 0) { 21724734Sbloom #ifdef DEBUG 21832603Skarels if (_res.options & RES_DEBUG) 21932603Skarels perror("connect"); 22032603Skarels #endif DEBUG 22132603Skarels continue; 22232603Skarels } 22332603Skarels connected = 1; 22432603Skarels } 22532603Skarels if (send(s, buf, buflen, 0) != buflen) { 22632603Skarels #ifdef DEBUG 22730394Skjd if (_res.options & RES_DEBUG) 22832603Skarels perror("send"); 22925243Skjd #endif DEBUG 23030394Skjd continue; 23130394Skjd } 23234341Skarels } else { 23334341Skarels /* 23434341Skarels * Disconnect if we want to listen 23534341Skarels * for responses from more than one server. 23634341Skarels */ 23734341Skarels if (connected) { 23834341Skarels (void) connect(s, &no_addr, 23934341Skarels sizeof(no_addr)); 24034341Skarels connected = 0; 24134341Skarels } 24230394Skjd #endif BSD 24334341Skarels if (sendto(s, buf, buflen, 0, 24434341Skarels &_res.nsaddr_list[ns], 24534341Skarels sizeof(struct sockaddr)) != buflen) { 24626483Skarels #ifdef DEBUG 24734341Skarels if (_res.options & RES_DEBUG) 24834341Skarels perror("sendto"); 24926483Skarels #endif DEBUG 25034341Skarels continue; 25134341Skarels } 25234341Skarels #if BSD >= 43 25326483Skarels } 25434341Skarels #endif 25530394Skjd 25618144Sralph /* 25727031Skjd * Wait for reply 25818144Sralph */ 25927031Skjd timeout.tv_sec = (_res.retrans << (_res.retry - retry)) 26027031Skjd / _res.nscount; 26127031Skjd if (timeout.tv_sec <= 0) 26227031Skjd timeout.tv_sec = 1; 26318144Sralph timeout.tv_usec = 0; 26426323Skarels wait: 26527796Skjd FD_ZERO(&dsmask); 26627796Skjd FD_SET(s, &dsmask); 26727664Sbloom n = select(s+1, &dsmask, (fd_set *)NULL, 26827664Sbloom (fd_set *)NULL, &timeout); 26918144Sralph if (n < 0) { 27024734Sbloom #ifdef DEBUG 27118144Sralph if (_res.options & RES_DEBUG) 27227031Skjd perror("select"); 27325243Skjd #endif DEBUG 27418144Sralph continue; 27518144Sralph } 27618144Sralph if (n == 0) { 27718144Sralph /* 27818144Sralph * timeout 27918144Sralph */ 28024734Sbloom #ifdef DEBUG 28118144Sralph if (_res.options & RES_DEBUG) 28218144Sralph printf("timeout\n"); 28325243Skjd #endif DEBUG 28426483Skarels gotsomewhere = 1; 28518144Sralph continue; 28618144Sralph } 28725332Skjd if ((resplen = recv(s, answer, anslen, 0)) <= 0) { 28824734Sbloom #ifdef DEBUG 28918144Sralph if (_res.options & RES_DEBUG) 29027031Skjd perror("recvfrom"); 29125243Skjd #endif DEBUG 29218144Sralph continue; 29318144Sralph } 29426483Skarels gotsomewhere = 1; 29518144Sralph if (id != anhp->id) { 29618144Sralph /* 29718144Sralph * response from old query, ignore it 29818144Sralph */ 29924734Sbloom #ifdef DEBUG 30018144Sralph if (_res.options & RES_DEBUG) { 30118144Sralph printf("old answer:\n"); 30218144Sralph p_query(answer); 30318144Sralph } 30425243Skjd #endif DEBUG 30526323Skarels goto wait; 30618144Sralph } 30718144Sralph if (!(_res.options & RES_IGNTC) && anhp->tc) { 30818144Sralph /* 30918144Sralph * get rest of answer 31018144Sralph */ 31124734Sbloom #ifdef DEBUG 31218144Sralph if (_res.options & RES_DEBUG) 31318144Sralph printf("truncated answer\n"); 31425243Skjd #endif DEBUG 31518144Sralph (void) close(s); 31618144Sralph s = -1; 31727040Skjd /* 31827040Skjd * retry decremented on continue 31927040Skjd * to desired starting value 32027040Skjd */ 32127040Skjd retry = _res.retry + 1; 32218144Sralph v_circuit = 1; 32318144Sralph continue; 32418144Sralph } 32518144Sralph } 32624734Sbloom #ifdef DEBUG 32718144Sralph if (_res.options & RES_DEBUG) { 32818144Sralph printf("got answer:\n"); 32918144Sralph p_query(answer); 33018144Sralph } 33125243Skjd #endif DEBUG 33226322Sbloom /* 33326322Sbloom * We are going to assume that the first server is preferred 33426322Sbloom * over the rest (i.e. it is on the local machine) and only 33526322Sbloom * keep that one open. 33626322Sbloom */ 33734341Skarels if ((_res.options & KEEPOPEN) == 0 || ns != 0) { 33826322Sbloom (void) close(s); 33926322Sbloom s = -1; 34026322Sbloom } 34134341Skarels return (resplen); 34225243Skjd } 34318144Sralph } 34429434Sbloom if (s >= 0) { 34529434Sbloom (void) close(s); 34629434Sbloom s = -1; 34729434Sbloom } 34829434Sbloom if (v_circuit == 0) 34929434Sbloom if (gotsomewhere == 0) 35029434Sbloom errno = ECONNREFUSED; 35129434Sbloom else 35229434Sbloom errno = ETIMEDOUT; 35326483Skarels else 35429434Sbloom errno = terrno; 35518144Sralph return (-1); 35618144Sralph } 35727025Sbloom 35827025Sbloom /* 35927025Sbloom * This routine is for closing the socket if a virtual circuit is used and 36027025Sbloom * the program wants to close it. This provides support for endhostent() 36127025Sbloom * which expects to close the socket. 36227025Sbloom * 36327025Sbloom * This routine is not expected to be user visible. 36427025Sbloom */ 36527025Sbloom _res_close() 36627025Sbloom { 36727025Sbloom if (s != -1) { 36827025Sbloom (void) close(s); 36927025Sbloom s = -1; 37027025Sbloom } 37127025Sbloom } 372