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