xref: /csrg-svn/lib/libc/net/res_send.c (revision 32603)
125243Skjd 
218144Sralph /*
321388Sdist  * Copyright (c) 1985 Regents of the University of California.
421388Sdist  * All rights reserved.  The Berkeley software License Agreement
521388Sdist  * specifies the terms and conditions for redistribution.
618548Sralph  */
718548Sralph 
826635Sdonn #if defined(LIBC_SCCS) && !defined(lint)
9*32603Skarels static char sccsid[] = "@(#)res_send.c	6.18 (Berkeley) 11/07/87";
1026635Sdonn #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 
4126322Sbloom #define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
4226322Sbloom 
4318531Sralph res_send(buf, buflen, answer, anslen)
4418144Sralph 	char *buf;
4518144Sralph 	int buflen;
4618144Sralph 	char *answer;
4718144Sralph 	int anslen;
4818144Sralph {
4918144Sralph 	register int n;
5026322Sbloom 	int retry, v_circuit, resplen, ns;
51*32603Skarels 	int gotsomewhere = 0, connected = 0;
5218144Sralph 	u_short id, len;
5318144Sralph 	char *cp;
5427796Skjd 	fd_set dsmask;
5518144Sralph 	struct timeval timeout;
5618144Sralph 	HEADER *hp = (HEADER *) buf;
5718144Sralph 	HEADER *anhp = (HEADER *) answer;
5827664Sbloom 	struct iovec iov[2];
5929434Sbloom 	int terrno = ETIMEDOUT;
60*32603Skarels 	char junk[512];
6118144Sralph 
6224734Sbloom #ifdef DEBUG
6318144Sralph 	if (_res.options & RES_DEBUG) {
6418531Sralph 		printf("res_send()\n");
6518144Sralph 		p_query(buf);
6618144Sralph 	}
6725243Skjd #endif DEBUG
6818531Sralph 	if (!(_res.options & RES_INIT))
6924734Sbloom 		if (res_init() == -1) {
7024734Sbloom 			return(-1);
7124734Sbloom 		}
7218144Sralph 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
7318144Sralph 	id = hp->id;
7418144Sralph 	/*
7518144Sralph 	 * Send request, RETRY times, or until successful
7618144Sralph 	 */
7727040Skjd 	for (retry = _res.retry; retry > 0; retry--) {
7825243Skjd 	   for (ns = 0; ns < _res.nscount; ns++) {
7925243Skjd #ifdef DEBUG
8025243Skjd 		if (_res.options & RES_DEBUG)
8125243Skjd 			printf("Querying server (# %d) address = %s\n", ns+1,
82*32603Skarels 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr));
8325243Skjd #endif DEBUG
8418144Sralph 		if (v_circuit) {
85*32603Skarels 			int truncated = 0;
86*32603Skarels 
8718144Sralph 			/*
8818144Sralph 			 * Use virtual circuit.
8918144Sralph 			 */
9026322Sbloom 			if (s < 0) {
9118144Sralph 				s = socket(AF_INET, SOCK_STREAM, 0);
9226483Skarels 				if (s < 0) {
9329434Sbloom 					terrno = errno;
9426483Skarels #ifdef DEBUG
9526483Skarels 					if (_res.options & RES_DEBUG)
9627031Skjd 					    perror("socket failed");
9726483Skarels #endif DEBUG
9826483Skarels 					continue;
9926483Skarels 				}
10027031Skjd 				if (connect(s, &(_res.nsaddr_list[ns]),
10126322Sbloom 				   sizeof(struct sockaddr)) < 0) {
10229434Sbloom 					terrno = errno;
10324734Sbloom #ifdef DEBUG
10426322Sbloom 					if (_res.options & RES_DEBUG)
10527031Skjd 					    perror("connect failed");
10625243Skjd #endif DEBUG
10726322Sbloom 					(void) close(s);
10826322Sbloom 					s = -1;
10926322Sbloom 					continue;
11026322Sbloom 				}
11118144Sralph 			}
11218144Sralph 			/*
11318144Sralph 			 * Send length & message
11418144Sralph 			 */
11527025Sbloom 			len = htons((u_short)buflen);
11627664Sbloom 			iov[0].iov_base = (caddr_t)&len;
11727664Sbloom 			iov[0].iov_len = sizeof(len);
11827664Sbloom 			iov[1].iov_base = buf;
11927664Sbloom 			iov[1].iov_len = buflen;
12027664Sbloom 			if (writev(s, iov, 2) != sizeof(len) + buflen) {
12129434Sbloom 				terrno = errno;
12224734Sbloom #ifdef DEBUG
12318144Sralph 				if (_res.options & RES_DEBUG)
12427031Skjd 					perror("write failed");
12525243Skjd #endif DEBUG
12618144Sralph 				(void) close(s);
12718144Sralph 				s = -1;
12818144Sralph 				continue;
12918144Sralph 			}
13018144Sralph 			/*
13118144Sralph 			 * Receive length & response
13218144Sralph 			 */
13318144Sralph 			cp = answer;
13418144Sralph 			len = sizeof(short);
13527664Sbloom 			while (len != 0 &&
13627031Skjd 			    (n = read(s, (char *)cp, (int)len)) > 0) {
13718144Sralph 				cp += n;
13818144Sralph 				len -= n;
13918144Sralph 			}
14018144Sralph 			if (n <= 0) {
14129434Sbloom 				terrno = errno;
14224734Sbloom #ifdef DEBUG
14318144Sralph 				if (_res.options & RES_DEBUG)
14427031Skjd 					perror("read failed");
14525243Skjd #endif DEBUG
14618144Sralph 				(void) close(s);
14718144Sralph 				s = -1;
14818144Sralph 				continue;
14918144Sralph 			}
15018144Sralph 			cp = answer;
151*32603Skarels 			if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
152*32603Skarels #ifdef DEBUG
153*32603Skarels 				if (_res.options & RES_DEBUG)
154*32603Skarels 					fprintf(stderr, "response truncated\n");
155*32603Skarels #endif DEBUG
156*32603Skarels 				len = anslen;
157*32603Skarels 				truncated = 1;
158*32603Skarels 			} else
159*32603Skarels 				len = resplen;
16027664Sbloom 			while (len != 0 &&
16127031Skjd 			   (n = read(s, (char *)cp, (int)len)) > 0) {
16218144Sralph 				cp += n;
16318144Sralph 				len -= n;
16418144Sralph 			}
16518144Sralph 			if (n <= 0) {
16629434Sbloom 				terrno = errno;
16724734Sbloom #ifdef DEBUG
16818144Sralph 				if (_res.options & RES_DEBUG)
16927031Skjd 					perror("read failed");
17025243Skjd #endif DEBUG
17118144Sralph 				(void) close(s);
17218144Sralph 				s = -1;
17318144Sralph 				continue;
17418144Sralph 			}
175*32603Skarels 			if (truncated) {
176*32603Skarels 				/*
177*32603Skarels 				 * Flush rest of answer
178*32603Skarels 				 * so connection stays in synch.
179*32603Skarels 				 */
180*32603Skarels 				anhp->tc = 1;
181*32603Skarels 				len = resplen - anslen;
182*32603Skarels 				while (len != 0) {
183*32603Skarels 					n = (len > sizeof(junk) ?
184*32603Skarels 					    sizeof(junk) : len);
185*32603Skarels 					if ((n = read(s, junk, n)) > 0)
186*32603Skarels 						len -= n;
187*32603Skarels 					else
188*32603Skarels 						break;
189*32603Skarels 				}
190*32603Skarels 			}
19118144Sralph 		} else {
19218144Sralph 			/*
19318144Sralph 			 * Use datagrams.
19418144Sralph 			 */
19518144Sralph 			if (s < 0)
19618144Sralph 				s = socket(AF_INET, SOCK_DGRAM, 0);
19726483Skarels #if	BSD >= 43
19831113Skarels 			if (_res.nscount == 1 || retry == _res.retry) {
19930394Skjd 				/*
20031113Skarels 				 * Don't use connect if we might
20131113Skarels 				 * still receive a response
20231113Skarels 				 * from another server.
20330394Skjd 				 */
204*32603Skarels 				if (connected == 0) {
205*32603Skarels 					if (connect(s, &_res.nsaddr_list[ns],
206*32603Skarels 					    sizeof(struct sockaddr)) < 0) {
20724734Sbloom #ifdef DEBUG
208*32603Skarels 						if (_res.options & RES_DEBUG)
209*32603Skarels 							perror("connect");
210*32603Skarels #endif DEBUG
211*32603Skarels 						continue;
212*32603Skarels 					}
213*32603Skarels 					connected = 1;
214*32603Skarels 				}
215*32603Skarels 				if (send(s, buf, buflen, 0) != buflen) {
216*32603Skarels #ifdef DEBUG
21730394Skjd 					if (_res.options & RES_DEBUG)
218*32603Skarels 						perror("send");
21925243Skjd #endif DEBUG
22030394Skjd 					continue;
22130394Skjd 				}
22230394Skjd 			} else
22330394Skjd #endif BSD
22426483Skarels 			if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
22526483Skarels 			    sizeof(struct sockaddr)) != buflen) {
22626483Skarels #ifdef DEBUG
22727031Skjd 				if (_res.options & RES_DEBUG)
22827031Skjd 					perror("sendto");
22926483Skarels #endif DEBUG
23026483Skarels 				continue;
23126483Skarels 			}
23230394Skjd 
23318144Sralph 			/*
23427031Skjd 			 * Wait for reply
23518144Sralph 			 */
23627031Skjd 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
23727031Skjd 				/ _res.nscount;
23827031Skjd 			if (timeout.tv_sec <= 0)
23927031Skjd 				timeout.tv_sec = 1;
24018144Sralph 			timeout.tv_usec = 0;
24126323Skarels wait:
24227796Skjd 			FD_ZERO(&dsmask);
24327796Skjd 			FD_SET(s, &dsmask);
24427664Sbloom 			n = select(s+1, &dsmask, (fd_set *)NULL,
24527664Sbloom 				(fd_set *)NULL, &timeout);
24618144Sralph 			if (n < 0) {
24724734Sbloom #ifdef DEBUG
24818144Sralph 				if (_res.options & RES_DEBUG)
24927031Skjd 					perror("select");
25025243Skjd #endif DEBUG
25118144Sralph 				continue;
25218144Sralph 			}
25318144Sralph 			if (n == 0) {
25418144Sralph 				/*
25518144Sralph 				 * timeout
25618144Sralph 				 */
25724734Sbloom #ifdef DEBUG
25818144Sralph 				if (_res.options & RES_DEBUG)
25918144Sralph 					printf("timeout\n");
26025243Skjd #endif DEBUG
26131113Skarels 				/*
26231113Skarels 				 * Disconnect if we want to listen
26331113Skarels 				 * for responses from more than one server.
26431113Skarels 				 */
265*32603Skarels 				if (_res.nscount > 1 && connected) {
26631113Skarels 					(void) connect(s, &no_addr,
26731113Skarels 					    sizeof(no_addr));
268*32603Skarels 					connected = 0;
269*32603Skarels 				}
27026483Skarels 				gotsomewhere = 1;
27118144Sralph 				continue;
27218144Sralph 			}
27325332Skjd 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
27424734Sbloom #ifdef DEBUG
27518144Sralph 				if (_res.options & RES_DEBUG)
27627031Skjd 					perror("recvfrom");
27725243Skjd #endif DEBUG
27818144Sralph 				continue;
27918144Sralph 			}
28026483Skarels 			gotsomewhere = 1;
28118144Sralph 			if (id != anhp->id) {
28218144Sralph 				/*
28318144Sralph 				 * response from old query, ignore it
28418144Sralph 				 */
28524734Sbloom #ifdef DEBUG
28618144Sralph 				if (_res.options & RES_DEBUG) {
28718144Sralph 					printf("old answer:\n");
28818144Sralph 					p_query(answer);
28918144Sralph 				}
29025243Skjd #endif DEBUG
29126323Skarels 				goto wait;
29218144Sralph 			}
29318144Sralph 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
29418144Sralph 				/*
29518144Sralph 				 * get rest of answer
29618144Sralph 				 */
29724734Sbloom #ifdef DEBUG
29818144Sralph 				if (_res.options & RES_DEBUG)
29918144Sralph 					printf("truncated answer\n");
30025243Skjd #endif DEBUG
30118144Sralph 				(void) close(s);
30218144Sralph 				s = -1;
30327040Skjd 				/*
30427040Skjd 				 * retry decremented on continue
30527040Skjd 				 * to desired starting value
30627040Skjd 				 */
30727040Skjd 				retry = _res.retry + 1;
30818144Sralph 				v_circuit = 1;
30918144Sralph 				continue;
31018144Sralph 			}
31118144Sralph 		}
31224734Sbloom #ifdef DEBUG
31318144Sralph 		if (_res.options & RES_DEBUG) {
31418144Sralph 			printf("got answer:\n");
31518144Sralph 			p_query(answer);
31618144Sralph 		}
31725243Skjd #endif DEBUG
31826322Sbloom 		/*
31926322Sbloom 		 * We are going to assume that the first server is preferred
32026322Sbloom 		 * over the rest (i.e. it is on the local machine) and only
32126322Sbloom 		 * keep that one open.
32226322Sbloom 		 */
32326322Sbloom 		if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
32426322Sbloom 			return (resplen);
32526322Sbloom 		} else {
32626322Sbloom 			(void) close(s);
32726322Sbloom 			s = -1;
32826322Sbloom 			return (resplen);
32926322Sbloom 		}
33025243Skjd 	   }
33118144Sralph 	}
33229434Sbloom 	if (s >= 0) {
33329434Sbloom 		(void) close(s);
33429434Sbloom 		s = -1;
33529434Sbloom 	}
33629434Sbloom 	if (v_circuit == 0)
33729434Sbloom 		if (gotsomewhere == 0)
33829434Sbloom 			errno = ECONNREFUSED;
33929434Sbloom 		else
34029434Sbloom 			errno = ETIMEDOUT;
34126483Skarels 	else
34229434Sbloom 		errno = terrno;
34318144Sralph 	return (-1);
34418144Sralph }
34527025Sbloom 
34627025Sbloom /*
34727025Sbloom  * This routine is for closing the socket if a virtual circuit is used and
34827025Sbloom  * the program wants to close it.  This provides support for endhostent()
34927025Sbloom  * which expects to close the socket.
35027025Sbloom  *
35127025Sbloom  * This routine is not expected to be user visible.
35227025Sbloom  */
35327025Sbloom _res_close()
35427025Sbloom {
35527025Sbloom 	if (s != -1) {
35627025Sbloom 		(void) close(s);
35727025Sbloom 		s = -1;
35827025Sbloom 	}
35927025Sbloom }
360