xref: /csrg-svn/lib/libc/net/res_send.c (revision 34341)
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
633679Sbostic  * provided that this notice is preserved and that due credit is given
733679Sbostic  * to the University of California at Berkeley. The name of the University
833679Sbostic  * may not be used to endorse or promote products derived from this
933679Sbostic  * software without specific prior written permission. This software
1033679Sbostic  * is provided ``as is'' without express or implied warranty.
1118548Sralph  */
1218548Sralph 
1326635Sdonn #if defined(LIBC_SCCS) && !defined(lint)
14*34341Skarels static char sccsid[] = "@(#)res_send.c	6.20 (Berkeley) 05/19/88";
1533679Sbostic #endif /* LIBC_SCCS and not lint */
1621388Sdist 
1718548Sralph /*
1818144Sralph  * Send query to name server and wait for reply.
1918144Sralph  */
2018144Sralph 
2126886Skjd #include <sys/param.h>
2218144Sralph #include <sys/time.h>
2318144Sralph #include <sys/socket.h>
2427664Sbloom #include <sys/uio.h>
2518144Sralph #include <netinet/in.h>
2618144Sralph #include <stdio.h>
2718144Sralph #include <errno.h>
2824083Skjd #include <arpa/nameser.h>
2926896Skjd #include <resolv.h>
3018144Sralph 
3118144Sralph extern int errno;
3218144Sralph 
3327025Sbloom static int s = -1;	/* socket used for communications */
3431113Skarels static struct sockaddr no_addr;
3530100Skjd 
3627025Sbloom 
3730100Skjd #ifndef FD_SET
3830100Skjd #define	NFDBITS		32
3930100Skjd #define	FD_SETSIZE	32
4030100Skjd #define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
4130100Skjd #define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
4230100Skjd #define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
4330100Skjd #define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
4430100Skjd #endif
4530100Skjd 
4626322Sbloom #define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
4726322Sbloom 
4818531Sralph res_send(buf, buflen, answer, anslen)
4918144Sralph 	char *buf;
5018144Sralph 	int buflen;
5118144Sralph 	char *answer;
5218144Sralph 	int anslen;
5318144Sralph {
5418144Sralph 	register int n;
5526322Sbloom 	int retry, v_circuit, resplen, ns;
5632603Skarels 	int gotsomewhere = 0, connected = 0;
5718144Sralph 	u_short id, len;
5818144Sralph 	char *cp;
5927796Skjd 	fd_set dsmask;
6018144Sralph 	struct timeval timeout;
6118144Sralph 	HEADER *hp = (HEADER *) buf;
6218144Sralph 	HEADER *anhp = (HEADER *) answer;
6327664Sbloom 	struct iovec iov[2];
6429434Sbloom 	int terrno = ETIMEDOUT;
6532603Skarels 	char junk[512];
6618144Sralph 
6724734Sbloom #ifdef DEBUG
6818144Sralph 	if (_res.options & RES_DEBUG) {
6918531Sralph 		printf("res_send()\n");
7018144Sralph 		p_query(buf);
7118144Sralph 	}
7225243Skjd #endif DEBUG
7318531Sralph 	if (!(_res.options & RES_INIT))
7424734Sbloom 		if (res_init() == -1) {
7524734Sbloom 			return(-1);
7624734Sbloom 		}
7718144Sralph 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
7818144Sralph 	id = hp->id;
7918144Sralph 	/*
8018144Sralph 	 * Send request, RETRY times, or until successful
8118144Sralph 	 */
8227040Skjd 	for (retry = _res.retry; retry > 0; retry--) {
8325243Skjd 	   for (ns = 0; ns < _res.nscount; ns++) {
8425243Skjd #ifdef DEBUG
8525243Skjd 		if (_res.options & RES_DEBUG)
8625243Skjd 			printf("Querying server (# %d) address = %s\n", ns+1,
8732603Skarels 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr));
8825243Skjd #endif DEBUG
8918144Sralph 		if (v_circuit) {
9032603Skarels 			int truncated = 0;
9132603Skarels 
9218144Sralph 			/*
9318144Sralph 			 * Use virtual circuit.
9418144Sralph 			 */
9526322Sbloom 			if (s < 0) {
9618144Sralph 				s = socket(AF_INET, SOCK_STREAM, 0);
9726483Skarels 				if (s < 0) {
9829434Sbloom 					terrno = errno;
9926483Skarels #ifdef DEBUG
10026483Skarels 					if (_res.options & RES_DEBUG)
10127031Skjd 					    perror("socket failed");
10226483Skarels #endif DEBUG
10326483Skarels 					continue;
10426483Skarels 				}
10527031Skjd 				if (connect(s, &(_res.nsaddr_list[ns]),
10626322Sbloom 				   sizeof(struct sockaddr)) < 0) {
10729434Sbloom 					terrno = errno;
10824734Sbloom #ifdef DEBUG
10926322Sbloom 					if (_res.options & RES_DEBUG)
11027031Skjd 					    perror("connect failed");
11125243Skjd #endif DEBUG
11226322Sbloom 					(void) close(s);
11326322Sbloom 					s = -1;
11426322Sbloom 					continue;
11526322Sbloom 				}
11618144Sralph 			}
11718144Sralph 			/*
11818144Sralph 			 * Send length & message
11918144Sralph 			 */
12027025Sbloom 			len = htons((u_short)buflen);
12127664Sbloom 			iov[0].iov_base = (caddr_t)&len;
12227664Sbloom 			iov[0].iov_len = sizeof(len);
12327664Sbloom 			iov[1].iov_base = buf;
12427664Sbloom 			iov[1].iov_len = buflen;
12527664Sbloom 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
12629434Sbloom 				terrno = errno;
12724734Sbloom #ifdef DEBUG
12818144Sralph 				if (_res.options & RES_DEBUG)
12927031Skjd 					perror("write failed");
13025243Skjd #endif DEBUG
13118144Sralph 				(void) close(s);
13218144Sralph 				s = -1;
13318144Sralph 				continue;
13418144Sralph 			}
13518144Sralph 			/*
13618144Sralph 			 * Receive length & response
13718144Sralph 			 */
13818144Sralph 			cp = answer;
13918144Sralph 			len = sizeof(short);
14027664Sbloom 			while (len != 0 &&
14127031Skjd 			    (n = read(s, (char *)cp, (int)len)) > 0) {
14218144Sralph 				cp += n;
14318144Sralph 				len -= n;
14418144Sralph 			}
14518144Sralph 			if (n <= 0) {
14629434Sbloom 				terrno = errno;
14724734Sbloom #ifdef DEBUG
14818144Sralph 				if (_res.options & RES_DEBUG)
14927031Skjd 					perror("read failed");
15025243Skjd #endif DEBUG
15118144Sralph 				(void) close(s);
15218144Sralph 				s = -1;
15318144Sralph 				continue;
15418144Sralph 			}
15518144Sralph 			cp = answer;
15632603Skarels 			if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
15732603Skarels #ifdef DEBUG
15832603Skarels 				if (_res.options & RES_DEBUG)
15932603Skarels 					fprintf(stderr, "response truncated\n");
16032603Skarels #endif DEBUG
16132603Skarels 				len = anslen;
16232603Skarels 				truncated = 1;
16332603Skarels 			} else
16432603Skarels 				len = resplen;
16527664Sbloom 			while (len != 0 &&
16627031Skjd 			   (n = read(s, (char *)cp, (int)len)) > 0) {
16718144Sralph 				cp += n;
16818144Sralph 				len -= n;
16918144Sralph 			}
17018144Sralph 			if (n <= 0) {
17129434Sbloom 				terrno = errno;
17224734Sbloom #ifdef DEBUG
17318144Sralph 				if (_res.options & RES_DEBUG)
17427031Skjd 					perror("read failed");
17525243Skjd #endif DEBUG
17618144Sralph 				(void) close(s);
17718144Sralph 				s = -1;
17818144Sralph 				continue;
17918144Sralph 			}
18032603Skarels 			if (truncated) {
18132603Skarels 				/*
18232603Skarels 				 * Flush rest of answer
18332603Skarels 				 * so connection stays in synch.
18432603Skarels 				 */
18532603Skarels 				anhp->tc = 1;
18632603Skarels 				len = resplen - anslen;
18732603Skarels 				while (len != 0) {
18832603Skarels 					n = (len > sizeof(junk) ?
18932603Skarels 					    sizeof(junk) : len);
19032603Skarels 					if ((n = read(s, junk, n)) > 0)
19132603Skarels 						len -= n;
19232603Skarels 					else
19332603Skarels 						break;
19432603Skarels 				}
19532603Skarels 			}
19618144Sralph 		} else {
19718144Sralph 			/*
19818144Sralph 			 * Use datagrams.
19918144Sralph 			 */
20018144Sralph 			if (s < 0)
20118144Sralph 				s = socket(AF_INET, SOCK_DGRAM, 0);
20226483Skarels #if	BSD >= 43
20331113Skarels 			if (_res.nscount == 1 || retry == _res.retry) {
20430394Skjd 				/*
20531113Skarels 				 * Don't use connect if we might
20631113Skarels 				 * still receive a response
20731113Skarels 				 * from another server.
20830394Skjd 				 */
20932603Skarels 				if (connected == 0) {
21032603Skarels 					if (connect(s, &_res.nsaddr_list[ns],
21132603Skarels 					    sizeof(struct sockaddr)) < 0) {
21224734Sbloom #ifdef DEBUG
21332603Skarels 						if (_res.options & RES_DEBUG)
21432603Skarels 							perror("connect");
21532603Skarels #endif DEBUG
21632603Skarels 						continue;
21732603Skarels 					}
21832603Skarels 					connected = 1;
21932603Skarels 				}
22032603Skarels 				if (send(s, buf, buflen, 0) != buflen) {
22132603Skarels #ifdef DEBUG
22230394Skjd 					if (_res.options & RES_DEBUG)
22332603Skarels 						perror("send");
22425243Skjd #endif DEBUG
22530394Skjd 					continue;
22630394Skjd 				}
227*34341Skarels 			} else {
228*34341Skarels 				/*
229*34341Skarels 				 * Disconnect if we want to listen
230*34341Skarels 				 * for responses from more than one server.
231*34341Skarels 				 */
232*34341Skarels 				if (connected) {
233*34341Skarels 					(void) connect(s, &no_addr,
234*34341Skarels 					    sizeof(no_addr));
235*34341Skarels 					connected = 0;
236*34341Skarels 				}
23730394Skjd #endif BSD
238*34341Skarels 				if (sendto(s, buf, buflen, 0,
239*34341Skarels 				    &_res.nsaddr_list[ns],
240*34341Skarels 				    sizeof(struct sockaddr)) != buflen) {
24126483Skarels #ifdef DEBUG
242*34341Skarels 					if (_res.options & RES_DEBUG)
243*34341Skarels 						perror("sendto");
24426483Skarels #endif DEBUG
245*34341Skarels 					continue;
246*34341Skarels 				}
247*34341Skarels #if	BSD >= 43
24826483Skarels 			}
249*34341Skarels #endif
25030394Skjd 
25118144Sralph 			/*
25227031Skjd 			 * Wait for reply
25318144Sralph 			 */
25427031Skjd 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
25527031Skjd 				/ _res.nscount;
25627031Skjd 			if (timeout.tv_sec <= 0)
25727031Skjd 				timeout.tv_sec = 1;
25818144Sralph 			timeout.tv_usec = 0;
25926323Skarels wait:
26027796Skjd 			FD_ZERO(&dsmask);
26127796Skjd 			FD_SET(s, &dsmask);
26227664Sbloom 			n = select(s+1, &dsmask, (fd_set *)NULL,
26327664Sbloom 				(fd_set *)NULL, &timeout);
26418144Sralph 			if (n < 0) {
26524734Sbloom #ifdef DEBUG
26618144Sralph 				if (_res.options & RES_DEBUG)
26727031Skjd 					perror("select");
26825243Skjd #endif DEBUG
26918144Sralph 				continue;
27018144Sralph 			}
27118144Sralph 			if (n == 0) {
27218144Sralph 				/*
27318144Sralph 				 * timeout
27418144Sralph 				 */
27524734Sbloom #ifdef DEBUG
27618144Sralph 				if (_res.options & RES_DEBUG)
27718144Sralph 					printf("timeout\n");
27825243Skjd #endif DEBUG
27926483Skarels 				gotsomewhere = 1;
28018144Sralph 				continue;
28118144Sralph 			}
28225332Skjd 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
28324734Sbloom #ifdef DEBUG
28418144Sralph 				if (_res.options & RES_DEBUG)
28527031Skjd 					perror("recvfrom");
28625243Skjd #endif DEBUG
28718144Sralph 				continue;
28818144Sralph 			}
28926483Skarels 			gotsomewhere = 1;
29018144Sralph 			if (id != anhp->id) {
29118144Sralph 				/*
29218144Sralph 				 * response from old query, ignore it
29318144Sralph 				 */
29424734Sbloom #ifdef DEBUG
29518144Sralph 				if (_res.options & RES_DEBUG) {
29618144Sralph 					printf("old answer:\n");
29718144Sralph 					p_query(answer);
29818144Sralph 				}
29925243Skjd #endif DEBUG
30026323Skarels 				goto wait;
30118144Sralph 			}
30218144Sralph 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
30318144Sralph 				/*
30418144Sralph 				 * get rest of answer
30518144Sralph 				 */
30624734Sbloom #ifdef DEBUG
30718144Sralph 				if (_res.options & RES_DEBUG)
30818144Sralph 					printf("truncated answer\n");
30925243Skjd #endif DEBUG
31018144Sralph 				(void) close(s);
31118144Sralph 				s = -1;
31227040Skjd 				/*
31327040Skjd 				 * retry decremented on continue
31427040Skjd 				 * to desired starting value
31527040Skjd 				 */
31627040Skjd 				retry = _res.retry + 1;
31718144Sralph 				v_circuit = 1;
31818144Sralph 				continue;
31918144Sralph 			}
32018144Sralph 		}
32124734Sbloom #ifdef DEBUG
32218144Sralph 		if (_res.options & RES_DEBUG) {
32318144Sralph 			printf("got answer:\n");
32418144Sralph 			p_query(answer);
32518144Sralph 		}
32625243Skjd #endif DEBUG
32726322Sbloom 		/*
32826322Sbloom 		 * We are going to assume that the first server is preferred
32926322Sbloom 		 * over the rest (i.e. it is on the local machine) and only
33026322Sbloom 		 * keep that one open.
33126322Sbloom 		 */
332*34341Skarels 		if ((_res.options & KEEPOPEN) == 0 || ns != 0) {
33326322Sbloom 			(void) close(s);
33426322Sbloom 			s = -1;
33526322Sbloom 		}
336*34341Skarels 		return (resplen);
33725243Skjd 	   }
33818144Sralph 	}
33929434Sbloom 	if (s >= 0) {
34029434Sbloom 		(void) close(s);
34129434Sbloom 		s = -1;
34229434Sbloom 	}
34329434Sbloom 	if (v_circuit == 0)
34429434Sbloom 		if (gotsomewhere == 0)
34529434Sbloom 			errno = ECONNREFUSED;
34629434Sbloom 		else
34729434Sbloom 			errno = ETIMEDOUT;
34826483Skarels 	else
34929434Sbloom 		errno = terrno;
35018144Sralph 	return (-1);
35118144Sralph }
35227025Sbloom 
35327025Sbloom /*
35427025Sbloom  * This routine is for closing the socket if a virtual circuit is used and
35527025Sbloom  * the program wants to close it.  This provides support for endhostent()
35627025Sbloom  * which expects to close the socket.
35727025Sbloom  *
35827025Sbloom  * This routine is not expected to be user visible.
35927025Sbloom  */
36027025Sbloom _res_close()
36127025Sbloom {
36227025Sbloom 	if (s != -1) {
36327025Sbloom 		(void) close(s);
36427025Sbloom 		s = -1;
36527025Sbloom 	}
36627025Sbloom }
367