xref: /csrg-svn/lib/libc/net/res_send.c (revision 42627)
118144Sralph /*
238214Skarels  * Copyright (c) 1985, 1989 Regents of the University of California.
333679Sbostic  * All rights reserved.
433679Sbostic  *
5*42627Sbostic  * %sccs.include.redist.c%
618548Sralph  */
718548Sralph 
826635Sdonn #if defined(LIBC_SCCS) && !defined(lint)
9*42627Sbostic static char sccsid[] = "@(#)res_send.c	6.25 (Berkeley) 06/01/90";
1033679Sbostic #endif /* LIBC_SCCS and not lint */
1121388Sdist 
1218548Sralph /*
1318144Sralph  * Send query to name server and wait for reply.
1418144Sralph  */
1518144Sralph 
1626886Skjd #include <sys/param.h>
1718144Sralph #include <sys/time.h>
1818144Sralph #include <sys/socket.h>
1927664Sbloom #include <sys/uio.h>
2018144Sralph #include <netinet/in.h>
2118144Sralph #include <stdio.h>
2218144Sralph #include <errno.h>
2324083Skjd #include <arpa/nameser.h>
2426896Skjd #include <resolv.h>
2518144Sralph 
2618144Sralph extern int errno;
2718144Sralph 
2827025Sbloom static int s = -1;	/* socket used for communications */
2931113Skarels static struct sockaddr no_addr;
3030100Skjd 
3127025Sbloom 
3230100Skjd #ifndef FD_SET
3330100Skjd #define	NFDBITS		32
3430100Skjd #define	FD_SETSIZE	32
3530100Skjd #define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
3630100Skjd #define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
3730100Skjd #define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
3830100Skjd #define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
3930100Skjd #endif
4030100Skjd 
4118531Sralph res_send(buf, buflen, answer, anslen)
4218144Sralph 	char *buf;
4318144Sralph 	int buflen;
4418144Sralph 	char *answer;
4518144Sralph 	int anslen;
4618144Sralph {
4718144Sralph 	register int n;
4838214Skarels 	int try, v_circuit, resplen, ns;
4932603Skarels 	int gotsomewhere = 0, connected = 0;
5039703Sbloom 	int connreset = 0;
5118144Sralph 	u_short id, len;
5218144Sralph 	char *cp;
5327796Skjd 	fd_set dsmask;
5418144Sralph 	struct timeval timeout;
5518144Sralph 	HEADER *hp = (HEADER *) buf;
5618144Sralph 	HEADER *anhp = (HEADER *) answer;
5727664Sbloom 	struct iovec iov[2];
5829434Sbloom 	int terrno = ETIMEDOUT;
5932603Skarels 	char junk[512];
6018144Sralph 
6124734Sbloom #ifdef DEBUG
6218144Sralph 	if (_res.options & RES_DEBUG) {
6318531Sralph 		printf("res_send()\n");
6418144Sralph 		p_query(buf);
6518144Sralph 	}
6625243Skjd #endif DEBUG
6718531Sralph 	if (!(_res.options & RES_INIT))
6824734Sbloom 		if (res_init() == -1) {
6924734Sbloom 			return(-1);
7024734Sbloom 		}
7118144Sralph 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
7218144Sralph 	id = hp->id;
7318144Sralph 	/*
7418144Sralph 	 * Send request, RETRY times, or until successful
7518144Sralph 	 */
7638214Skarels 	for (try = 0; try < _res.retry; try++) {
7725243Skjd 	   for (ns = 0; ns < _res.nscount; ns++) {
7825243Skjd #ifdef DEBUG
7925243Skjd 		if (_res.options & RES_DEBUG)
8025243Skjd 			printf("Querying server (# %d) address = %s\n", ns+1,
8132603Skarels 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr));
8225243Skjd #endif DEBUG
8338214Skarels 	usevc:
8418144Sralph 		if (v_circuit) {
8532603Skarels 			int truncated = 0;
8632603Skarels 
8718144Sralph 			/*
8838214Skarels 			 * Use virtual circuit;
8938214Skarels 			 * at most one attempt per server.
9018144Sralph 			 */
9138214Skarels 			try = _res.retry;
9226322Sbloom 			if (s < 0) {
9318144Sralph 				s = socket(AF_INET, SOCK_STREAM, 0);
9426483Skarels 				if (s < 0) {
9529434Sbloom 					terrno = errno;
9626483Skarels #ifdef DEBUG
9726483Skarels 					if (_res.options & RES_DEBUG)
9839790Sbloom 					    perror("socket (vc) failed");
9926483Skarels #endif DEBUG
10026483Skarels 					continue;
10126483Skarels 				}
10227031Skjd 				if (connect(s, &(_res.nsaddr_list[ns]),
10326322Sbloom 				   sizeof(struct sockaddr)) < 0) {
10429434Sbloom 					terrno = errno;
10524734Sbloom #ifdef DEBUG
10626322Sbloom 					if (_res.options & RES_DEBUG)
10727031Skjd 					    perror("connect failed");
10825243Skjd #endif DEBUG
10926322Sbloom 					(void) close(s);
11026322Sbloom 					s = -1;
11126322Sbloom 					continue;
11226322Sbloom 				}
11318144Sralph 			}
11418144Sralph 			/*
11518144Sralph 			 * Send length & message
11618144Sralph 			 */
11727025Sbloom 			len = htons((u_short)buflen);
11827664Sbloom 			iov[0].iov_base = (caddr_t)&len;
11927664Sbloom 			iov[0].iov_len = sizeof(len);
12027664Sbloom 			iov[1].iov_base = buf;
12127664Sbloom 			iov[1].iov_len = buflen;
12227664Sbloom 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
12329434Sbloom 				terrno = errno;
12424734Sbloom #ifdef DEBUG
12518144Sralph 				if (_res.options & RES_DEBUG)
12627031Skjd 					perror("write failed");
12725243Skjd #endif DEBUG
12818144Sralph 				(void) close(s);
12918144Sralph 				s = -1;
13018144Sralph 				continue;
13118144Sralph 			}
13218144Sralph 			/*
13318144Sralph 			 * Receive length & response
13418144Sralph 			 */
13518144Sralph 			cp = answer;
13618144Sralph 			len = sizeof(short);
13727664Sbloom 			while (len != 0 &&
13827031Skjd 			    (n = read(s, (char *)cp, (int)len)) > 0) {
13918144Sralph 				cp += n;
14018144Sralph 				len -= n;
14118144Sralph 			}
14218144Sralph 			if (n <= 0) {
14329434Sbloom 				terrno = errno;
14424734Sbloom #ifdef DEBUG
14518144Sralph 				if (_res.options & RES_DEBUG)
14627031Skjd 					perror("read failed");
14725243Skjd #endif DEBUG
14818144Sralph 				(void) close(s);
14918144Sralph 				s = -1;
15039703Sbloom 				/*
15139703Sbloom 				 * A long running process might get its TCP
15239703Sbloom 				 * connection reset if the remote server was
15339703Sbloom 				 * restarted.  Requery the server instead of
15439703Sbloom 				 * trying a new one.  When there is only one
15539703Sbloom 				 * server, this means that a query might work
15639703Sbloom 				 * instead of failing.  We only allow one reset
15739703Sbloom 				 * per query to prevent looping.
15839703Sbloom 				 */
15939703Sbloom 				if (terrno == ECONNRESET && !connreset) {
16039703Sbloom 					connreset = 1;
16139703Sbloom 					ns--;
16239703Sbloom 				}
16318144Sralph 				continue;
16418144Sralph 			}
16518144Sralph 			cp = answer;
16632603Skarels 			if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
16732603Skarels #ifdef DEBUG
16832603Skarels 				if (_res.options & RES_DEBUG)
16932603Skarels 					fprintf(stderr, "response truncated\n");
17032603Skarels #endif DEBUG
17132603Skarels 				len = anslen;
17232603Skarels 				truncated = 1;
17332603Skarels 			} else
17432603Skarels 				len = resplen;
17527664Sbloom 			while (len != 0 &&
17627031Skjd 			   (n = read(s, (char *)cp, (int)len)) > 0) {
17718144Sralph 				cp += n;
17818144Sralph 				len -= n;
17918144Sralph 			}
18018144Sralph 			if (n <= 0) {
18129434Sbloom 				terrno = errno;
18224734Sbloom #ifdef DEBUG
18318144Sralph 				if (_res.options & RES_DEBUG)
18427031Skjd 					perror("read failed");
18525243Skjd #endif DEBUG
18618144Sralph 				(void) close(s);
18718144Sralph 				s = -1;
18818144Sralph 				continue;
18918144Sralph 			}
19032603Skarels 			if (truncated) {
19132603Skarels 				/*
19232603Skarels 				 * Flush rest of answer
19332603Skarels 				 * so connection stays in synch.
19432603Skarels 				 */
19532603Skarels 				anhp->tc = 1;
19632603Skarels 				len = resplen - anslen;
19732603Skarels 				while (len != 0) {
19832603Skarels 					n = (len > sizeof(junk) ?
19932603Skarels 					    sizeof(junk) : len);
20032603Skarels 					if ((n = read(s, junk, n)) > 0)
20132603Skarels 						len -= n;
20232603Skarels 					else
20332603Skarels 						break;
20432603Skarels 				}
20532603Skarels 			}
20618144Sralph 		} else {
20718144Sralph 			/*
20818144Sralph 			 * Use datagrams.
20918144Sralph 			 */
21039790Sbloom 			if (s < 0) {
21118144Sralph 				s = socket(AF_INET, SOCK_DGRAM, 0);
21239790Sbloom 				if (s < 0) {
21339790Sbloom 					terrno = errno;
21439790Sbloom #ifdef DEBUG
21539790Sbloom 					if (_res.options & RES_DEBUG)
21639790Sbloom 					    perror("socket (dg) failed");
21739790Sbloom #endif DEBUG
21839790Sbloom 					continue;
21939790Sbloom 				}
22039790Sbloom 			}
22126483Skarels #if	BSD >= 43
22238214Skarels 			/*
22338214Skarels 			 * I'm tired of answering this question, so:
22438214Skarels 			 * On a 4.3BSD+ machine (client and server,
22538214Skarels 			 * actually), sending to a nameserver datagram
22638214Skarels 			 * port with no nameserver will cause an
22738214Skarels 			 * ICMP port unreachable message to be returned.
22838214Skarels 			 * If our datagram socket is "connected" to the
22938214Skarels 			 * server, we get an ECONNREFUSED error on the next
23038214Skarels 			 * socket operation, and select returns if the
23138214Skarels 			 * error message is received.  We can thus detect
23238214Skarels 			 * the absence of a nameserver without timing out.
23338214Skarels 			 * If we have sent queries to at least two servers,
23438214Skarels 			 * however, we don't want to remain connected,
23538214Skarels 			 * as we wish to receive answers from the first
23638214Skarels 			 * server to respond.
23738214Skarels 			 */
23838214Skarels 			if (_res.nscount == 1 || (try == 0 && ns == 0)) {
23930394Skjd 				/*
24031113Skarels 				 * Don't use connect if we might
24131113Skarels 				 * still receive a response
24231113Skarels 				 * from another server.
24330394Skjd 				 */
24432603Skarels 				if (connected == 0) {
24532603Skarels 					if (connect(s, &_res.nsaddr_list[ns],
24632603Skarels 					    sizeof(struct sockaddr)) < 0) {
24724734Sbloom #ifdef DEBUG
24832603Skarels 						if (_res.options & RES_DEBUG)
24932603Skarels 							perror("connect");
25032603Skarels #endif DEBUG
25132603Skarels 						continue;
25232603Skarels 					}
25332603Skarels 					connected = 1;
25432603Skarels 				}
25532603Skarels 				if (send(s, buf, buflen, 0) != buflen) {
25632603Skarels #ifdef DEBUG
25730394Skjd 					if (_res.options & RES_DEBUG)
25832603Skarels 						perror("send");
25925243Skjd #endif DEBUG
26030394Skjd 					continue;
26130394Skjd 				}
26234341Skarels 			} else {
26334341Skarels 				/*
26434341Skarels 				 * Disconnect if we want to listen
26534341Skarels 				 * for responses from more than one server.
26634341Skarels 				 */
26734341Skarels 				if (connected) {
26834341Skarels 					(void) connect(s, &no_addr,
26934341Skarels 					    sizeof(no_addr));
27034341Skarels 					connected = 0;
27134341Skarels 				}
27230394Skjd #endif BSD
27334341Skarels 				if (sendto(s, buf, buflen, 0,
27434341Skarels 				    &_res.nsaddr_list[ns],
27534341Skarels 				    sizeof(struct sockaddr)) != buflen) {
27626483Skarels #ifdef DEBUG
27734341Skarels 					if (_res.options & RES_DEBUG)
27834341Skarels 						perror("sendto");
27926483Skarels #endif DEBUG
28034341Skarels 					continue;
28134341Skarels 				}
28234341Skarels #if	BSD >= 43
28326483Skarels 			}
28434341Skarels #endif
28530394Skjd 
28618144Sralph 			/*
28727031Skjd 			 * Wait for reply
28818144Sralph 			 */
28938214Skarels 			timeout.tv_sec = (_res.retrans << try);
29038214Skarels 			if (try > 0)
29138214Skarels 				timeout.tv_sec /= _res.nscount;
29227031Skjd 			if (timeout.tv_sec <= 0)
29327031Skjd 				timeout.tv_sec = 1;
29418144Sralph 			timeout.tv_usec = 0;
29526323Skarels wait:
29627796Skjd 			FD_ZERO(&dsmask);
29727796Skjd 			FD_SET(s, &dsmask);
29827664Sbloom 			n = select(s+1, &dsmask, (fd_set *)NULL,
29927664Sbloom 				(fd_set *)NULL, &timeout);
30018144Sralph 			if (n < 0) {
30124734Sbloom #ifdef DEBUG
30218144Sralph 				if (_res.options & RES_DEBUG)
30327031Skjd 					perror("select");
30425243Skjd #endif DEBUG
30518144Sralph 				continue;
30618144Sralph 			}
30718144Sralph 			if (n == 0) {
30818144Sralph 				/*
30918144Sralph 				 * timeout
31018144Sralph 				 */
31124734Sbloom #ifdef DEBUG
31218144Sralph 				if (_res.options & RES_DEBUG)
31318144Sralph 					printf("timeout\n");
31425243Skjd #endif DEBUG
31538214Skarels #if BSD >= 43
31626483Skarels 				gotsomewhere = 1;
31738214Skarels #endif
31818144Sralph 				continue;
31918144Sralph 			}
32025332Skjd 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
32124734Sbloom #ifdef DEBUG
32218144Sralph 				if (_res.options & RES_DEBUG)
32327031Skjd 					perror("recvfrom");
32425243Skjd #endif DEBUG
32518144Sralph 				continue;
32618144Sralph 			}
32726483Skarels 			gotsomewhere = 1;
32818144Sralph 			if (id != anhp->id) {
32918144Sralph 				/*
33018144Sralph 				 * response from old query, ignore it
33118144Sralph 				 */
33224734Sbloom #ifdef DEBUG
33318144Sralph 				if (_res.options & RES_DEBUG) {
33418144Sralph 					printf("old answer:\n");
33518144Sralph 					p_query(answer);
33618144Sralph 				}
33725243Skjd #endif DEBUG
33826323Skarels 				goto wait;
33918144Sralph 			}
34018144Sralph 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
34118144Sralph 				/*
34238214Skarels 				 * get rest of answer;
34338214Skarels 				 * use TCP with same server.
34418144Sralph 				 */
34524734Sbloom #ifdef DEBUG
34618144Sralph 				if (_res.options & RES_DEBUG)
34718144Sralph 					printf("truncated answer\n");
34825243Skjd #endif DEBUG
34918144Sralph 				(void) close(s);
35018144Sralph 				s = -1;
35118144Sralph 				v_circuit = 1;
35238214Skarels 				goto usevc;
35318144Sralph 			}
35418144Sralph 		}
35524734Sbloom #ifdef DEBUG
35618144Sralph 		if (_res.options & RES_DEBUG) {
35718144Sralph 			printf("got answer:\n");
35818144Sralph 			p_query(answer);
35918144Sralph 		}
36025243Skjd #endif DEBUG
36126322Sbloom 		/*
36238214Skarels 		 * If using virtual circuits, we assume that the first server
36338214Skarels 		 * is preferred * over the rest (i.e. it is on the local
36438214Skarels 		 * machine) and only keep that one open.
36538214Skarels 		 * If we have temporarily opened a virtual circuit,
36638214Skarels 		 * or if we haven't been asked to keep a socket open,
36738214Skarels 		 * close the socket.
36826322Sbloom 		 */
36938214Skarels 		if ((v_circuit &&
37038214Skarels 		    ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
37138214Skarels 		    (_res.options & RES_STAYOPEN) == 0) {
37226322Sbloom 			(void) close(s);
37326322Sbloom 			s = -1;
37426322Sbloom 		}
37534341Skarels 		return (resplen);
37625243Skjd 	   }
37718144Sralph 	}
37829434Sbloom 	if (s >= 0) {
37929434Sbloom 		(void) close(s);
38029434Sbloom 		s = -1;
38129434Sbloom 	}
38229434Sbloom 	if (v_circuit == 0)
38329434Sbloom 		if (gotsomewhere == 0)
38438214Skarels 			errno = ECONNREFUSED;	/* no nameservers found */
38529434Sbloom 		else
38638214Skarels 			errno = ETIMEDOUT;	/* no answer obtained */
38726483Skarels 	else
38829434Sbloom 		errno = terrno;
38918144Sralph 	return (-1);
39018144Sralph }
39127025Sbloom 
39227025Sbloom /*
39327025Sbloom  * This routine is for closing the socket if a virtual circuit is used and
39427025Sbloom  * the program wants to close it.  This provides support for endhostent()
39527025Sbloom  * which expects to close the socket.
39627025Sbloom  *
39727025Sbloom  * This routine is not expected to be user visible.
39827025Sbloom  */
39927025Sbloom _res_close()
40027025Sbloom {
40127025Sbloom 	if (s != -1) {
40227025Sbloom 		(void) close(s);
40327025Sbloom 		s = -1;
40427025Sbloom 	}
40527025Sbloom }
406