Commit | Line | Data |
---|---|---|
b423e985 | 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% |
b423e985 RC |
6 | */ |
7 | ||
2ce81398 | 8 | #if defined(LIBC_SCCS) && !defined(lint) |
269a7923 | 9 | static char sccsid[] = "@(#)res_send.c 6.25 (Berkeley) %G%"; |
6b2f9dd0 | 10 | #endif /* LIBC_SCCS and not lint */ |
8ea4199d | 11 | |
616a94ba RC |
12 | /* |
13 | * Send query to name server and wait for reply. | |
14 | */ | |
15 | ||
04a773cb | 16 | #include <sys/param.h> |
616a94ba RC |
17 | #include <sys/time.h> |
18 | #include <sys/socket.h> | |
679f3274 | 19 | #include <sys/uio.h> |
616a94ba RC |
20 | #include <netinet/in.h> |
21 | #include <stdio.h> | |
22 | #include <errno.h> | |
27df2740 | 23 | #include <arpa/nameser.h> |
f94b2f50 | 24 | #include <resolv.h> |
616a94ba RC |
25 | |
26 | extern int errno; | |
27 | ||
3126f4c0 | 28 | static int s = -1; /* socket used for communications */ |
9bb3997d | 29 | static struct sockaddr no_addr; |
15537e14 KD |
30 | |
31 | ||
32 | #ifndef FD_SET | |
33 | #define NFDBITS 32 | |
34 | #define FD_SETSIZE 32 | |
35 | #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) | |
36 | #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) | |
37 | #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) | |
38 | #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) | |
39 | #endif | |
3126f4c0 | 40 | |
31f0ead0 | 41 | res_send(buf, buflen, answer, anslen) |
616a94ba RC |
42 | char *buf; |
43 | int buflen; | |
44 | char *answer; | |
45 | int anslen; | |
46 | { | |
47 | register int n; | |
e9e1a1f3 | 48 | int try, v_circuit, resplen, ns; |
2344735f | 49 | int gotsomewhere = 0, connected = 0; |
17b5e053 | 50 | int connreset = 0; |
616a94ba RC |
51 | u_short id, len; |
52 | char *cp; | |
ac3ceb42 | 53 | fd_set dsmask; |
616a94ba RC |
54 | struct timeval timeout; |
55 | HEADER *hp = (HEADER *) buf; | |
56 | HEADER *anhp = (HEADER *) answer; | |
679f3274 | 57 | struct iovec iov[2]; |
1ea504e8 | 58 | int terrno = ETIMEDOUT; |
2344735f | 59 | char junk[512]; |
616a94ba | 60 | |
ae94a224 | 61 | #ifdef DEBUG |
616a94ba | 62 | if (_res.options & RES_DEBUG) { |
31f0ead0 | 63 | printf("res_send()\n"); |
616a94ba RC |
64 | p_query(buf); |
65 | } | |
0b8992ca | 66 | #endif DEBUG |
31f0ead0 | 67 | if (!(_res.options & RES_INIT)) |
ae94a224 JB |
68 | if (res_init() == -1) { |
69 | return(-1); | |
70 | } | |
616a94ba RC |
71 | v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; |
72 | id = hp->id; | |
73 | /* | |
74 | * Send request, RETRY times, or until successful | |
75 | */ | |
e9e1a1f3 | 76 | for (try = 0; try < _res.retry; try++) { |
0b8992ca KD |
77 | for (ns = 0; ns < _res.nscount; ns++) { |
78 | #ifdef DEBUG | |
79 | if (_res.options & RES_DEBUG) | |
80 | printf("Querying server (# %d) address = %s\n", ns+1, | |
2344735f | 81 | inet_ntoa(_res.nsaddr_list[ns].sin_addr)); |
0b8992ca | 82 | #endif DEBUG |
e9e1a1f3 | 83 | usevc: |
616a94ba | 84 | if (v_circuit) { |
2344735f MK |
85 | int truncated = 0; |
86 | ||
616a94ba | 87 | /* |
e9e1a1f3 MK |
88 | * Use virtual circuit; |
89 | * at most one attempt per server. | |
616a94ba | 90 | */ |
e9e1a1f3 | 91 | try = _res.retry; |
a5d4a4c0 | 92 | if (s < 0) { |
616a94ba | 93 | s = socket(AF_INET, SOCK_STREAM, 0); |
92a82fe4 | 94 | if (s < 0) { |
1ea504e8 | 95 | terrno = errno; |
92a82fe4 MK |
96 | #ifdef DEBUG |
97 | if (_res.options & RES_DEBUG) | |
f7d00d5e | 98 | perror("socket (vc) failed"); |
92a82fe4 MK |
99 | #endif DEBUG |
100 | continue; | |
101 | } | |
747d796e | 102 | if (connect(s, &(_res.nsaddr_list[ns]), |
a5d4a4c0 | 103 | sizeof(struct sockaddr)) < 0) { |
1ea504e8 | 104 | terrno = errno; |
ae94a224 | 105 | #ifdef DEBUG |
a5d4a4c0 | 106 | if (_res.options & RES_DEBUG) |
747d796e | 107 | perror("connect failed"); |
0b8992ca | 108 | #endif DEBUG |
a5d4a4c0 JB |
109 | (void) close(s); |
110 | s = -1; | |
111 | continue; | |
112 | } | |
616a94ba RC |
113 | } |
114 | /* | |
115 | * Send length & message | |
116 | */ | |
3126f4c0 | 117 | len = htons((u_short)buflen); |
679f3274 JB |
118 | iov[0].iov_base = (caddr_t)&len; |
119 | iov[0].iov_len = sizeof(len); | |
120 | iov[1].iov_base = buf; | |
121 | iov[1].iov_len = buflen; | |
122 | if (writev(s, iov, 2) != sizeof(len) + buflen) { | |
1ea504e8 | 123 | terrno = errno; |
ae94a224 | 124 | #ifdef DEBUG |
616a94ba | 125 | if (_res.options & RES_DEBUG) |
747d796e | 126 | perror("write failed"); |
0b8992ca | 127 | #endif DEBUG |
616a94ba RC |
128 | (void) close(s); |
129 | s = -1; | |
130 | continue; | |
131 | } | |
132 | /* | |
133 | * Receive length & response | |
134 | */ | |
135 | cp = answer; | |
136 | len = sizeof(short); | |
679f3274 | 137 | while (len != 0 && |
747d796e | 138 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
139 | cp += n; |
140 | len -= n; | |
141 | } | |
142 | if (n <= 0) { | |
1ea504e8 | 143 | terrno = errno; |
ae94a224 | 144 | #ifdef DEBUG |
616a94ba | 145 | if (_res.options & RES_DEBUG) |
747d796e | 146 | perror("read failed"); |
0b8992ca | 147 | #endif DEBUG |
616a94ba RC |
148 | (void) close(s); |
149 | s = -1; | |
17b5e053 JB |
150 | /* |
151 | * A long running process might get its TCP | |
152 | * connection reset if the remote server was | |
153 | * restarted. Requery the server instead of | |
154 | * trying a new one. When there is only one | |
155 | * server, this means that a query might work | |
156 | * instead of failing. We only allow one reset | |
157 | * per query to prevent looping. | |
158 | */ | |
159 | if (terrno == ECONNRESET && !connreset) { | |
160 | connreset = 1; | |
161 | ns--; | |
162 | } | |
616a94ba RC |
163 | continue; |
164 | } | |
165 | cp = answer; | |
2344735f MK |
166 | if ((resplen = ntohs(*(u_short *)cp)) > anslen) { |
167 | #ifdef DEBUG | |
168 | if (_res.options & RES_DEBUG) | |
169 | fprintf(stderr, "response truncated\n"); | |
170 | #endif DEBUG | |
171 | len = anslen; | |
172 | truncated = 1; | |
173 | } else | |
174 | len = resplen; | |
679f3274 | 175 | while (len != 0 && |
747d796e | 176 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
177 | cp += n; |
178 | len -= n; | |
179 | } | |
180 | if (n <= 0) { | |
1ea504e8 | 181 | terrno = errno; |
ae94a224 | 182 | #ifdef DEBUG |
616a94ba | 183 | if (_res.options & RES_DEBUG) |
747d796e | 184 | perror("read failed"); |
0b8992ca | 185 | #endif DEBUG |
616a94ba RC |
186 | (void) close(s); |
187 | s = -1; | |
188 | continue; | |
189 | } | |
2344735f MK |
190 | if (truncated) { |
191 | /* | |
192 | * Flush rest of answer | |
193 | * so connection stays in synch. | |
194 | */ | |
195 | anhp->tc = 1; | |
196 | len = resplen - anslen; | |
197 | while (len != 0) { | |
198 | n = (len > sizeof(junk) ? | |
199 | sizeof(junk) : len); | |
200 | if ((n = read(s, junk, n)) > 0) | |
201 | len -= n; | |
202 | else | |
203 | break; | |
204 | } | |
205 | } | |
616a94ba RC |
206 | } else { |
207 | /* | |
208 | * Use datagrams. | |
209 | */ | |
f7d00d5e | 210 | if (s < 0) { |
616a94ba | 211 | s = socket(AF_INET, SOCK_DGRAM, 0); |
f7d00d5e JB |
212 | if (s < 0) { |
213 | terrno = errno; | |
214 | #ifdef DEBUG | |
215 | if (_res.options & RES_DEBUG) | |
216 | perror("socket (dg) failed"); | |
217 | #endif DEBUG | |
218 | continue; | |
219 | } | |
220 | } | |
92a82fe4 | 221 | #if BSD >= 43 |
e9e1a1f3 MK |
222 | /* |
223 | * I'm tired of answering this question, so: | |
224 | * On a 4.3BSD+ machine (client and server, | |
225 | * actually), sending to a nameserver datagram | |
226 | * port with no nameserver will cause an | |
227 | * ICMP port unreachable message to be returned. | |
228 | * If our datagram socket is "connected" to the | |
229 | * server, we get an ECONNREFUSED error on the next | |
230 | * socket operation, and select returns if the | |
231 | * error message is received. We can thus detect | |
232 | * the absence of a nameserver without timing out. | |
233 | * If we have sent queries to at least two servers, | |
234 | * however, we don't want to remain connected, | |
235 | * as we wish to receive answers from the first | |
236 | * server to respond. | |
237 | */ | |
238 | if (_res.nscount == 1 || (try == 0 && ns == 0)) { | |
7a8f7802 | 239 | /* |
9bb3997d MK |
240 | * Don't use connect if we might |
241 | * still receive a response | |
242 | * from another server. | |
7a8f7802 | 243 | */ |
2344735f MK |
244 | if (connected == 0) { |
245 | if (connect(s, &_res.nsaddr_list[ns], | |
246 | sizeof(struct sockaddr)) < 0) { | |
247 | #ifdef DEBUG | |
248 | if (_res.options & RES_DEBUG) | |
249 | perror("connect"); | |
250 | #endif DEBUG | |
251 | continue; | |
252 | } | |
253 | connected = 1; | |
254 | } | |
255 | if (send(s, buf, buflen, 0) != buflen) { | |
ae94a224 | 256 | #ifdef DEBUG |
7a8f7802 | 257 | if (_res.options & RES_DEBUG) |
2344735f | 258 | perror("send"); |
0b8992ca | 259 | #endif DEBUG |
7a8f7802 KD |
260 | continue; |
261 | } | |
fee59cfd MK |
262 | } else { |
263 | /* | |
264 | * Disconnect if we want to listen | |
265 | * for responses from more than one server. | |
266 | */ | |
267 | if (connected) { | |
268 | (void) connect(s, &no_addr, | |
269 | sizeof(no_addr)); | |
270 | connected = 0; | |
271 | } | |
7a8f7802 | 272 | #endif BSD |
fee59cfd MK |
273 | if (sendto(s, buf, buflen, 0, |
274 | &_res.nsaddr_list[ns], | |
275 | sizeof(struct sockaddr)) != buflen) { | |
92a82fe4 | 276 | #ifdef DEBUG |
fee59cfd MK |
277 | if (_res.options & RES_DEBUG) |
278 | perror("sendto"); | |
92a82fe4 | 279 | #endif DEBUG |
fee59cfd MK |
280 | continue; |
281 | } | |
282 | #if BSD >= 43 | |
616a94ba | 283 | } |
fee59cfd | 284 | #endif |
7a8f7802 | 285 | |
616a94ba | 286 | /* |
747d796e | 287 | * Wait for reply |
616a94ba | 288 | */ |
e9e1a1f3 MK |
289 | timeout.tv_sec = (_res.retrans << try); |
290 | if (try > 0) | |
291 | timeout.tv_sec /= _res.nscount; | |
747d796e KD |
292 | if (timeout.tv_sec <= 0) |
293 | timeout.tv_sec = 1; | |
616a94ba | 294 | timeout.tv_usec = 0; |
87b12937 | 295 | wait: |
ac3ceb42 KD |
296 | FD_ZERO(&dsmask); |
297 | FD_SET(s, &dsmask); | |
679f3274 JB |
298 | n = select(s+1, &dsmask, (fd_set *)NULL, |
299 | (fd_set *)NULL, &timeout); | |
616a94ba | 300 | if (n < 0) { |
ae94a224 | 301 | #ifdef DEBUG |
616a94ba | 302 | if (_res.options & RES_DEBUG) |
747d796e | 303 | perror("select"); |
0b8992ca | 304 | #endif DEBUG |
616a94ba RC |
305 | continue; |
306 | } | |
307 | if (n == 0) { | |
308 | /* | |
309 | * timeout | |
310 | */ | |
ae94a224 | 311 | #ifdef DEBUG |
616a94ba RC |
312 | if (_res.options & RES_DEBUG) |
313 | printf("timeout\n"); | |
0b8992ca | 314 | #endif DEBUG |
e9e1a1f3 | 315 | #if BSD >= 43 |
92a82fe4 | 316 | gotsomewhere = 1; |
e9e1a1f3 | 317 | #endif |
616a94ba RC |
318 | continue; |
319 | } | |
dd7a6a73 | 320 | if ((resplen = recv(s, answer, anslen, 0)) <= 0) { |
ae94a224 | 321 | #ifdef DEBUG |
616a94ba | 322 | if (_res.options & RES_DEBUG) |
747d796e | 323 | perror("recvfrom"); |
0b8992ca | 324 | #endif DEBUG |
616a94ba RC |
325 | continue; |
326 | } | |
92a82fe4 | 327 | gotsomewhere = 1; |
616a94ba RC |
328 | if (id != anhp->id) { |
329 | /* | |
330 | * response from old query, ignore it | |
331 | */ | |
ae94a224 | 332 | #ifdef DEBUG |
616a94ba | 333 | if (_res.options & RES_DEBUG) { |
616a94ba | 334 | printf("old answer:\n"); |
616a94ba RC |
335 | p_query(answer); |
336 | } | |
0b8992ca | 337 | #endif DEBUG |
87b12937 | 338 | goto wait; |
616a94ba RC |
339 | } |
340 | if (!(_res.options & RES_IGNTC) && anhp->tc) { | |
341 | /* | |
e9e1a1f3 MK |
342 | * get rest of answer; |
343 | * use TCP with same server. | |
616a94ba | 344 | */ |
ae94a224 | 345 | #ifdef DEBUG |
616a94ba RC |
346 | if (_res.options & RES_DEBUG) |
347 | printf("truncated answer\n"); | |
0b8992ca | 348 | #endif DEBUG |
616a94ba RC |
349 | (void) close(s); |
350 | s = -1; | |
616a94ba | 351 | v_circuit = 1; |
e9e1a1f3 | 352 | goto usevc; |
616a94ba RC |
353 | } |
354 | } | |
ae94a224 | 355 | #ifdef DEBUG |
616a94ba RC |
356 | if (_res.options & RES_DEBUG) { |
357 | printf("got answer:\n"); | |
358 | p_query(answer); | |
359 | } | |
0b8992ca | 360 | #endif DEBUG |
a5d4a4c0 | 361 | /* |
e9e1a1f3 MK |
362 | * If using virtual circuits, we assume that the first server |
363 | * is preferred * over the rest (i.e. it is on the local | |
364 | * machine) and only keep that one open. | |
365 | * If we have temporarily opened a virtual circuit, | |
366 | * or if we haven't been asked to keep a socket open, | |
367 | * close the socket. | |
a5d4a4c0 | 368 | */ |
e9e1a1f3 MK |
369 | if ((v_circuit && |
370 | ((_res.options & RES_USEVC) == 0 || ns != 0)) || | |
371 | (_res.options & RES_STAYOPEN) == 0) { | |
a5d4a4c0 JB |
372 | (void) close(s); |
373 | s = -1; | |
a5d4a4c0 | 374 | } |
fee59cfd | 375 | return (resplen); |
0b8992ca | 376 | } |
616a94ba | 377 | } |
1ea504e8 JB |
378 | if (s >= 0) { |
379 | (void) close(s); | |
380 | s = -1; | |
381 | } | |
382 | if (v_circuit == 0) | |
383 | if (gotsomewhere == 0) | |
e9e1a1f3 | 384 | errno = ECONNREFUSED; /* no nameservers found */ |
1ea504e8 | 385 | else |
e9e1a1f3 | 386 | errno = ETIMEDOUT; /* no answer obtained */ |
92a82fe4 | 387 | else |
1ea504e8 | 388 | errno = terrno; |
616a94ba RC |
389 | return (-1); |
390 | } | |
3126f4c0 JB |
391 | |
392 | /* | |
393 | * This routine is for closing the socket if a virtual circuit is used and | |
394 | * the program wants to close it. This provides support for endhostent() | |
395 | * which expects to close the socket. | |
396 | * | |
397 | * This routine is not expected to be user visible. | |
398 | */ | |
399 | _res_close() | |
400 | { | |
401 | if (s != -1) { | |
402 | (void) close(s); | |
403 | s = -1; | |
404 | } | |
405 | } |