xref: /csrg-svn/lib/libc/net/res_send.c (revision 34817)
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