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