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