xref: /csrg-svn/lib/libc/net/res_send.c (revision 27040)
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 #if defined(LIBC_SCCS) && !defined(lint)
9 static char sccsid[] = "@(#)res_send.c	6.11 (Berkeley) 04/11/86";
10 #endif LIBC_SCCS and not lint
11 
12 /*
13  * Send query to name server and wait for reply.
14  */
15 
16 #include <sys/param.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 <resolv.h>
24 
25 extern int errno;
26 
27 static int s = -1;	/* socket used for communications */
28 
29 #define KEEPOPEN (RES_USEVC|RES_STAYOPEN)
30 
31 res_send(buf, buflen, answer, anslen)
32 	char *buf;
33 	int buflen;
34 	char *answer;
35 	int anslen;
36 {
37 	register int n;
38 	int retry, v_circuit, resplen, ns;
39 	int gotsomewhere = 0;
40 	u_short id, len;
41 	char *cp;
42 	int dsmask;
43 	struct timeval timeout;
44 	HEADER *hp = (HEADER *) buf;
45 	HEADER *anhp = (HEADER *) answer;
46 
47 #ifdef DEBUG
48 	if (_res.options & RES_DEBUG) {
49 		printf("res_send()\n");
50 		p_query(buf);
51 	}
52 #endif DEBUG
53 	if (!(_res.options & RES_INIT))
54 		if (res_init() == -1) {
55 			return(-1);
56 		}
57 	v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
58 	id = hp->id;
59 	/*
60 	 * Send request, RETRY times, or until successful
61 	 */
62 	for (retry = _res.retry; retry > 0; retry--) {
63 	   for (ns = 0; ns < _res.nscount; ns++) {
64 #ifdef DEBUG
65 		if (_res.options & RES_DEBUG)
66 			printf("Querying server (# %d) address = %s\n", ns+1,
67 			      inet_ntoa(_res.nsaddr_list[ns].sin_addr.s_addr));
68 #endif DEBUG
69 		if (v_circuit) {
70 			/*
71 			 * Use virtual circuit.
72 			 */
73 			if (s < 0) {
74 				s = socket(AF_INET, SOCK_STREAM, 0);
75 				if (s < 0) {
76 #ifdef DEBUG
77 					if (_res.options & RES_DEBUG)
78 					    perror("socket failed");
79 #endif DEBUG
80 					continue;
81 				}
82 				if (connect(s, &(_res.nsaddr_list[ns]),
83 				   sizeof(struct sockaddr)) < 0) {
84 #ifdef DEBUG
85 					if (_res.options & RES_DEBUG)
86 					    perror("connect failed");
87 #endif DEBUG
88 					(void) close(s);
89 					s = -1;
90 					continue;
91 				}
92 			}
93 			/*
94 			 * Send length & message
95 			 */
96 			len = htons((u_short)buflen);
97 			if (write(s, (char *)&len, sizeof(len)) != sizeof(len)||
98 			    write(s, buf, buflen) != buflen) {
99 #ifdef DEBUG
100 				if (_res.options & RES_DEBUG)
101 					perror("write failed");
102 #endif DEBUG
103 				(void) close(s);
104 				s = -1;
105 				continue;
106 			}
107 			/*
108 			 * Receive length & response
109 			 */
110 			cp = answer;
111 			len = sizeof(short);
112 			while (len > 0 &&
113 			    (n = read(s, (char *)cp, (int)len)) > 0) {
114 				cp += n;
115 				len -= n;
116 			}
117 			if (n <= 0) {
118 #ifdef DEBUG
119 				if (_res.options & RES_DEBUG)
120 					perror("read failed");
121 #endif DEBUG
122 				(void) close(s);
123 				s = -1;
124 				continue;
125 			}
126 			cp = answer;
127 			resplen = len = ntohs(*(u_short *)cp);
128 			while (len > 0 &&
129 			   (n = read(s, (char *)cp, (int)len)) > 0) {
130 				cp += n;
131 				len -= n;
132 			}
133 			if (n <= 0) {
134 #ifdef DEBUG
135 				if (_res.options & RES_DEBUG)
136 					perror("read failed");
137 #endif DEBUG
138 				(void) close(s);
139 				s = -1;
140 				continue;
141 			}
142 		} else {
143 			/*
144 			 * Use datagrams.
145 			 */
146 			if (s < 0)
147 				s = socket(AF_INET, SOCK_DGRAM, 0);
148 #if	BSD >= 43
149 			if (connect(s, &_res.nsaddr_list[ns],
150 			    sizeof(struct sockaddr)) < 0 ||
151 			    send(s, buf, buflen, 0) != buflen) {
152 #ifdef DEBUG
153 				if (_res.options & RES_DEBUG)
154 					perror("connect");
155 #endif DEBUG
156 				continue;
157 			}
158 #else BSD
159 			if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns],
160 			    sizeof(struct sockaddr)) != buflen) {
161 #ifdef DEBUG
162 				if (_res.options & RES_DEBUG)
163 					perror("sendto");
164 #endif DEBUG
165 				continue;
166 			}
167 #endif BSD
168 			/*
169 			 * Wait for reply
170 			 */
171 			timeout.tv_sec = (_res.retrans << (_res.retry - retry))
172 				/ _res.nscount;
173 			if (timeout.tv_sec <= 0)
174 				timeout.tv_sec = 1;
175 			timeout.tv_usec = 0;
176 wait:
177 			dsmask = 1 << s;
178 			n = select(s+1, &dsmask, 0, 0, &timeout);
179 			if (n < 0) {
180 #ifdef DEBUG
181 				if (_res.options & RES_DEBUG)
182 					perror("select");
183 #endif DEBUG
184 				continue;
185 			}
186 			if (n == 0) {
187 				/*
188 				 * timeout
189 				 */
190 #ifdef DEBUG
191 				if (_res.options & RES_DEBUG)
192 					printf("timeout\n");
193 #endif DEBUG
194 				gotsomewhere = 1;
195 				continue;
196 			}
197 			if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
198 #ifdef DEBUG
199 				if (_res.options & RES_DEBUG)
200 					perror("recvfrom");
201 #endif DEBUG
202 				continue;
203 			}
204 			gotsomewhere = 1;
205 			if (id != anhp->id) {
206 				/*
207 				 * response from old query, ignore it
208 				 */
209 #ifdef DEBUG
210 				if (_res.options & RES_DEBUG) {
211 					printf("old answer:\n");
212 					p_query(answer);
213 				}
214 #endif DEBUG
215 				goto wait;
216 			}
217 			if (!(_res.options & RES_IGNTC) && anhp->tc) {
218 				/*
219 				 * get rest of answer
220 				 */
221 #ifdef DEBUG
222 				if (_res.options & RES_DEBUG)
223 					printf("truncated answer\n");
224 #endif DEBUG
225 				(void) close(s);
226 				s = -1;
227 				/*
228 				 * retry decremented on continue
229 				 * to desired starting value
230 				 */
231 				retry = _res.retry + 1;
232 				v_circuit = 1;
233 				continue;
234 			}
235 		}
236 #ifdef DEBUG
237 		if (_res.options & RES_DEBUG) {
238 			printf("got answer:\n");
239 			p_query(answer);
240 		}
241 #endif DEBUG
242 		/*
243 		 * We are going to assume that the first server is preferred
244 		 * over the rest (i.e. it is on the local machine) and only
245 		 * keep that one open.
246 		 */
247 		if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) {
248 			return (resplen);
249 		} else {
250 			(void) close(s);
251 			s = -1;
252 			return (resplen);
253 		}
254 	   }
255 	}
256 	(void) close(s);
257 	s = -1;
258 	if (v_circuit == 0 && gotsomewhere == 0)
259 		errno = ECONNREFUSED;
260 	else
261 		errno = ETIMEDOUT;
262 	return (-1);
263 }
264 
265 /*
266  * This routine is for closing the socket if a virtual circuit is used and
267  * the program wants to close it.  This provides support for endhostent()
268  * which expects to close the socket.
269  *
270  * This routine is not expected to be user visible.
271  */
272 _res_close()
273 {
274 	if (s != -1) {
275 		(void) close(s);
276 		s = -1;
277 	}
278 }
279