1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * University Copyright- Copyright (c) 1982, 1986, 1988 32 * The Regents of the University of California 33 * All Rights Reserved 34 * 35 * University Acknowledgment- Portions of this document are derived from 36 * software developed by the University of California, Berkeley, and its 37 * contributors. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 /* 43 * Send query to name server and wait for reply. 44 */ 45 46 #include <sys/param.h> 47 #include <sys/time.h> 48 #include <sys/socket.h> 49 #include <sys/uio.h> 50 #include <sys/stat.h> 51 #include <netinet/in.h> 52 #include <stdio.h> 53 #include <errno.h> 54 #include <arpa/nameser.h> 55 #include <resolv.h> 56 57 58 static int s = -1; /* socket used for communications */ 59 static struct sockaddr no_addr; 60 61 62 #ifndef FD_SET 63 #define NFDBITS 32 64 #define FD_SETSIZE 32 65 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) 66 #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) 67 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) 68 #ifdef SYSV 69 #define FD_ZERO(p) memset((void *)(p), 0, sizeof (*(p))) 70 #else 71 #define FD_ZERO(p) bzero((char *)(p), sizeof (*(p))) 72 #endif 73 #endif 74 75 /* 76 * 1247019: Kludge to time out quickly if there is no /etc/resolv.conf 77 * and a TCP connection to the local DNS server fails. 78 */ 79 80 static int _confcheck() 81 { 82 int ns; 83 struct stat rc_stat; 84 struct sockaddr_in ns_sin; 85 86 87 /* First, we check to see if /etc/resolv.conf exists. 88 * If it doesn't, then localhost is mostlikely to be 89 * the nameserver. 90 */ 91 if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) { 92 93 /* Next, we check to see if _res.nsaddr is set to loopback. 94 * If it isn't, it has been altered by the application 95 * explicitly and we then want to bail with success. 96 */ 97 if (_res.nsaddr.sin_addr.S_un.S_addr == htonl(INADDR_LOOPBACK)) { 98 99 /* Lastly, we try to connect to the TCP port of the 100 * nameserver. If this fails, then we know that 101 * DNS is misconfigured and we can quickly exit. 102 */ 103 ns = socket(AF_INET, SOCK_STREAM, 0); 104 IN_SET_LOOPBACK_ADDR(&ns_sin); 105 ns_sin.sin_port = htons(NAMESERVER_PORT); 106 if (connect(ns, (struct sockaddr *) &ns_sin, 107 sizeof ns_sin) == -1) { 108 close(ns); 109 return(-1); 110 } 111 else { 112 close(ns); 113 return(0); 114 } 115 } 116 117 return(0); 118 } 119 120 return (0); 121 } 122 123 int 124 res_send(buf, buflen, answer, anslen) 125 char *buf; 126 int buflen; 127 char *answer; 128 int anslen; 129 { 130 register int n; 131 int try, v_circuit, resplen, ns; 132 int gotsomewhere = 0, connected = 0; 133 int connreset = 0; 134 u_short id, len; 135 char *cp; 136 fd_set dsmask; 137 struct timeval timeout; 138 HEADER *hp = (HEADER *) buf; 139 HEADER *anhp = (HEADER *) answer; 140 struct iovec iov[2]; 141 int terrno = ETIMEDOUT; 142 char junk[512]; 143 144 #ifdef DEBUG 145 if (_res.options & RES_DEBUG) { 146 printf("res_send()\n"); 147 p_query(buf); 148 } 149 #endif 150 if (!(_res.options & RES_INIT)) 151 if (res_init() == -1) { 152 return (-1); 153 } 154 155 /* 1247019: Check to see if we can bailout quickly. */ 156 if (_confcheck() == -1) 157 return(-1); 158 159 v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; 160 id = hp->id; 161 /* 162 * Send request, RETRY times, or until successful 163 */ 164 for (try = 0; try < _res.retry; try++) { 165 for (ns = 0; ns < _res.nscount; ns++) { 166 #ifdef DEBUG 167 if (_res.options & RES_DEBUG) 168 printf("Querying server (# %d) address = %s\n", 169 ns+1, inet_ntoa(_res.nsaddr_list[ns].sin_addr)); 170 #endif 171 usevc: 172 if (v_circuit) { 173 int truncated = 0; 174 175 /* 176 * Use virtual circuit; 177 * at most one attempt per server. 178 */ 179 try = _res.retry; 180 if (s < 0) { 181 s = _socket(AF_INET, SOCK_STREAM, 0); 182 if (s < 0) { 183 terrno = errno; 184 #ifdef DEBUG 185 if (_res.options & RES_DEBUG) { 186 perror("socket (vc) failed"); 187 } 188 #endif 189 continue; 190 } 191 if (connect(s, (struct sockaddr *) &_res.nsaddr_list[ns], 192 sizeof (struct sockaddr)) < 0) { 193 terrno = errno; 194 #ifdef DEBUG 195 if (_res.options & RES_DEBUG) { 196 perror("connect failed"); 197 } 198 #endif 199 (void) close(s); 200 s = -1; 201 continue; 202 } 203 } 204 /* 205 * Send length & message 206 */ 207 len = htons((u_short)buflen); 208 iov[0].iov_base = (caddr_t)&len; 209 iov[0].iov_len = sizeof (len); 210 iov[1].iov_base = buf; 211 iov[1].iov_len = buflen; 212 if (writev(s, iov, 2) != sizeof (len) + 213 buflen) { 214 terrno = errno; 215 #ifdef DEBUG 216 if (_res.options & RES_DEBUG) 217 perror("write failed"); 218 #endif 219 (void) close(s); 220 s = -1; 221 continue; 222 } 223 /* 224 * Receive length & response 225 */ 226 cp = answer; 227 len = sizeof (short); 228 while (len != 0 && (n = read 229 (s, (char *)cp, (int)len)) > 0) { 230 cp += n; 231 len -= n; 232 } 233 if (n <= 0) { 234 terrno = errno; 235 #ifdef DEBUG 236 if (_res.options & RES_DEBUG) 237 perror("read failed"); 238 #endif 239 (void) close(s); 240 s = -1; 241 /* 242 * A long running process might get its TCP 243 * connection reset if the remote server was 244 * restarted. Requery the server instead of 245 * trying a new one. When there is only one 246 * server, this means that a query might work 247 * instead of failing. We only allow one reset 248 * per query to prevent looping. 249 */ 250 if (terrno == ECONNRESET && 251 !connreset) { 252 connreset = 1; 253 ns--; 254 } 255 continue; 256 } 257 cp = answer; 258 if ((resplen = ntohs(*(u_short *)cp)) > 259 anslen) { 260 #ifdef DEBUG 261 if (_res.options & RES_DEBUG) 262 fprintf(stderr, 263 "response truncated\n"); 264 #endif 265 len = anslen; 266 truncated = 1; 267 } else 268 len = resplen; 269 while (len != 0 && 270 (n = read(s, (char *)cp, 271 (int)len)) > 0) { 272 cp += n; 273 len -= n; 274 } 275 if (n <= 0) { 276 terrno = errno; 277 #ifdef DEBUG 278 if (_res.options & RES_DEBUG) 279 perror("read failed"); 280 #endif 281 (void) close(s); 282 s = -1; 283 continue; 284 } 285 if (truncated) { 286 /* 287 * Flush rest of answer 288 * so connection stays in synch. 289 */ 290 anhp->tc = 1; 291 len = resplen - anslen; 292 /* 293 * set the value of resplen to anslen, 294 * this is done because the caller 295 * assumes resplen contains the size of 296 * message read into the "answer" buffer 297 * passed in. 298 */ 299 resplen = anslen; 300 301 while (len != 0) { 302 n = (len > sizeof (junk) ? 303 sizeof (junk) : len); 304 if ((n = read(s, junk, n)) > 0) 305 len -= n; 306 else 307 break; 308 } 309 } 310 } else { 311 /* 312 * Use datagrams. 313 */ 314 if (s < 0) { 315 s = _socket(AF_INET, SOCK_DGRAM, 0); 316 if (s < 0) { 317 terrno = errno; 318 #ifdef DEBUG 319 if (_res.options & RES_DEBUG) { 320 perror("socket (dg) failed"); 321 } 322 #endif 323 continue; 324 } 325 } 326 #if BSD >= 43 327 /* 328 * I'm tired of answering this question, so: 329 * On a 4.3BSD+ machine (client and server, 330 * actually), sending to a nameserver datagram 331 * port with no nameserver will cause an 332 * ICMP port unreachable message to be returned. 333 * If our datagram socket is "connected" to the 334 * server, we get an ECONNREFUSED error on the next 335 * socket operation, and select returns if the 336 * error message is received. We can thus detect 337 * the absence of a nameserver without timing out. 338 * If we have sent queries to at least two servers, 339 * however, we don't want to remain connected, 340 * as we wish to receive answers from the first 341 * server to respond. 342 */ 343 if (_res.nscount == 1 || 344 (try == 0 && ns == 0)) { 345 /* 346 * Don't use connect if we might 347 * still receive a response 348 * from another server. 349 */ 350 if (connected == 0) { 351 if (connect(s, 352 (struct sockaddr *) &_res.nsaddr_list[ns], 353 sizeof (struct sockaddr)) < 0) { 354 #ifdef DEBUG 355 if (_res.options & 356 RES_DEBUG) { 357 perror("connect"); 358 } 359 #endif 360 continue; 361 } 362 connected = 1; 363 } 364 if (send(s, buf, buflen, 0) != buflen) { 365 #ifdef DEBUG 366 if (_res.options & RES_DEBUG) 367 perror("send"); 368 #endif 369 continue; 370 } 371 } else { 372 /* 373 * Disconnect if we want to listen for 374 * responses from more than one server. 375 */ 376 if (connected) { 377 (void) connect(s, &no_addr, 378 sizeof (no_addr)); 379 connected = 0; 380 } 381 #endif /* BSD */ 382 if (sendto(s, buf, buflen, 0, 383 (struct sockaddr *) &_res.nsaddr_list[ns], 384 sizeof (struct sockaddr)) != buflen) { 385 #ifdef DEBUG 386 if (_res.options & RES_DEBUG) 387 perror("sendto"); 388 #endif 389 continue; 390 } 391 #if BSD >= 43 392 } 393 #endif 394 395 /* 396 * Wait for reply 397 */ 398 timeout.tv_sec = (_res.retrans << try); 399 if (try > 0) 400 timeout.tv_sec /= _res.nscount; 401 if (timeout.tv_sec <= 0) 402 timeout.tv_sec = 1; 403 timeout.tv_usec = 0; 404 wait: 405 FD_ZERO(&dsmask); 406 FD_SET(s, &dsmask); 407 n = select(s+1, &dsmask, (fd_set *)NULL, 408 (fd_set *)NULL, &timeout); 409 if (n < 0) { 410 #ifdef DEBUG 411 if (_res.options & RES_DEBUG) 412 perror("select"); 413 #endif 414 continue; 415 } 416 if (n == 0) { 417 /* 418 * timeout 419 */ 420 #ifdef DEBUG 421 if (_res.options & RES_DEBUG) 422 printf("timeout\n"); 423 #endif 424 #if BSD >= 43 425 gotsomewhere = 1; 426 #endif 427 continue; 428 } 429 if ((resplen = recv(s, answer, anslen, 0)) 430 <= 0) { 431 #ifdef DEBUG 432 if (_res.options & RES_DEBUG) 433 perror("recvfrom"); 434 #endif 435 continue; 436 } 437 gotsomewhere = 1; 438 if (id != anhp->id) { 439 /* 440 * response from old query, ignore it 441 */ 442 #ifdef DEBUG 443 if (_res.options & RES_DEBUG) { 444 printf("old answer:\n"); 445 p_query(answer); 446 } 447 #endif 448 goto wait; 449 } 450 if (!(_res.options & RES_IGNTC) && anhp->tc) { 451 /* 452 * get rest of answer; 453 * use TCP with same server. 454 */ 455 #ifdef DEBUG 456 if (_res.options & RES_DEBUG) 457 printf("truncated answer\n"); 458 #endif 459 (void) close(s); 460 s = -1; 461 v_circuit = 1; 462 goto usevc; 463 } 464 } 465 #ifdef DEBUG 466 if (_res.options & RES_DEBUG) { 467 printf("got answer:\n"); 468 p_query(answer); 469 } 470 #endif 471 /* 472 * If using virtual circuits, we assume that the first server 473 * is preferred * over the rest (i.e. it is on the local 474 * machine) and only keep that one open. 475 * If we have temporarily opened a virtual circuit, 476 * or if we haven't been asked to keep a socket open, 477 * close the socket. 478 */ 479 if ((v_circuit && 480 ((_res.options & RES_USEVC) == 0 || ns != 0)) || 481 (_res.options & RES_STAYOPEN) == 0) { 482 (void) close(s); 483 s = -1; 484 } 485 return (resplen); 486 } 487 } 488 if (s >= 0) { 489 (void) close(s); 490 s = -1; 491 } 492 if (v_circuit == 0) 493 if (gotsomewhere == 0) 494 errno = ECONNREFUSED; /* no nameservers found */ 495 else 496 errno = ETIMEDOUT; /* no answer obtained */ 497 else 498 errno = terrno; 499 return (-1); 500 } 501 502 /* 503 * This routine is for closing the socket if a virtual circuit is used and 504 * the program wants to close it. This provides support for endhostent() 505 * which expects to close the socket. 506 * 507 * This routine is not expected to be user visible. 508 */ 509 void 510 _res_close() 511 { 512 if (s != -1) { 513 (void) close(s); 514 s = -1; 515 } 516 }