1 /*-
2 * Copyright (c) 1985, 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 * -
7 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies, and that
12 * the name of Digital Equipment Corporation not be used in advertising or
13 * publicity pertaining to distribution of the document or software without
14 * specific, written prior permission.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
17 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
19 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
20 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
21 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
22 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 * SOFTWARE.
24 * -
25 * --Copyright--
26 */
27
28 #if defined(LIBC_SCCS) && !defined(lint)
29 static char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 06/04/93";
30 static char rcsid[] = "$Id: res_send.c,v 4.9.1.1 1993/05/02 22:43:03 vixie Rel $";
31 #endif /* LIBC_SCCS and not lint */
32
33 /*
34 * Send query to name server and wait for reply.
35 */
36
37 #include <sys/param.h>
38 #include <sys/time.h>
39 #include <sys/socket.h>
40 #include <sys/uio.h>
41 #include <netinet/in.h>
42 #include <arpa/nameser.h>
43 #include <arpa/inet.h>
44 #include <stdio.h>
45 #include <errno.h>
46 #include <resolv.h>
47 #include <unistd.h>
48 #include <string.h>
49
50 static int s = -1; /* socket used for communications */
51 static struct sockaddr no_addr;
52
53 #ifndef FD_SET
54 #define NFDBITS 32
55 #define FD_SETSIZE 32
56 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
57 #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
58 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
59 #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
60 #endif
61
res_send(buf,buflen,answer,anslen)62 res_send(buf, buflen, answer, anslen)
63 const char *buf;
64 int buflen;
65 char *answer;
66 int anslen;
67 {
68 register int n;
69 int try, v_circuit, resplen, ns;
70 int gotsomewhere = 0, connected = 0;
71 int connreset = 0;
72 u_short id, len;
73 char *cp;
74 fd_set dsmask;
75 struct timeval timeout;
76 HEADER *hp = (HEADER *) buf;
77 HEADER *anhp = (HEADER *) answer;
78 u_int badns; /* XXX NSMAX can't exceed #/bits per this */
79 struct iovec iov[2];
80 int terrno = ETIMEDOUT;
81 char junk[512];
82
83 #ifdef DEBUG
84 if ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY)) {
85 printf(";; res_send()\n");
86 __p_query(buf);
87 }
88 #endif
89 if (!(_res.options & RES_INIT))
90 if (res_init() == -1) {
91 return(-1);
92 }
93 v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
94 id = hp->id;
95 badns = 0;
96 /*
97 * Send request, RETRY times, or until successful
98 */
99 for (try = 0; try < _res.retry; try++) {
100 for (ns = 0; ns < _res.nscount; ns++) {
101 if (badns & (1<<ns))
102 continue;
103 #ifdef DEBUG
104 if (_res.options & RES_DEBUG)
105 printf(";; Querying server (# %d) address = %s\n",
106 ns+1,
107 inet_ntoa(_res.nsaddr_list[ns].sin_addr));
108 #endif
109 usevc:
110 if (v_circuit) {
111 int truncated = 0;
112
113 /*
114 * Use virtual circuit;
115 * at most one attempt per server.
116 */
117 try = _res.retry;
118 if (s < 0) {
119 s = socket(AF_INET, SOCK_STREAM, 0);
120 if (s < 0) {
121 terrno = errno;
122 #ifdef DEBUG
123 if (_res.options & RES_DEBUG)
124 perror("socket (vc) failed");
125 #endif
126 continue;
127 }
128 if (connect(s,
129 (struct sockaddr *)&(_res.nsaddr_list[ns]),
130 sizeof(struct sockaddr)) < 0) {
131 terrno = errno;
132 #ifdef DEBUG
133 if (_res.options & RES_DEBUG)
134 perror("connect failed");
135 #endif
136 (void) close(s);
137 s = -1;
138 continue;
139 }
140 }
141 /*
142 * Send length & message
143 */
144 len = htons((u_short)buflen);
145 iov[0].iov_base = (caddr_t)&len;
146 iov[0].iov_len = sizeof(len);
147 iov[1].iov_base = (char *)buf;
148 iov[1].iov_len = buflen;
149 if (writev(s, iov, 2) != sizeof(len) + buflen) {
150 terrno = errno;
151 #ifdef DEBUG
152 if (_res.options & RES_DEBUG)
153 perror("write failed");
154 #endif
155 (void) close(s);
156 s = -1;
157 continue;
158 }
159 /*
160 * Receive length & response
161 */
162 cp = answer;
163 len = sizeof(short);
164 while (len != 0 &&
165 (n = read(s, (char *)cp, (int)len)) > 0) {
166 cp += n;
167 len -= n;
168 }
169 if (n <= 0) {
170 terrno = errno;
171 #ifdef DEBUG
172 if (_res.options & RES_DEBUG)
173 perror("read failed");
174 #endif
175 (void) close(s);
176 s = -1;
177 /*
178 * A long running process might get its TCP
179 * connection reset if the remote server was
180 * restarted. Requery the server instead of
181 * trying a new one. When there is only one
182 * server, this means that a query might work
183 * instead of failing. We only allow one reset
184 * per query to prevent looping.
185 */
186 if (terrno == ECONNRESET && !connreset) {
187 connreset = 1;
188 ns--;
189 }
190 continue;
191 }
192 cp = answer;
193 if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
194 #ifdef DEBUG
195 if (_res.options & RES_DEBUG)
196 fprintf(stderr,
197 ";; response truncated\n");
198 #endif
199 len = anslen;
200 truncated = 1;
201 } else
202 len = resplen;
203 while (len != 0 &&
204 (n = read(s, (char *)cp, (int)len)) > 0) {
205 cp += n;
206 len -= n;
207 }
208 if (n <= 0) {
209 terrno = errno;
210 #ifdef DEBUG
211 if (_res.options & RES_DEBUG)
212 perror("read failed");
213 #endif
214 (void) close(s);
215 s = -1;
216 continue;
217 }
218 if (truncated) {
219 /*
220 * Flush rest of answer
221 * so connection stays in synch.
222 */
223 anhp->tc = 1;
224 len = resplen - anslen;
225 while (len != 0) {
226 n = (len > sizeof(junk) ?
227 sizeof(junk) : len);
228 if ((n = read(s, junk, n)) > 0)
229 len -= n;
230 else
231 break;
232 }
233 }
234 } else {
235 /*
236 * Use datagrams.
237 */
238 if (s < 0) {
239 s = socket(AF_INET, SOCK_DGRAM, 0);
240 if (s < 0) {
241 terrno = errno;
242 #ifdef DEBUG
243 if (_res.options & RES_DEBUG)
244 perror("socket (dg) failed");
245 #endif
246 continue;
247 }
248 }
249 /*
250 * I'm tired of answering this question, so:
251 * On a 4.3BSD+ machine (client and server,
252 * actually), sending to a nameserver datagram
253 * port with no nameserver will cause an
254 * ICMP port unreachable message to be returned.
255 * If our datagram socket is "connected" to the
256 * server, we get an ECONNREFUSED error on the next
257 * socket operation, and select returns if the
258 * error message is received. We can thus detect
259 * the absence of a nameserver without timing out.
260 * If we have sent queries to at least two servers,
261 * however, we don't want to remain connected,
262 * as we wish to receive answers from the first
263 * server to respond.
264 */
265 if (_res.nscount == 1 || (try == 0 && ns == 0)) {
266 /*
267 * Don't use connect if we might
268 * still receive a response
269 * from another server.
270 */
271 if (connected == 0) {
272 if (connect(s,
273 (struct sockaddr *)
274 &_res.nsaddr_list[ns],
275 sizeof(struct sockaddr)) < 0) {
276 #ifdef DEBUG
277 if (_res.options & RES_DEBUG)
278 perror("connect");
279 #endif
280 continue;
281 }
282 connected = 1;
283 }
284 if (send(s, buf, buflen, 0) != buflen) {
285 #ifdef DEBUG
286 if (_res.options & RES_DEBUG)
287 perror("send");
288 #endif
289 continue;
290 }
291 } else {
292 /*
293 * Disconnect if we want to listen
294 * for responses from more than one server.
295 */
296 if (connected) {
297 (void) connect(s, &no_addr,
298 sizeof(no_addr));
299 connected = 0;
300 }
301 if (sendto(s, buf, buflen, 0,
302 (struct sockaddr *)&_res.nsaddr_list[ns],
303 sizeof(struct sockaddr)) != buflen) {
304 #ifdef DEBUG
305 if (_res.options & RES_DEBUG)
306 perror("sendto");
307 #endif
308 continue;
309 }
310 }
311
312 /*
313 * Wait for reply
314 */
315 timeout.tv_sec = (_res.retrans << try);
316 if (try > 0)
317 timeout.tv_sec /= _res.nscount;
318 if ((long) timeout.tv_sec <= 0)
319 timeout.tv_sec = 1;
320 timeout.tv_usec = 0;
321 wait:
322 FD_ZERO(&dsmask);
323 FD_SET(s, &dsmask);
324 n = select(s+1, &dsmask, (fd_set *)NULL,
325 (fd_set *)NULL, &timeout);
326 if (n < 0) {
327 #ifdef DEBUG
328 if (_res.options & RES_DEBUG)
329 perror("select");
330 #endif
331 continue;
332 }
333 if (n == 0) {
334 /*
335 * timeout
336 */
337 #ifdef DEBUG
338 if (_res.options & RES_DEBUG)
339 printf(";; timeout\n");
340 #endif
341 gotsomewhere = 1;
342 continue;
343 }
344 if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
345 #ifdef DEBUG
346 if (_res.options & RES_DEBUG)
347 perror("recvfrom");
348 #endif
349 continue;
350 }
351 gotsomewhere = 1;
352 if (id != anhp->id) {
353 /*
354 * response from old query, ignore it
355 */
356 #ifdef DEBUG
357 if ((_res.options & RES_DEBUG) ||
358 (_res.pfcode & RES_PRF_REPLY)) {
359 printf(";; old answer:\n");
360 __p_query(answer);
361 }
362 #endif
363 goto wait;
364 }
365 if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP ||
366 anhp->rcode == REFUSED) {
367 #ifdef DEBUG
368 if (_res.options & RES_DEBUG) {
369 printf("server rejected query:\n");
370 __p_query(answer);
371 }
372 #endif
373 badns |= (1<<ns);
374 continue;
375 }
376 if (!(_res.options & RES_IGNTC) && anhp->tc) {
377 /*
378 * get rest of answer;
379 * use TCP with same server.
380 */
381 #ifdef DEBUG
382 if (_res.options & RES_DEBUG)
383 printf(";; truncated answer\n");
384 #endif
385 (void) close(s);
386 s = -1;
387 v_circuit = 1;
388 goto usevc;
389 }
390 }
391 #ifdef DEBUG
392 if (_res.options & RES_DEBUG)
393 printf(";; got answer:\n");
394 if ((_res.options & RES_DEBUG) ||
395 (_res.pfcode & RES_PRF_REPLY))
396 __p_query(answer);
397 #endif
398 /*
399 * If using virtual circuits, we assume that the first server
400 * is preferred * over the rest (i.e. it is on the local
401 * machine) and only keep that one open.
402 * If we have temporarily opened a virtual circuit,
403 * or if we haven't been asked to keep a socket open,
404 * close the socket.
405 */
406 if ((v_circuit &&
407 ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
408 (_res.options & RES_STAYOPEN) == 0) {
409 (void) close(s);
410 s = -1;
411 }
412 return (resplen);
413 }
414 }
415 if (s >= 0) {
416 (void) close(s);
417 s = -1;
418 }
419 if (v_circuit == 0)
420 if (gotsomewhere == 0)
421 errno = ECONNREFUSED; /* no nameservers found */
422 else
423 errno = ETIMEDOUT; /* no answer obtained */
424 else
425 errno = terrno;
426 return (-1);
427 }
428
429 /*
430 * This routine is for closing the socket if a virtual circuit is used and
431 * the program wants to close it. This provides support for endhostent()
432 * which expects to close the socket.
433 *
434 * This routine is not expected to be user visible.
435 */
_res_close()436 _res_close()
437 {
438 if (s != -1) {
439 (void) close(s);
440 s = -1;
441 }
442 }
443