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 }