xref: /csrg-svn/lib/libc/net/res_send.c (revision 26323)
1 
2 /*
3  * Copyright (c) 1985 Regents of the University of California.
4  * All rights reserved.  The Berkeley software License Agreement
5  * specifies the terms and conditions for redistribution.
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)res_send.c	6.3 (Berkeley) 02/22/86";
10 #endif not lint
11 
12 /*
13  * Send query to name server and wait for reply.
14  */
15 
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <arpa/nameser.h>
23 #include <arpa/resolv.h>
24 
25 extern int errno;
26 
27 #define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
28 
29 res_send(buf, buflen, answer, anslen)
30 	char *buf;
31 	int buflen;
32 	char *answer;
33 	int anslen;
34 {
35 	register int n;
36 	int retry, v_circuit, resplen, ns;
37 	static int s = -1;
38 	u_short id, len;
39 	char *cp;
40 	int dsmask;
41 	struct timeval timeout;
42 	HEADER *hp = (HEADER *) buf;
43 	HEADER *anhp = (HEADER *) answer;
44 
45 #ifdef DEBUG
46 	if (_res.options & RES_DEBUG) {
47 		printf("res_send()\n");
48 		p_query(buf);
49 	}
50 #endif DEBUG
51 	if (!(_res.options & RES_INIT))
52 		if (res_init() == -1) {
53 			return(-1);
54 		}
55 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
56 	id = hp->id;
57 	/*
58 	 * Send request, RETRY times, or until successful
59 	 */
60 	for (retry = _res.retry; --retry >= 0; ) {
61 	   for (ns = 0; ns < _res.nscount; ns++) {
62 #ifdef DEBUG
63 		if (_res.options & RES_DEBUG)
64 			printf("Querying server (# %d) address = %s\n", ns+1,
65 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr.s_addr));
66 #endif DEBUG
67 		if (v_circuit) {
68 			/*
69 			 * Use virtual circuit.
70 			 */
71 			if (s < 0) {
72 				s = socket(AF_INET, SOCK_STREAM, 0);
73 				if (connect(s, &(_res.nsaddr_list[ns]),
74 				   sizeof(struct sockaddr)) < 0) {
75 #ifdef DEBUG
76 					if (_res.options & RES_DEBUG)
77 					    printf("connect failed %d\n",errno);
78 #endif DEBUG
79 					(void) close(s);
80 					s = -1;
81 					continue;
82 				}
83 			}
84 			/*
85 			 * Send length & message
86 			 */
87 			len = htons(buflen);
88 			if (write(s, &len, sizeof(len)) != sizeof(len) ||
89 				    write(s, buf, buflen) != buflen) {
90 #ifdef DEBUG
91 				if (_res.options & RES_DEBUG)
92 					printf("write failed %d\n", errno);
93 #endif DEBUG
94 				(void) close(s);
95 				s = -1;
96 				continue;
97 			}
98 			/*
99 			 * Receive length & response
100 			 */
101 			cp = answer;
102 			len = sizeof(short);
103 			while (len > 0 && (n = read(s, cp, len)) > 0) {
104 				cp += n;
105 				len -= n;
106 			}
107 			if (n <= 0) {
108 #ifdef DEBUG
109 				if (_res.options & RES_DEBUG)
110 					printf("read failed %d\n", errno);
111 #endif DEBUG
112 				(void) close(s);
113 				s = -1;
114 				continue;
115 			}
116 			cp = answer;
117 			resplen = len = ntohs(*(short *)cp);
118 			while (len > 0 && (n = read(s, cp, len)) > 0) {
119 				cp += n;
120 				len -= n;
121 			}
122 			if (n <= 0) {
123 #ifdef DEBUG
124 				if (_res.options & RES_DEBUG)
125 					printf("read failed %d\n", errno);
126 #endif DEBUG
127 				(void) close(s);
128 				s = -1;
129 				continue;
130 			}
131 		} else {
132 			/*
133 			 * Use datagrams.
134 			 */
135 			if (s < 0)
136 				s = socket(AF_INET, SOCK_DGRAM, 0);
137 			if (connect(s, &_res.nsaddr_list[ns],
138 			    sizeof(struct sockaddr)) < 0 ||
139 			    send(s, buf, buflen, 0) != buflen) {
140 #ifdef DEBUG
141 				if (_res.options & RES_DEBUG)
142 					printf("connect/send errno = %d\n",
143 					    errno);
144 #endif DEBUG
145 			}
146 			/*
147 			 * Wait for reply
148 			 */
149 			timeout.tv_sec =
150 				((_res.retrans * _res.retry) / _res.nscount);
151 			timeout.tv_usec = 0;
152 wait:
153 			dsmask = 1 << s;
154 			n = select(s+1, &dsmask, 0, 0, &timeout);
155 			if (n < 0) {
156 #ifdef DEBUG
157 				if (_res.options & RES_DEBUG)
158 					printf("select errno = %d\n", errno);
159 #endif DEBUG
160 				continue;
161 			}
162 			if (n == 0) {
163 				/*
164 				 * timeout
165 				 */
166 #ifdef DEBUG
167 				if (_res.options & RES_DEBUG)
168 					printf("timeout\n");
169 #endif DEBUG
170 				continue;
171 			}
172 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
173 #ifdef DEBUG
174 				if (_res.options & RES_DEBUG)
175 					printf("recvfrom, errno=%d\n", errno);
176 #endif DEBUG
177 				continue;
178 			}
179 			if (id != anhp->id) {
180 				/*
181 				 * response from old query, ignore it
182 				 */
183 #ifdef DEBUG
184 				if (_res.options & RES_DEBUG) {
185 					printf("old answer:\n");
186 					p_query(answer);
187 				}
188 #endif DEBUG
189 				goto wait;
190 			}
191 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
192 				/*
193 				 * get rest of answer
194 				 */
195 #ifdef DEBUG
196 				if (_res.options & RES_DEBUG)
197 					printf("truncated answer\n");
198 #endif DEBUG
199 				(void) close(s);
200 				s = -1;
201 				retry = _res.retry;
202 				v_circuit = 1;
203 				continue;
204 			}
205 		}
206 #ifdef DEBUG
207 		if (_res.options & RES_DEBUG) {
208 			printf("got answer:\n");
209 			p_query(answer);
210 		}
211 #endif DEBUG
212 		/*
213 		 * We are going to assume that the first server is preferred
214 		 * over the rest (i.e. it is on the local machine) and only
215 		 * keep that one open.
216 		 */
217 		if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
218 			return (resplen);
219 		} else {
220 			(void) close(s);
221 			s = -1;
222 			return (resplen);
223 		}
224 	   }
225 	}
226 	(void) close(s);
227 	errno = ETIMEDOUT;
228 	return (-1);
229 }
230