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