Commit | Line | Data |
---|---|---|
b423e985 | 1 | /* |
8ea4199d | 2 | * Copyright (c) 1985 Regents of the University of California. |
6b2f9dd0 KB |
3 | * All rights reserved. |
4 | * | |
5 | * Redistribution and use in source and binary forms are permitted | |
f4f66d2c KB |
6 | * provided that the above copyright notice and this paragraph are |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
b423e985 RC |
16 | */ |
17 | ||
2ce81398 | 18 | #if defined(LIBC_SCCS) && !defined(lint) |
ca67e7b4 | 19 | static char sccsid[] = "@(#)res_send.c 6.21 (Berkeley) 6/27/88"; |
6b2f9dd0 | 20 | #endif /* LIBC_SCCS and not lint */ |
8ea4199d | 21 | |
616a94ba RC |
22 | /* |
23 | * Send query to name server and wait for reply. | |
24 | */ | |
25 | ||
04a773cb | 26 | #include <sys/param.h> |
616a94ba RC |
27 | #include <sys/time.h> |
28 | #include <sys/socket.h> | |
679f3274 | 29 | #include <sys/uio.h> |
616a94ba RC |
30 | #include <netinet/in.h> |
31 | #include <stdio.h> | |
32 | #include <errno.h> | |
27df2740 | 33 | #include <arpa/nameser.h> |
f94b2f50 | 34 | #include <resolv.h> |
616a94ba RC |
35 | |
36 | extern int errno; | |
37 | ||
3126f4c0 | 38 | static int s = -1; /* socket used for communications */ |
9bb3997d | 39 | static struct sockaddr no_addr; |
15537e14 KD |
40 | |
41 | ||
42 | #ifndef FD_SET | |
43 | #define NFDBITS 32 | |
44 | #define FD_SETSIZE 32 | |
45 | #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) | |
46 | #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) | |
47 | #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) | |
48 | #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) | |
49 | #endif | |
3126f4c0 | 50 | |
a5d4a4c0 JB |
51 | #define KEEPOPEN (RES_USEVC|RES_STAYOPEN) |
52 | ||
31f0ead0 | 53 | res_send(buf, buflen, answer, anslen) |
616a94ba RC |
54 | char *buf; |
55 | int buflen; | |
56 | char *answer; | |
57 | int anslen; | |
58 | { | |
59 | register int n; | |
a5d4a4c0 | 60 | int retry, v_circuit, resplen, ns; |
2344735f | 61 | int gotsomewhere = 0, connected = 0; |
616a94ba RC |
62 | u_short id, len; |
63 | char *cp; | |
ac3ceb42 | 64 | fd_set dsmask; |
616a94ba RC |
65 | struct timeval timeout; |
66 | HEADER *hp = (HEADER *) buf; | |
67 | HEADER *anhp = (HEADER *) answer; | |
679f3274 | 68 | struct iovec iov[2]; |
1ea504e8 | 69 | int terrno = ETIMEDOUT; |
2344735f | 70 | char junk[512]; |
616a94ba | 71 | |
ae94a224 | 72 | #ifdef DEBUG |
616a94ba | 73 | if (_res.options & RES_DEBUG) { |
31f0ead0 | 74 | printf("res_send()\n"); |
616a94ba RC |
75 | p_query(buf); |
76 | } | |
0b8992ca | 77 | #endif DEBUG |
31f0ead0 | 78 | if (!(_res.options & RES_INIT)) |
ae94a224 JB |
79 | if (res_init() == -1) { |
80 | return(-1); | |
81 | } | |
616a94ba RC |
82 | v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; |
83 | id = hp->id; | |
84 | /* | |
85 | * Send request, RETRY times, or until successful | |
86 | */ | |
09af7f8a | 87 | for (retry = _res.retry; retry > 0; retry--) { |
0b8992ca KD |
88 | for (ns = 0; ns < _res.nscount; ns++) { |
89 | #ifdef DEBUG | |
90 | if (_res.options & RES_DEBUG) | |
91 | printf("Querying server (# %d) address = %s\n", ns+1, | |
2344735f | 92 | inet_ntoa(_res.nsaddr_list[ns].sin_addr)); |
0b8992ca | 93 | #endif DEBUG |
616a94ba | 94 | if (v_circuit) { |
2344735f MK |
95 | int truncated = 0; |
96 | ||
616a94ba RC |
97 | /* |
98 | * Use virtual circuit. | |
99 | */ | |
a5d4a4c0 | 100 | if (s < 0) { |
616a94ba | 101 | s = socket(AF_INET, SOCK_STREAM, 0); |
92a82fe4 | 102 | if (s < 0) { |
1ea504e8 | 103 | terrno = errno; |
92a82fe4 MK |
104 | #ifdef DEBUG |
105 | if (_res.options & RES_DEBUG) | |
747d796e | 106 | perror("socket failed"); |
92a82fe4 MK |
107 | #endif DEBUG |
108 | continue; | |
109 | } | |
747d796e | 110 | if (connect(s, &(_res.nsaddr_list[ns]), |
a5d4a4c0 | 111 | sizeof(struct sockaddr)) < 0) { |
1ea504e8 | 112 | terrno = errno; |
ae94a224 | 113 | #ifdef DEBUG |
a5d4a4c0 | 114 | if (_res.options & RES_DEBUG) |
747d796e | 115 | perror("connect failed"); |
0b8992ca | 116 | #endif DEBUG |
a5d4a4c0 JB |
117 | (void) close(s); |
118 | s = -1; | |
119 | continue; | |
120 | } | |
616a94ba RC |
121 | } |
122 | /* | |
123 | * Send length & message | |
124 | */ | |
3126f4c0 | 125 | len = htons((u_short)buflen); |
679f3274 JB |
126 | iov[0].iov_base = (caddr_t)&len; |
127 | iov[0].iov_len = sizeof(len); | |
128 | iov[1].iov_base = buf; | |
129 | iov[1].iov_len = buflen; | |
130 | if (writev(s, iov, 2) != sizeof(len) + buflen) { | |
1ea504e8 | 131 | terrno = errno; |
ae94a224 | 132 | #ifdef DEBUG |
616a94ba | 133 | if (_res.options & RES_DEBUG) |
747d796e | 134 | perror("write failed"); |
0b8992ca | 135 | #endif DEBUG |
616a94ba RC |
136 | (void) close(s); |
137 | s = -1; | |
138 | continue; | |
139 | } | |
140 | /* | |
141 | * Receive length & response | |
142 | */ | |
143 | cp = answer; | |
144 | len = sizeof(short); | |
679f3274 | 145 | while (len != 0 && |
747d796e | 146 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
147 | cp += n; |
148 | len -= n; | |
149 | } | |
150 | if (n <= 0) { | |
1ea504e8 | 151 | terrno = errno; |
ae94a224 | 152 | #ifdef DEBUG |
616a94ba | 153 | if (_res.options & RES_DEBUG) |
747d796e | 154 | perror("read failed"); |
0b8992ca | 155 | #endif DEBUG |
616a94ba RC |
156 | (void) close(s); |
157 | s = -1; | |
158 | continue; | |
159 | } | |
160 | cp = answer; | |
2344735f MK |
161 | if ((resplen = ntohs(*(u_short *)cp)) > anslen) { |
162 | #ifdef DEBUG | |
163 | if (_res.options & RES_DEBUG) | |
164 | fprintf(stderr, "response truncated\n"); | |
165 | #endif DEBUG | |
166 | len = anslen; | |
167 | truncated = 1; | |
168 | } else | |
169 | len = resplen; | |
679f3274 | 170 | while (len != 0 && |
747d796e | 171 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
172 | cp += n; |
173 | len -= n; | |
174 | } | |
175 | if (n <= 0) { | |
1ea504e8 | 176 | terrno = errno; |
ae94a224 | 177 | #ifdef DEBUG |
616a94ba | 178 | if (_res.options & RES_DEBUG) |
747d796e | 179 | perror("read failed"); |
0b8992ca | 180 | #endif DEBUG |
616a94ba RC |
181 | (void) close(s); |
182 | s = -1; | |
183 | continue; | |
184 | } | |
2344735f MK |
185 | if (truncated) { |
186 | /* | |
187 | * Flush rest of answer | |
188 | * so connection stays in synch. | |
189 | */ | |
190 | anhp->tc = 1; | |
191 | len = resplen - anslen; | |
192 | while (len != 0) { | |
193 | n = (len > sizeof(junk) ? | |
194 | sizeof(junk) : len); | |
195 | if ((n = read(s, junk, n)) > 0) | |
196 | len -= n; | |
197 | else | |
198 | break; | |
199 | } | |
200 | } | |
616a94ba RC |
201 | } else { |
202 | /* | |
203 | * Use datagrams. | |
204 | */ | |
205 | if (s < 0) | |
206 | s = socket(AF_INET, SOCK_DGRAM, 0); | |
92a82fe4 | 207 | #if BSD >= 43 |
9bb3997d | 208 | if (_res.nscount == 1 || retry == _res.retry) { |
7a8f7802 | 209 | /* |
9bb3997d MK |
210 | * Don't use connect if we might |
211 | * still receive a response | |
212 | * from another server. | |
7a8f7802 | 213 | */ |
2344735f MK |
214 | if (connected == 0) { |
215 | if (connect(s, &_res.nsaddr_list[ns], | |
216 | sizeof(struct sockaddr)) < 0) { | |
217 | #ifdef DEBUG | |
218 | if (_res.options & RES_DEBUG) | |
219 | perror("connect"); | |
220 | #endif DEBUG | |
221 | continue; | |
222 | } | |
223 | connected = 1; | |
224 | } | |
225 | if (send(s, buf, buflen, 0) != buflen) { | |
ae94a224 | 226 | #ifdef DEBUG |
7a8f7802 | 227 | if (_res.options & RES_DEBUG) |
2344735f | 228 | perror("send"); |
0b8992ca | 229 | #endif DEBUG |
7a8f7802 KD |
230 | continue; |
231 | } | |
fee59cfd MK |
232 | } else { |
233 | /* | |
234 | * Disconnect if we want to listen | |
235 | * for responses from more than one server. | |
236 | */ | |
237 | if (connected) { | |
238 | (void) connect(s, &no_addr, | |
239 | sizeof(no_addr)); | |
240 | connected = 0; | |
241 | } | |
7a8f7802 | 242 | #endif BSD |
fee59cfd MK |
243 | if (sendto(s, buf, buflen, 0, |
244 | &_res.nsaddr_list[ns], | |
245 | sizeof(struct sockaddr)) != buflen) { | |
92a82fe4 | 246 | #ifdef DEBUG |
fee59cfd MK |
247 | if (_res.options & RES_DEBUG) |
248 | perror("sendto"); | |
92a82fe4 | 249 | #endif DEBUG |
fee59cfd MK |
250 | continue; |
251 | } | |
252 | #if BSD >= 43 | |
616a94ba | 253 | } |
fee59cfd | 254 | #endif |
7a8f7802 | 255 | |
616a94ba | 256 | /* |
747d796e | 257 | * Wait for reply |
616a94ba | 258 | */ |
747d796e KD |
259 | timeout.tv_sec = (_res.retrans << (_res.retry - retry)) |
260 | / _res.nscount; | |
261 | if (timeout.tv_sec <= 0) | |
262 | timeout.tv_sec = 1; | |
616a94ba | 263 | timeout.tv_usec = 0; |
87b12937 | 264 | wait: |
ac3ceb42 KD |
265 | FD_ZERO(&dsmask); |
266 | FD_SET(s, &dsmask); | |
679f3274 JB |
267 | n = select(s+1, &dsmask, (fd_set *)NULL, |
268 | (fd_set *)NULL, &timeout); | |
616a94ba | 269 | if (n < 0) { |
ae94a224 | 270 | #ifdef DEBUG |
616a94ba | 271 | if (_res.options & RES_DEBUG) |
747d796e | 272 | perror("select"); |
0b8992ca | 273 | #endif DEBUG |
616a94ba RC |
274 | continue; |
275 | } | |
276 | if (n == 0) { | |
277 | /* | |
278 | * timeout | |
279 | */ | |
ae94a224 | 280 | #ifdef DEBUG |
616a94ba RC |
281 | if (_res.options & RES_DEBUG) |
282 | printf("timeout\n"); | |
0b8992ca | 283 | #endif DEBUG |
92a82fe4 | 284 | gotsomewhere = 1; |
616a94ba RC |
285 | continue; |
286 | } | |
dd7a6a73 | 287 | if ((resplen = recv(s, answer, anslen, 0)) <= 0) { |
ae94a224 | 288 | #ifdef DEBUG |
616a94ba | 289 | if (_res.options & RES_DEBUG) |
747d796e | 290 | perror("recvfrom"); |
0b8992ca | 291 | #endif DEBUG |
616a94ba RC |
292 | continue; |
293 | } | |
92a82fe4 | 294 | gotsomewhere = 1; |
616a94ba RC |
295 | if (id != anhp->id) { |
296 | /* | |
297 | * response from old query, ignore it | |
298 | */ | |
ae94a224 | 299 | #ifdef DEBUG |
616a94ba | 300 | if (_res.options & RES_DEBUG) { |
616a94ba | 301 | printf("old answer:\n"); |
616a94ba RC |
302 | p_query(answer); |
303 | } | |
0b8992ca | 304 | #endif DEBUG |
87b12937 | 305 | goto wait; |
616a94ba RC |
306 | } |
307 | if (!(_res.options & RES_IGNTC) && anhp->tc) { | |
308 | /* | |
309 | * get rest of answer | |
310 | */ | |
ae94a224 | 311 | #ifdef DEBUG |
616a94ba RC |
312 | if (_res.options & RES_DEBUG) |
313 | printf("truncated answer\n"); | |
0b8992ca | 314 | #endif DEBUG |
616a94ba RC |
315 | (void) close(s); |
316 | s = -1; | |
09af7f8a KD |
317 | /* |
318 | * retry decremented on continue | |
319 | * to desired starting value | |
320 | */ | |
321 | retry = _res.retry + 1; | |
616a94ba RC |
322 | v_circuit = 1; |
323 | continue; | |
324 | } | |
325 | } | |
ae94a224 | 326 | #ifdef DEBUG |
616a94ba RC |
327 | if (_res.options & RES_DEBUG) { |
328 | printf("got answer:\n"); | |
329 | p_query(answer); | |
330 | } | |
0b8992ca | 331 | #endif DEBUG |
a5d4a4c0 JB |
332 | /* |
333 | * We are going to assume that the first server is preferred | |
334 | * over the rest (i.e. it is on the local machine) and only | |
335 | * keep that one open. | |
336 | */ | |
fee59cfd | 337 | if ((_res.options & KEEPOPEN) == 0 || ns != 0) { |
a5d4a4c0 JB |
338 | (void) close(s); |
339 | s = -1; | |
a5d4a4c0 | 340 | } |
fee59cfd | 341 | return (resplen); |
0b8992ca | 342 | } |
616a94ba | 343 | } |
1ea504e8 JB |
344 | if (s >= 0) { |
345 | (void) close(s); | |
346 | s = -1; | |
347 | } | |
348 | if (v_circuit == 0) | |
349 | if (gotsomewhere == 0) | |
350 | errno = ECONNREFUSED; | |
351 | else | |
352 | errno = ETIMEDOUT; | |
92a82fe4 | 353 | else |
1ea504e8 | 354 | errno = terrno; |
616a94ba RC |
355 | return (-1); |
356 | } | |
3126f4c0 JB |
357 | |
358 | /* | |
359 | * This routine is for closing the socket if a virtual circuit is used and | |
360 | * the program wants to close it. This provides support for endhostent() | |
361 | * which expects to close the socket. | |
362 | * | |
363 | * This routine is not expected to be user visible. | |
364 | */ | |
365 | _res_close() | |
366 | { | |
367 | if (s != -1) { | |
368 | (void) close(s); | |
369 | s = -1; | |
370 | } | |
371 | } |