Commit | Line | Data |
---|---|---|
882ceeb2 | 1 | /*- |
e9e1a1f3 | 2 | * Copyright (c) 1985, 1989 Regents of the University of California. |
6b2f9dd0 KB |
3 | * All rights reserved. |
4 | * | |
269a7923 | 5 | * %sccs.include.redist.c% |
882ceeb2 MK |
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-- | |
b423e985 RC |
26 | */ |
27 | ||
2ce81398 | 28 | #if defined(LIBC_SCCS) && !defined(lint) |
882ceeb2 MK |
29 | static char sccsid[] = "@(#)res_send.c 6.28 (Berkeley) %G%"; |
30 | static char rcsid[] = "$Id: res_send.c,v 4.9.1.1 1993/05/02 22:43:03 vixie Rel $"; | |
6b2f9dd0 | 31 | #endif /* LIBC_SCCS and not lint */ |
8ea4199d | 32 | |
616a94ba RC |
33 | /* |
34 | * Send query to name server and wait for reply. | |
35 | */ | |
36 | ||
04a773cb | 37 | #include <sys/param.h> |
616a94ba RC |
38 | #include <sys/time.h> |
39 | #include <sys/socket.h> | |
679f3274 | 40 | #include <sys/uio.h> |
616a94ba | 41 | #include <netinet/in.h> |
24fac7d8 | 42 | #include <arpa/nameser.h> |
882ceeb2 | 43 | #include <arpa/inet.h> |
616a94ba RC |
44 | #include <stdio.h> |
45 | #include <errno.h> | |
f94b2f50 | 46 | #include <resolv.h> |
24fac7d8 KB |
47 | #include <unistd.h> |
48 | #include <string.h> | |
616a94ba | 49 | |
3126f4c0 | 50 | static int s = -1; /* socket used for communications */ |
9bb3997d | 51 | static struct sockaddr no_addr; |
15537e14 KD |
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 | |
3126f4c0 | 61 | |
31f0ead0 | 62 | res_send(buf, buflen, answer, anslen) |
24fac7d8 | 63 | const char *buf; |
616a94ba RC |
64 | int buflen; |
65 | char *answer; | |
66 | int anslen; | |
67 | { | |
68 | register int n; | |
e9e1a1f3 | 69 | int try, v_circuit, resplen, ns; |
2344735f | 70 | int gotsomewhere = 0, connected = 0; |
17b5e053 | 71 | int connreset = 0; |
616a94ba RC |
72 | u_short id, len; |
73 | char *cp; | |
ac3ceb42 | 74 | fd_set dsmask; |
616a94ba RC |
75 | struct timeval timeout; |
76 | HEADER *hp = (HEADER *) buf; | |
77 | HEADER *anhp = (HEADER *) answer; | |
882ceeb2 | 78 | u_int badns; /* XXX NSMAX can't exceed #/bits per this */ |
679f3274 | 79 | struct iovec iov[2]; |
1ea504e8 | 80 | int terrno = ETIMEDOUT; |
2344735f | 81 | char junk[512]; |
616a94ba | 82 | |
ae94a224 | 83 | #ifdef DEBUG |
882ceeb2 MK |
84 | if ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY)) { |
85 | printf(";; res_send()\n"); | |
371d3c9b | 86 | __p_query(buf); |
616a94ba | 87 | } |
882ceeb2 | 88 | #endif |
31f0ead0 | 89 | if (!(_res.options & RES_INIT)) |
ae94a224 JB |
90 | if (res_init() == -1) { |
91 | return(-1); | |
92 | } | |
616a94ba RC |
93 | v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; |
94 | id = hp->id; | |
882ceeb2 | 95 | badns = 0; |
616a94ba RC |
96 | /* |
97 | * Send request, RETRY times, or until successful | |
98 | */ | |
e9e1a1f3 | 99 | for (try = 0; try < _res.retry; try++) { |
882ceeb2 MK |
100 | for (ns = 0; ns < _res.nscount; ns++) { |
101 | if (badns & (1<<ns)) | |
102 | continue; | |
0b8992ca KD |
103 | #ifdef DEBUG |
104 | if (_res.options & RES_DEBUG) | |
882ceeb2 MK |
105 | printf(";; Querying server (# %d) address = %s\n", |
106 | ns+1, | |
107 | inet_ntoa(_res.nsaddr_list[ns].sin_addr)); | |
108 | #endif | |
e9e1a1f3 | 109 | usevc: |
616a94ba | 110 | if (v_circuit) { |
2344735f MK |
111 | int truncated = 0; |
112 | ||
616a94ba | 113 | /* |
e9e1a1f3 MK |
114 | * Use virtual circuit; |
115 | * at most one attempt per server. | |
616a94ba | 116 | */ |
e9e1a1f3 | 117 | try = _res.retry; |
a5d4a4c0 | 118 | if (s < 0) { |
616a94ba | 119 | s = socket(AF_INET, SOCK_STREAM, 0); |
92a82fe4 | 120 | if (s < 0) { |
1ea504e8 | 121 | terrno = errno; |
92a82fe4 MK |
122 | #ifdef DEBUG |
123 | if (_res.options & RES_DEBUG) | |
f7d00d5e | 124 | perror("socket (vc) failed"); |
882ceeb2 | 125 | #endif |
92a82fe4 MK |
126 | continue; |
127 | } | |
24fac7d8 KB |
128 | if (connect(s, |
129 | (struct sockaddr *)&(_res.nsaddr_list[ns]), | |
130 | sizeof(struct sockaddr)) < 0) { | |
1ea504e8 | 131 | terrno = errno; |
ae94a224 | 132 | #ifdef DEBUG |
a5d4a4c0 | 133 | if (_res.options & RES_DEBUG) |
747d796e | 134 | perror("connect failed"); |
882ceeb2 | 135 | #endif |
a5d4a4c0 JB |
136 | (void) close(s); |
137 | s = -1; | |
138 | continue; | |
139 | } | |
616a94ba RC |
140 | } |
141 | /* | |
142 | * Send length & message | |
143 | */ | |
3126f4c0 | 144 | len = htons((u_short)buflen); |
679f3274 JB |
145 | iov[0].iov_base = (caddr_t)&len; |
146 | iov[0].iov_len = sizeof(len); | |
24fac7d8 | 147 | iov[1].iov_base = (char *)buf; |
679f3274 JB |
148 | iov[1].iov_len = buflen; |
149 | if (writev(s, iov, 2) != sizeof(len) + buflen) { | |
1ea504e8 | 150 | terrno = errno; |
ae94a224 | 151 | #ifdef DEBUG |
616a94ba | 152 | if (_res.options & RES_DEBUG) |
747d796e | 153 | perror("write failed"); |
882ceeb2 | 154 | #endif |
616a94ba RC |
155 | (void) close(s); |
156 | s = -1; | |
157 | continue; | |
158 | } | |
159 | /* | |
160 | * Receive length & response | |
161 | */ | |
162 | cp = answer; | |
163 | len = sizeof(short); | |
679f3274 | 164 | while (len != 0 && |
747d796e | 165 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
166 | cp += n; |
167 | len -= n; | |
168 | } | |
169 | if (n <= 0) { | |
1ea504e8 | 170 | terrno = errno; |
ae94a224 | 171 | #ifdef DEBUG |
616a94ba | 172 | if (_res.options & RES_DEBUG) |
747d796e | 173 | perror("read failed"); |
882ceeb2 | 174 | #endif |
616a94ba RC |
175 | (void) close(s); |
176 | s = -1; | |
17b5e053 JB |
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 | } | |
616a94ba RC |
190 | continue; |
191 | } | |
192 | cp = answer; | |
2344735f MK |
193 | if ((resplen = ntohs(*(u_short *)cp)) > anslen) { |
194 | #ifdef DEBUG | |
195 | if (_res.options & RES_DEBUG) | |
882ceeb2 MK |
196 | fprintf(stderr, |
197 | ";; response truncated\n"); | |
198 | #endif | |
2344735f MK |
199 | len = anslen; |
200 | truncated = 1; | |
201 | } else | |
202 | len = resplen; | |
679f3274 | 203 | while (len != 0 && |
747d796e | 204 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
205 | cp += n; |
206 | len -= n; | |
207 | } | |
208 | if (n <= 0) { | |
1ea504e8 | 209 | terrno = errno; |
ae94a224 | 210 | #ifdef DEBUG |
616a94ba | 211 | if (_res.options & RES_DEBUG) |
747d796e | 212 | perror("read failed"); |
882ceeb2 | 213 | #endif |
616a94ba RC |
214 | (void) close(s); |
215 | s = -1; | |
216 | continue; | |
217 | } | |
2344735f MK |
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 | } | |
616a94ba RC |
234 | } else { |
235 | /* | |
236 | * Use datagrams. | |
237 | */ | |
f7d00d5e | 238 | if (s < 0) { |
616a94ba | 239 | s = socket(AF_INET, SOCK_DGRAM, 0); |
f7d00d5e JB |
240 | if (s < 0) { |
241 | terrno = errno; | |
242 | #ifdef DEBUG | |
243 | if (_res.options & RES_DEBUG) | |
244 | perror("socket (dg) failed"); | |
882ceeb2 | 245 | #endif |
f7d00d5e JB |
246 | continue; |
247 | } | |
248 | } | |
e9e1a1f3 MK |
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)) { | |
7a8f7802 | 266 | /* |
9bb3997d MK |
267 | * Don't use connect if we might |
268 | * still receive a response | |
269 | * from another server. | |
7a8f7802 | 270 | */ |
2344735f | 271 | if (connected == 0) { |
882ceeb2 MK |
272 | if (connect(s, |
273 | (struct sockaddr *) | |
274 | &_res.nsaddr_list[ns], | |
2344735f MK |
275 | sizeof(struct sockaddr)) < 0) { |
276 | #ifdef DEBUG | |
277 | if (_res.options & RES_DEBUG) | |
278 | perror("connect"); | |
882ceeb2 | 279 | #endif |
2344735f MK |
280 | continue; |
281 | } | |
282 | connected = 1; | |
283 | } | |
284 | if (send(s, buf, buflen, 0) != buflen) { | |
ae94a224 | 285 | #ifdef DEBUG |
7a8f7802 | 286 | if (_res.options & RES_DEBUG) |
2344735f | 287 | perror("send"); |
882ceeb2 | 288 | #endif |
7a8f7802 KD |
289 | continue; |
290 | } | |
fee59cfd MK |
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 | } | |
fee59cfd | 301 | if (sendto(s, buf, buflen, 0, |
24fac7d8 | 302 | (struct sockaddr *)&_res.nsaddr_list[ns], |
fee59cfd | 303 | sizeof(struct sockaddr)) != buflen) { |
92a82fe4 | 304 | #ifdef DEBUG |
fee59cfd MK |
305 | if (_res.options & RES_DEBUG) |
306 | perror("sendto"); | |
882ceeb2 | 307 | #endif |
fee59cfd MK |
308 | continue; |
309 | } | |
616a94ba | 310 | } |
7a8f7802 | 311 | |
616a94ba | 312 | /* |
747d796e | 313 | * Wait for reply |
616a94ba | 314 | */ |
e9e1a1f3 MK |
315 | timeout.tv_sec = (_res.retrans << try); |
316 | if (try > 0) | |
317 | timeout.tv_sec /= _res.nscount; | |
882ceeb2 | 318 | if ((long) timeout.tv_sec <= 0) |
747d796e | 319 | timeout.tv_sec = 1; |
616a94ba | 320 | timeout.tv_usec = 0; |
87b12937 | 321 | wait: |
ac3ceb42 KD |
322 | FD_ZERO(&dsmask); |
323 | FD_SET(s, &dsmask); | |
679f3274 JB |
324 | n = select(s+1, &dsmask, (fd_set *)NULL, |
325 | (fd_set *)NULL, &timeout); | |
616a94ba | 326 | if (n < 0) { |
ae94a224 | 327 | #ifdef DEBUG |
616a94ba | 328 | if (_res.options & RES_DEBUG) |
747d796e | 329 | perror("select"); |
882ceeb2 | 330 | #endif |
616a94ba RC |
331 | continue; |
332 | } | |
333 | if (n == 0) { | |
334 | /* | |
335 | * timeout | |
336 | */ | |
ae94a224 | 337 | #ifdef DEBUG |
616a94ba | 338 | if (_res.options & RES_DEBUG) |
882ceeb2 | 339 | printf(";; timeout\n"); |
e9e1a1f3 | 340 | #endif |
882ceeb2 | 341 | gotsomewhere = 1; |
616a94ba RC |
342 | continue; |
343 | } | |
dd7a6a73 | 344 | if ((resplen = recv(s, answer, anslen, 0)) <= 0) { |
ae94a224 | 345 | #ifdef DEBUG |
616a94ba | 346 | if (_res.options & RES_DEBUG) |
747d796e | 347 | perror("recvfrom"); |
882ceeb2 | 348 | #endif |
616a94ba RC |
349 | continue; |
350 | } | |
92a82fe4 | 351 | gotsomewhere = 1; |
616a94ba RC |
352 | if (id != anhp->id) { |
353 | /* | |
354 | * response from old query, ignore it | |
355 | */ | |
ae94a224 | 356 | #ifdef DEBUG |
882ceeb2 MK |
357 | if ((_res.options & RES_DEBUG) || |
358 | (_res.pfcode & RES_PRF_REPLY)) { | |
359 | printf(";; old answer:\n"); | |
371d3c9b | 360 | __p_query(answer); |
616a94ba | 361 | } |
882ceeb2 | 362 | #endif |
87b12937 | 363 | goto wait; |
616a94ba | 364 | } |
882ceeb2 MK |
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 | } | |
616a94ba RC |
376 | if (!(_res.options & RES_IGNTC) && anhp->tc) { |
377 | /* | |
e9e1a1f3 MK |
378 | * get rest of answer; |
379 | * use TCP with same server. | |
616a94ba | 380 | */ |
ae94a224 | 381 | #ifdef DEBUG |
616a94ba | 382 | if (_res.options & RES_DEBUG) |
882ceeb2 MK |
383 | printf(";; truncated answer\n"); |
384 | #endif | |
616a94ba RC |
385 | (void) close(s); |
386 | s = -1; | |
616a94ba | 387 | v_circuit = 1; |
e9e1a1f3 | 388 | goto usevc; |
616a94ba RC |
389 | } |
390 | } | |
ae94a224 | 391 | #ifdef DEBUG |
882ceeb2 MK |
392 | if (_res.options & RES_DEBUG) |
393 | printf(";; got answer:\n"); | |
394 | if ((_res.options & RES_DEBUG) || | |
395 | (_res.pfcode & RES_PRF_REPLY)) | |
371d3c9b | 396 | __p_query(answer); |
882ceeb2 | 397 | #endif |
a5d4a4c0 | 398 | /* |
e9e1a1f3 MK |
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. | |
a5d4a4c0 | 405 | */ |
e9e1a1f3 MK |
406 | if ((v_circuit && |
407 | ((_res.options & RES_USEVC) == 0 || ns != 0)) || | |
408 | (_res.options & RES_STAYOPEN) == 0) { | |
a5d4a4c0 JB |
409 | (void) close(s); |
410 | s = -1; | |
a5d4a4c0 | 411 | } |
fee59cfd | 412 | return (resplen); |
0b8992ca | 413 | } |
616a94ba | 414 | } |
1ea504e8 JB |
415 | if (s >= 0) { |
416 | (void) close(s); | |
417 | s = -1; | |
418 | } | |
419 | if (v_circuit == 0) | |
420 | if (gotsomewhere == 0) | |
e9e1a1f3 | 421 | errno = ECONNREFUSED; /* no nameservers found */ |
1ea504e8 | 422 | else |
e9e1a1f3 | 423 | errno = ETIMEDOUT; /* no answer obtained */ |
92a82fe4 | 424 | else |
1ea504e8 | 425 | errno = terrno; |
616a94ba RC |
426 | return (-1); |
427 | } | |
3126f4c0 JB |
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 | */ | |
436 | _res_close() | |
437 | { | |
438 | if (s != -1) { | |
439 | (void) close(s); | |
440 | s = -1; | |
441 | } | |
442 | } |