xref: /csrg-svn/lib/libc/net/res_send.c (revision 39790)
118144Sralph /*
238214Skarels  * Copyright (c) 1985, 1989 Regents of the University of California.
333679Sbostic  * All rights reserved.
433679Sbostic  *
533679Sbostic  * Redistribution and use in source and binary forms are permitted
634817Sbostic  * provided that the above copyright notice and this paragraph are
734817Sbostic  * duplicated in all such forms and that any documentation,
834817Sbostic  * advertising materials, and other materials related to such
934817Sbostic  * distribution and use acknowledge that the software was developed
1034817Sbostic  * by the University of California, Berkeley.  The name of the
1134817Sbostic  * University may not be used to endorse or promote products derived
1234817Sbostic  * from this software without specific prior written permission.
1334817Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434817Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534817Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1618548Sralph  */
1718548Sralph 
1826635Sdonn #if defined(LIBC_SCCS) && !defined(lint)
19*39790Sbloom static char sccsid[] = "@(#)res_send.c	6.24 (Berkeley) 12/27/89";
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 
5118531Sralph res_send(buf, buflen, answer, anslen)
5218144Sralph 	char *buf;
5318144Sralph 	int buflen;
5418144Sralph 	char *answer;
5518144Sralph 	int anslen;
5618144Sralph {
5718144Sralph 	register int n;
5838214Skarels 	int try, v_circuit, resplen, ns;
5932603Skarels 	int gotsomewhere = 0, connected = 0;
6039703Sbloom 	int connreset = 0;
6118144Sralph 	u_short id, len;
6218144Sralph 	char *cp;
6327796Skjd 	fd_set dsmask;
6418144Sralph 	struct timeval timeout;
6518144Sralph 	HEADER *hp = (HEADER *) buf;
6618144Sralph 	HEADER *anhp = (HEADER *) answer;
6727664Sbloom 	struct iovec iov[2];
6829434Sbloom 	int terrno = ETIMEDOUT;
6932603Skarels 	char junk[512];
7018144Sralph 
7124734Sbloom #ifdef DEBUG
7218144Sralph 	if (_res.options & RES_DEBUG) {
7318531Sralph 		printf("res_send()\n");
7418144Sralph 		p_query(buf);
7518144Sralph 	}
7625243Skjd #endif DEBUG
7718531Sralph 	if (!(_res.options & RES_INIT))
7824734Sbloom 		if (res_init() == -1) {
7924734Sbloom 			return(-1);
8024734Sbloom 		}
8118144Sralph 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
8218144Sralph 	id = hp->id;
8318144Sralph 	/*
8418144Sralph 	 * Send request, RETRY times, or until successful
8518144Sralph 	 */
8638214Skarels 	for (try = 0; try < _res.retry; try++) {
8725243Skjd 	   for (ns = 0; ns < _res.nscount; ns++) {
8825243Skjd #ifdef DEBUG
8925243Skjd 		if (_res.options & RES_DEBUG)
9025243Skjd 			printf("Querying server (# %d) address = %s\n", ns+1,
9132603Skarels 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr));
9225243Skjd #endif DEBUG
9338214Skarels 	usevc:
9418144Sralph 		if (v_circuit) {
9532603Skarels 			int truncated = 0;
9632603Skarels 
9718144Sralph 			/*
9838214Skarels 			 * Use virtual circuit;
9938214Skarels 			 * at most one attempt per server.
10018144Sralph 			 */
10138214Skarels 			try = _res.retry;
10226322Sbloom 			if (s < 0) {
10318144Sralph 				s = socket(AF_INET, SOCK_STREAM, 0);
10426483Skarels 				if (s < 0) {
10529434Sbloom 					terrno = errno;
10626483Skarels #ifdef DEBUG
10726483Skarels 					if (_res.options & RES_DEBUG)
108*39790Sbloom 					    perror("socket (vc) failed");
10926483Skarels #endif DEBUG
11026483Skarels 					continue;
11126483Skarels 				}
11227031Skjd 				if (connect(s, &(_res.nsaddr_list[ns]),
11326322Sbloom 				   sizeof(struct sockaddr)) < 0) {
11429434Sbloom 					terrno = errno;
11524734Sbloom #ifdef DEBUG
11626322Sbloom 					if (_res.options & RES_DEBUG)
11727031Skjd 					    perror("connect failed");
11825243Skjd #endif DEBUG
11926322Sbloom 					(void) close(s);
12026322Sbloom 					s = -1;
12126322Sbloom 					continue;
12226322Sbloom 				}
12318144Sralph 			}
12418144Sralph 			/*
12518144Sralph 			 * Send length & message
12618144Sralph 			 */
12727025Sbloom 			len = htons((u_short)buflen);
12827664Sbloom 			iov[0].iov_base = (caddr_t)&len;
12927664Sbloom 			iov[0].iov_len = sizeof(len);
13027664Sbloom 			iov[1].iov_base = buf;
13127664Sbloom 			iov[1].iov_len = buflen;
13227664Sbloom 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
13329434Sbloom 				terrno = errno;
13424734Sbloom #ifdef DEBUG
13518144Sralph 				if (_res.options & RES_DEBUG)
13627031Skjd 					perror("write failed");
13725243Skjd #endif DEBUG
13818144Sralph 				(void) close(s);
13918144Sralph 				s = -1;
14018144Sralph 				continue;
14118144Sralph 			}
14218144Sralph 			/*
14318144Sralph 			 * Receive length & response
14418144Sralph 			 */
14518144Sralph 			cp = answer;
14618144Sralph 			len = sizeof(short);
14727664Sbloom 			while (len != 0 &&
14827031Skjd 			    (n = read(s, (char *)cp, (int)len)) > 0) {
14918144Sralph 				cp += n;
15018144Sralph 				len -= n;
15118144Sralph 			}
15218144Sralph 			if (n <= 0) {
15329434Sbloom 				terrno = errno;
15424734Sbloom #ifdef DEBUG
15518144Sralph 				if (_res.options & RES_DEBUG)
15627031Skjd 					perror("read failed");
15725243Skjd #endif DEBUG
15818144Sralph 				(void) close(s);
15918144Sralph 				s = -1;
16039703Sbloom 				/*
16139703Sbloom 				 * A long running process might get its TCP
16239703Sbloom 				 * connection reset if the remote server was
16339703Sbloom 				 * restarted.  Requery the server instead of
16439703Sbloom 				 * trying a new one.  When there is only one
16539703Sbloom 				 * server, this means that a query might work
16639703Sbloom 				 * instead of failing.  We only allow one reset
16739703Sbloom 				 * per query to prevent looping.
16839703Sbloom 				 */
16939703Sbloom 				if (terrno == ECONNRESET && !connreset) {
17039703Sbloom 					connreset = 1;
17139703Sbloom 					ns--;
17239703Sbloom 				}
17318144Sralph 				continue;
17418144Sralph 			}
17518144Sralph 			cp = answer;
17632603Skarels 			if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
17732603Skarels #ifdef DEBUG
17832603Skarels 				if (_res.options & RES_DEBUG)
17932603Skarels 					fprintf(stderr, "response truncated\n");
18032603Skarels #endif DEBUG
18132603Skarels 				len = anslen;
18232603Skarels 				truncated = 1;
18332603Skarels 			} else
18432603Skarels 				len = resplen;
18527664Sbloom 			while (len != 0 &&
18627031Skjd 			   (n = read(s, (char *)cp, (int)len)) > 0) {
18718144Sralph 				cp += n;
18818144Sralph 				len -= n;
18918144Sralph 			}
19018144Sralph 			if (n <= 0) {
19129434Sbloom 				terrno = errno;
19224734Sbloom #ifdef DEBUG
19318144Sralph 				if (_res.options & RES_DEBUG)
19427031Skjd 					perror("read failed");
19525243Skjd #endif DEBUG
19618144Sralph 				(void) close(s);
19718144Sralph 				s = -1;
19818144Sralph 				continue;
19918144Sralph 			}
20032603Skarels 			if (truncated) {
20132603Skarels 				/*
20232603Skarels 				 * Flush rest of answer
20332603Skarels 				 * so connection stays in synch.
20432603Skarels 				 */
20532603Skarels 				anhp->tc = 1;
20632603Skarels 				len = resplen - anslen;
20732603Skarels 				while (len != 0) {
20832603Skarels 					n = (len > sizeof(junk) ?
20932603Skarels 					    sizeof(junk) : len);
21032603Skarels 					if ((n = read(s, junk, n)) > 0)
21132603Skarels 						len -= n;
21232603Skarels 					else
21332603Skarels 						break;
21432603Skarels 				}
21532603Skarels 			}
21618144Sralph 		} else {
21718144Sralph 			/*
21818144Sralph 			 * Use datagrams.
21918144Sralph 			 */
220*39790Sbloom 			if (s < 0) {
22118144Sralph 				s = socket(AF_INET, SOCK_DGRAM, 0);
222*39790Sbloom 				if (s < 0) {
223*39790Sbloom 					terrno = errno;
224*39790Sbloom #ifdef DEBUG
225*39790Sbloom 					if (_res.options & RES_DEBUG)
226*39790Sbloom 					    perror("socket (dg) failed");
227*39790Sbloom #endif DEBUG
228*39790Sbloom 					continue;
229*39790Sbloom 				}
230*39790Sbloom 			}
23126483Skarels #if	BSD >= 43
23238214Skarels 			/*
23338214Skarels 			 * I'm tired of answering this question, so:
23438214Skarels 			 * On a 4.3BSD+ machine (client and server,
23538214Skarels 			 * actually), sending to a nameserver datagram
23638214Skarels 			 * port with no nameserver will cause an
23738214Skarels 			 * ICMP port unreachable message to be returned.
23838214Skarels 			 * If our datagram socket is "connected" to the
23938214Skarels 			 * server, we get an ECONNREFUSED error on the next
24038214Skarels 			 * socket operation, and select returns if the
24138214Skarels 			 * error message is received.  We can thus detect
24238214Skarels 			 * the absence of a nameserver without timing out.
24338214Skarels 			 * If we have sent queries to at least two servers,
24438214Skarels 			 * however, we don't want to remain connected,
24538214Skarels 			 * as we wish to receive answers from the first
24638214Skarels 			 * server to respond.
24738214Skarels 			 */
24838214Skarels 			if (_res.nscount == 1 || (try == 0 && ns == 0)) {
24930394Skjd 				/*
25031113Skarels 				 * Don't use connect if we might
25131113Skarels 				 * still receive a response
25231113Skarels 				 * from another server.
25330394Skjd 				 */
25432603Skarels 				if (connected == 0) {
25532603Skarels 					if (connect(s, &_res.nsaddr_list[ns],
25632603Skarels 					    sizeof(struct sockaddr)) < 0) {
25724734Sbloom #ifdef DEBUG
25832603Skarels 						if (_res.options & RES_DEBUG)
25932603Skarels 							perror("connect");
26032603Skarels #endif DEBUG
26132603Skarels 						continue;
26232603Skarels 					}
26332603Skarels 					connected = 1;
26432603Skarels 				}
26532603Skarels 				if (send(s, buf, buflen, 0) != buflen) {
26632603Skarels #ifdef DEBUG
26730394Skjd 					if (_res.options & RES_DEBUG)
26832603Skarels 						perror("send");
26925243Skjd #endif DEBUG
27030394Skjd 					continue;
27130394Skjd 				}
27234341Skarels 			} else {
27334341Skarels 				/*
27434341Skarels 				 * Disconnect if we want to listen
27534341Skarels 				 * for responses from more than one server.
27634341Skarels 				 */
27734341Skarels 				if (connected) {
27834341Skarels 					(void) connect(s, &no_addr,
27934341Skarels 					    sizeof(no_addr));
28034341Skarels 					connected = 0;
28134341Skarels 				}
28230394Skjd #endif BSD
28334341Skarels 				if (sendto(s, buf, buflen, 0,
28434341Skarels 				    &_res.nsaddr_list[ns],
28534341Skarels 				    sizeof(struct sockaddr)) != buflen) {
28626483Skarels #ifdef DEBUG
28734341Skarels 					if (_res.options & RES_DEBUG)
28834341Skarels 						perror("sendto");
28926483Skarels #endif DEBUG
29034341Skarels 					continue;
29134341Skarels 				}
29234341Skarels #if	BSD >= 43
29326483Skarels 			}
29434341Skarels #endif
29530394Skjd 
29618144Sralph 			/*
29727031Skjd 			 * Wait for reply
29818144Sralph 			 */
29938214Skarels 			timeout.tv_sec = (_res.retrans << try);
30038214Skarels 			if (try > 0)
30138214Skarels 				timeout.tv_sec /= _res.nscount;
30227031Skjd 			if (timeout.tv_sec <= 0)
30327031Skjd 				timeout.tv_sec = 1;
30418144Sralph 			timeout.tv_usec = 0;
30526323Skarels wait:
30627796Skjd 			FD_ZERO(&dsmask);
30727796Skjd 			FD_SET(s, &dsmask);
30827664Sbloom 			n = select(s+1, &dsmask, (fd_set *)NULL,
30927664Sbloom 				(fd_set *)NULL, &timeout);
31018144Sralph 			if (n < 0) {
31124734Sbloom #ifdef DEBUG
31218144Sralph 				if (_res.options & RES_DEBUG)
31327031Skjd 					perror("select");
31425243Skjd #endif DEBUG
31518144Sralph 				continue;
31618144Sralph 			}
31718144Sralph 			if (n == 0) {
31818144Sralph 				/*
31918144Sralph 				 * timeout
32018144Sralph 				 */
32124734Sbloom #ifdef DEBUG
32218144Sralph 				if (_res.options & RES_DEBUG)
32318144Sralph 					printf("timeout\n");
32425243Skjd #endif DEBUG
32538214Skarels #if BSD >= 43
32626483Skarels 				gotsomewhere = 1;
32738214Skarels #endif
32818144Sralph 				continue;
32918144Sralph 			}
33025332Skjd 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
33124734Sbloom #ifdef DEBUG
33218144Sralph 				if (_res.options & RES_DEBUG)
33327031Skjd 					perror("recvfrom");
33425243Skjd #endif DEBUG
33518144Sralph 				continue;
33618144Sralph 			}
33726483Skarels 			gotsomewhere = 1;
33818144Sralph 			if (id != anhp->id) {
33918144Sralph 				/*
34018144Sralph 				 * response from old query, ignore it
34118144Sralph 				 */
34224734Sbloom #ifdef DEBUG
34318144Sralph 				if (_res.options & RES_DEBUG) {
34418144Sralph 					printf("old answer:\n");
34518144Sralph 					p_query(answer);
34618144Sralph 				}
34725243Skjd #endif DEBUG
34826323Skarels 				goto wait;
34918144Sralph 			}
35018144Sralph 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
35118144Sralph 				/*
35238214Skarels 				 * get rest of answer;
35338214Skarels 				 * use TCP with same server.
35418144Sralph 				 */
35524734Sbloom #ifdef DEBUG
35618144Sralph 				if (_res.options & RES_DEBUG)
35718144Sralph 					printf("truncated answer\n");
35825243Skjd #endif DEBUG
35918144Sralph 				(void) close(s);
36018144Sralph 				s = -1;
36118144Sralph 				v_circuit = 1;
36238214Skarels 				goto usevc;
36318144Sralph 			}
36418144Sralph 		}
36524734Sbloom #ifdef DEBUG
36618144Sralph 		if (_res.options & RES_DEBUG) {
36718144Sralph 			printf("got answer:\n");
36818144Sralph 			p_query(answer);
36918144Sralph 		}
37025243Skjd #endif DEBUG
37126322Sbloom 		/*
37238214Skarels 		 * If using virtual circuits, we assume that the first server
37338214Skarels 		 * is preferred * over the rest (i.e. it is on the local
37438214Skarels 		 * machine) and only keep that one open.
37538214Skarels 		 * If we have temporarily opened a virtual circuit,
37638214Skarels 		 * or if we haven't been asked to keep a socket open,
37738214Skarels 		 * close the socket.
37826322Sbloom 		 */
37938214Skarels 		if ((v_circuit &&
38038214Skarels 		    ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
38138214Skarels 		    (_res.options & RES_STAYOPEN) == 0) {
38226322Sbloom 			(void) close(s);
38326322Sbloom 			s = -1;
38426322Sbloom 		}
38534341Skarels 		return (resplen);
38625243Skjd 	   }
38718144Sralph 	}
38829434Sbloom 	if (s >= 0) {
38929434Sbloom 		(void) close(s);
39029434Sbloom 		s = -1;
39129434Sbloom 	}
39229434Sbloom 	if (v_circuit == 0)
39329434Sbloom 		if (gotsomewhere == 0)
39438214Skarels 			errno = ECONNREFUSED;	/* no nameservers found */
39529434Sbloom 		else
39638214Skarels 			errno = ETIMEDOUT;	/* no answer obtained */
39726483Skarels 	else
39829434Sbloom 		errno = terrno;
39918144Sralph 	return (-1);
40018144Sralph }
40127025Sbloom 
40227025Sbloom /*
40327025Sbloom  * This routine is for closing the socket if a virtual circuit is used and
40427025Sbloom  * the program wants to close it.  This provides support for endhostent()
40527025Sbloom  * which expects to close the socket.
40627025Sbloom  *
40727025Sbloom  * This routine is not expected to be user visible.
40827025Sbloom  */
40927025Sbloom _res_close()
41027025Sbloom {
41127025Sbloom 	if (s != -1) {
41227025Sbloom 		(void) close(s);
41327025Sbloom 		s = -1;
41427025Sbloom 	}
41527025Sbloom }
416