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 }