Commit | Line | Data |
---|---|---|
0b8992ca | 1 | |
b423e985 | 2 | /* |
8ea4199d DF |
3 | * Copyright (c) 1985 Regents of the University of California. |
4 | * All rights reserved. The Berkeley software License Agreement | |
5 | * specifies the terms and conditions for redistribution. | |
b423e985 RC |
6 | */ |
7 | ||
2ce81398 | 8 | #if defined(LIBC_SCCS) && !defined(lint) |
851ee25c | 9 | static char sccsid[] = "@(#)res_send.c 6.15 (Berkeley) %G%"; |
2ce81398 | 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 */ |
15537e14 KD |
29 | |
30 | ||
31 | #ifndef FD_SET | |
32 | #define NFDBITS 32 | |
33 | #define FD_SETSIZE 32 | |
34 | #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) | |
35 | #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) | |
36 | #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) | |
37 | #define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) | |
38 | #endif | |
3126f4c0 | 39 | |
a5d4a4c0 JB |
40 | #define KEEPOPEN (RES_USEVC|RES_STAYOPEN) |
41 | ||
31f0ead0 | 42 | res_send(buf, buflen, answer, anslen) |
616a94ba RC |
43 | char *buf; |
44 | int buflen; | |
45 | char *answer; | |
46 | int anslen; | |
47 | { | |
48 | register int n; | |
a5d4a4c0 | 49 | int retry, v_circuit, resplen, ns; |
92a82fe4 | 50 | int gotsomewhere = 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; |
616a94ba | 59 | |
ae94a224 | 60 | #ifdef DEBUG |
616a94ba | 61 | if (_res.options & RES_DEBUG) { |
31f0ead0 | 62 | printf("res_send()\n"); |
616a94ba RC |
63 | p_query(buf); |
64 | } | |
0b8992ca | 65 | #endif DEBUG |
31f0ead0 | 66 | if (!(_res.options & RES_INIT)) |
ae94a224 JB |
67 | if (res_init() == -1) { |
68 | return(-1); | |
69 | } | |
616a94ba RC |
70 | v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; |
71 | id = hp->id; | |
72 | /* | |
73 | * Send request, RETRY times, or until successful | |
74 | */ | |
09af7f8a | 75 | for (retry = _res.retry; retry > 0; retry--) { |
0b8992ca KD |
76 | for (ns = 0; ns < _res.nscount; ns++) { |
77 | #ifdef DEBUG | |
78 | if (_res.options & RES_DEBUG) | |
79 | printf("Querying server (# %d) address = %s\n", ns+1, | |
747d796e | 80 | inet_ntoa(_res.nsaddr_list[ns].sin_addr.s_addr)); |
0b8992ca | 81 | #endif DEBUG |
616a94ba RC |
82 | if (v_circuit) { |
83 | /* | |
84 | * Use virtual circuit. | |
85 | */ | |
a5d4a4c0 | 86 | if (s < 0) { |
616a94ba | 87 | s = socket(AF_INET, SOCK_STREAM, 0); |
92a82fe4 | 88 | if (s < 0) { |
1ea504e8 | 89 | terrno = errno; |
92a82fe4 MK |
90 | #ifdef DEBUG |
91 | if (_res.options & RES_DEBUG) | |
747d796e | 92 | perror("socket failed"); |
92a82fe4 MK |
93 | #endif DEBUG |
94 | continue; | |
95 | } | |
747d796e | 96 | if (connect(s, &(_res.nsaddr_list[ns]), |
a5d4a4c0 | 97 | sizeof(struct sockaddr)) < 0) { |
1ea504e8 | 98 | terrno = errno; |
ae94a224 | 99 | #ifdef DEBUG |
a5d4a4c0 | 100 | if (_res.options & RES_DEBUG) |
747d796e | 101 | perror("connect failed"); |
0b8992ca | 102 | #endif DEBUG |
a5d4a4c0 JB |
103 | (void) close(s); |
104 | s = -1; | |
105 | continue; | |
106 | } | |
616a94ba RC |
107 | } |
108 | /* | |
109 | * Send length & message | |
110 | */ | |
3126f4c0 | 111 | len = htons((u_short)buflen); |
679f3274 JB |
112 | iov[0].iov_base = (caddr_t)&len; |
113 | iov[0].iov_len = sizeof(len); | |
114 | iov[1].iov_base = buf; | |
115 | iov[1].iov_len = buflen; | |
116 | if (writev(s, iov, 2) != sizeof(len) + buflen) { | |
1ea504e8 | 117 | terrno = errno; |
ae94a224 | 118 | #ifdef DEBUG |
616a94ba | 119 | if (_res.options & RES_DEBUG) |
747d796e | 120 | perror("write failed"); |
0b8992ca | 121 | #endif DEBUG |
616a94ba RC |
122 | (void) close(s); |
123 | s = -1; | |
124 | continue; | |
125 | } | |
126 | /* | |
127 | * Receive length & response | |
128 | */ | |
129 | cp = answer; | |
130 | len = sizeof(short); | |
679f3274 | 131 | while (len != 0 && |
747d796e | 132 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
133 | cp += n; |
134 | len -= n; | |
135 | } | |
136 | if (n <= 0) { | |
1ea504e8 | 137 | terrno = errno; |
ae94a224 | 138 | #ifdef DEBUG |
616a94ba | 139 | if (_res.options & RES_DEBUG) |
747d796e | 140 | perror("read failed"); |
0b8992ca | 141 | #endif DEBUG |
616a94ba RC |
142 | (void) close(s); |
143 | s = -1; | |
144 | continue; | |
145 | } | |
146 | cp = answer; | |
3126f4c0 | 147 | resplen = len = ntohs(*(u_short *)cp); |
679f3274 | 148 | while (len != 0 && |
747d796e | 149 | (n = read(s, (char *)cp, (int)len)) > 0) { |
616a94ba RC |
150 | cp += n; |
151 | len -= n; | |
152 | } | |
153 | if (n <= 0) { | |
1ea504e8 | 154 | terrno = errno; |
ae94a224 | 155 | #ifdef DEBUG |
616a94ba | 156 | if (_res.options & RES_DEBUG) |
747d796e | 157 | perror("read failed"); |
0b8992ca | 158 | #endif DEBUG |
616a94ba RC |
159 | (void) close(s); |
160 | s = -1; | |
161 | continue; | |
162 | } | |
163 | } else { | |
164 | /* | |
165 | * Use datagrams. | |
166 | */ | |
167 | if (s < 0) | |
168 | s = socket(AF_INET, SOCK_DGRAM, 0); | |
92a82fe4 | 169 | #if BSD >= 43 |
87b12937 MK |
170 | if (connect(s, &_res.nsaddr_list[ns], |
171 | sizeof(struct sockaddr)) < 0 || | |
172 | send(s, buf, buflen, 0) != buflen) { | |
ae94a224 | 173 | #ifdef DEBUG |
747d796e KD |
174 | if (_res.options & RES_DEBUG) |
175 | perror("connect"); | |
0b8992ca | 176 | #endif DEBUG |
92a82fe4 MK |
177 | continue; |
178 | } | |
179 | #else BSD | |
180 | if (sendto(s, buf, buflen, 0, &_res.nsaddr_list[ns], | |
181 | sizeof(struct sockaddr)) != buflen) { | |
182 | #ifdef DEBUG | |
747d796e KD |
183 | if (_res.options & RES_DEBUG) |
184 | perror("sendto"); | |
92a82fe4 MK |
185 | #endif DEBUG |
186 | continue; | |
616a94ba | 187 | } |
92a82fe4 | 188 | #endif BSD |
616a94ba | 189 | /* |
747d796e | 190 | * Wait for reply |
616a94ba | 191 | */ |
747d796e KD |
192 | timeout.tv_sec = (_res.retrans << (_res.retry - retry)) |
193 | / _res.nscount; | |
194 | if (timeout.tv_sec <= 0) | |
195 | timeout.tv_sec = 1; | |
616a94ba | 196 | timeout.tv_usec = 0; |
87b12937 | 197 | wait: |
ac3ceb42 KD |
198 | FD_ZERO(&dsmask); |
199 | FD_SET(s, &dsmask); | |
679f3274 JB |
200 | n = select(s+1, &dsmask, (fd_set *)NULL, |
201 | (fd_set *)NULL, &timeout); | |
616a94ba | 202 | if (n < 0) { |
ae94a224 | 203 | #ifdef DEBUG |
616a94ba | 204 | if (_res.options & RES_DEBUG) |
747d796e | 205 | perror("select"); |
0b8992ca | 206 | #endif DEBUG |
616a94ba RC |
207 | continue; |
208 | } | |
209 | if (n == 0) { | |
210 | /* | |
211 | * timeout | |
212 | */ | |
ae94a224 | 213 | #ifdef DEBUG |
616a94ba RC |
214 | if (_res.options & RES_DEBUG) |
215 | printf("timeout\n"); | |
0b8992ca | 216 | #endif DEBUG |
92a82fe4 | 217 | gotsomewhere = 1; |
616a94ba RC |
218 | continue; |
219 | } | |
dd7a6a73 | 220 | if ((resplen = recv(s, answer, anslen, 0)) <= 0) { |
ae94a224 | 221 | #ifdef DEBUG |
616a94ba | 222 | if (_res.options & RES_DEBUG) |
747d796e | 223 | perror("recvfrom"); |
0b8992ca | 224 | #endif DEBUG |
616a94ba RC |
225 | continue; |
226 | } | |
92a82fe4 | 227 | gotsomewhere = 1; |
616a94ba RC |
228 | if (id != anhp->id) { |
229 | /* | |
230 | * response from old query, ignore it | |
231 | */ | |
ae94a224 | 232 | #ifdef DEBUG |
616a94ba | 233 | if (_res.options & RES_DEBUG) { |
616a94ba | 234 | printf("old answer:\n"); |
616a94ba RC |
235 | p_query(answer); |
236 | } | |
0b8992ca | 237 | #endif DEBUG |
87b12937 | 238 | goto wait; |
616a94ba RC |
239 | } |
240 | if (!(_res.options & RES_IGNTC) && anhp->tc) { | |
241 | /* | |
242 | * get rest of answer | |
243 | */ | |
ae94a224 | 244 | #ifdef DEBUG |
616a94ba RC |
245 | if (_res.options & RES_DEBUG) |
246 | printf("truncated answer\n"); | |
0b8992ca | 247 | #endif DEBUG |
616a94ba RC |
248 | (void) close(s); |
249 | s = -1; | |
09af7f8a KD |
250 | /* |
251 | * retry decremented on continue | |
252 | * to desired starting value | |
253 | */ | |
254 | retry = _res.retry + 1; | |
616a94ba RC |
255 | v_circuit = 1; |
256 | continue; | |
257 | } | |
258 | } | |
ae94a224 | 259 | #ifdef DEBUG |
616a94ba RC |
260 | if (_res.options & RES_DEBUG) { |
261 | printf("got answer:\n"); | |
262 | p_query(answer); | |
263 | } | |
0b8992ca | 264 | #endif DEBUG |
a5d4a4c0 JB |
265 | /* |
266 | * We are going to assume that the first server is preferred | |
267 | * over the rest (i.e. it is on the local machine) and only | |
268 | * keep that one open. | |
269 | */ | |
270 | if ((_res.options & KEEPOPEN) == KEEPOPEN && ns == 0) { | |
271 | return (resplen); | |
272 | } else { | |
273 | (void) close(s); | |
274 | s = -1; | |
275 | return (resplen); | |
276 | } | |
0b8992ca | 277 | } |
616a94ba | 278 | } |
1ea504e8 JB |
279 | if (s >= 0) { |
280 | (void) close(s); | |
281 | s = -1; | |
282 | } | |
283 | if (v_circuit == 0) | |
284 | if (gotsomewhere == 0) | |
285 | errno = ECONNREFUSED; | |
286 | else | |
287 | errno = ETIMEDOUT; | |
92a82fe4 | 288 | else |
1ea504e8 | 289 | errno = terrno; |
616a94ba RC |
290 | return (-1); |
291 | } | |
3126f4c0 JB |
292 | |
293 | /* | |
294 | * This routine is for closing the socket if a virtual circuit is used and | |
295 | * the program wants to close it. This provides support for endhostent() | |
296 | * which expects to close the socket. | |
297 | * | |
298 | * This routine is not expected to be user visible. | |
299 | */ | |
300 | _res_close() | |
301 | { | |
302 | if (s != -1) { | |
303 | (void) close(s); | |
304 | s = -1; | |
305 | } | |
306 | } |