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 #include <stdio.h>
  42 #include <sys/types.h>
  43 #include <sys/socket.h>
  44 #include <sys/stat.h>
  45 #include <netinet/in.h>
  46 #include <arpa/nameser.h>
  47 #include <resolv.h>
  48 #include <string.h>
  49 #include <stdlib.h>
  50 #include <unistd.h>
  51 #include <errno.h>
  52 #include <netdb.h>
  53 #include "crossl.h"
  54 
  55 /*
  56  * Kludge to time out quickly if there is no /etc/resolv.conf
  57  * and a TCP connection to the local DNS server fails.
  58  *
  59  * Moved function from res_send.c to res_mkquery.c.  This
  60  * solves a long timeout problem with nslookup.
  61  *
  62  * __areweinnamed is needed because there is a possibility that the
  63  * user might do bad things to resolv.conf and cause in.named to call
  64  * _confcheck and deadlock the server.
  65  */
  66 
  67 int __areweinnamed()
  68 {
  69         return (0);
  70 }
  71 
  72 static int _confcheck()
  73 {
  74         int ns;
  75         struct stat rc_stat;
  76         struct sockaddr_in ns_sin;
  77 
  78 
  79         /* First, we check to see if /etc/resolv.conf exists.
  80          * If it doesn't, then localhost is mostlikely to be
  81          * the nameserver.
  82          */
  83         if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) {
  84 
  85                 /* Next, we check to see if _res.nsaddr is set to loopback.
  86                  * If it isn't, it has been altered by the application
  87                  * explicitly and we then want to bail with success.
  88                  */
  89                 if (__areweinnamed())
  90                         return (0);
  91                 
  92                 if (_res.nsaddr.sin_addr.S_un.S_addr == htonl(INADDR_LOOPBACK)) {
  93 
  94                         /* Lastly, we try to connect to the TCP port of the
  95                          * nameserver.  If this fails, then we know that
  96                          * DNS is misconfigured and we can quickly exit.
  97                          */
  98                         ns = socket(AF_INET, SOCK_STREAM, 0);
  99                         IN_SET_LOOPBACK_ADDR(&ns_sin);
 100                         ns_sin.sin_port = htons(NAMESERVER_PORT);
 101                         if (connect(ns, (struct sockaddr *) &ns_sin,
 102                                     sizeof ns_sin) == -1) {
 103                                 (void) close(ns);
 104                                 return(-1);
 105                         }
 106                         else {
 107                                 (void) close(ns);
 108                                 return(0);
 109                         }
 110                 }
 111         
 112                 return(0);
 113         }
 114         
 115         return (0);
 116 }
 117 
 118 /*
 119  * Form all types of queries.
 120  * Returns the size of the result or -1.
 121  */
 122 int
 123 res_mkquery(op, dname, class, type, data, datalen, newrr, buf, buflen)
 124         int op;                 /* opcode of query */
 125         char *dname;            /* domain name */
 126         int class, type;        /* class and type of query */
 127         char *data;             /* resource record data */
 128         int datalen;            /* length of data */
 129         struct rrec *newrr;     /* new rr for modify or append */
 130         char *buf;              /* buffer to put query */
 131         int buflen;             /* size of buffer */
 132 {
 133         register HEADER *hp;
 134         register u_char *cp;
 135         register int n;
 136         u_char *dnptrs[10], **dpp, **lastdnptr;
 137 
 138 #ifdef DEBUG
 139         if (_res.options & RES_DEBUG)
 140                 printf("res_mkquery(%d, %s, %d, %d)\n", op, dname, class, type);
 141 #endif /* DEBUG */
 142         
 143         /*
 144          * Check to see if we can bailout quickly.
 145          * Also rerun res_init if we failed in the past.
 146          */
 147 
 148         if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
 149                 h_errno = NO_RECOVERY;
 150                 return(-1);
 151         }
 152         
 153         if (_confcheck() == -1) {
 154                 _res.options &= ~RES_INIT;
 155                 h_errno = NO_RECOVERY;
 156                 return(-1);
 157         }
 158 
 159         /*
 160          * Initialize header fields.
 161          */
 162         if ((buf == NULL) || (buflen < sizeof (HEADER)))
 163                 return (-1);
 164 #ifdef SYSV
 165         (void) memset(buf, 0, sizeof (HEADER));
 166 #else
 167         bzero(buf, sizeof (HEADER));
 168 #endif
 169         hp = (HEADER *) buf;
 170         hp->id = htons(++_res.id);
 171         hp->opcode = op;
 172         hp->pr = (_res.options & RES_PRIMARY) != 0;
 173         hp->rd = (_res.options & RES_RECURSE) != 0;
 174         hp->rcode = NOERROR;
 175         cp = (u_char *)(buf + sizeof (HEADER));
 176         buflen -= sizeof (HEADER);
 177         dpp = dnptrs;
 178         *dpp++ = (u_char *)buf;
 179         *dpp++ = NULL;
 180         lastdnptr = dnptrs + sizeof (dnptrs) / sizeof (dnptrs[0]);
 181         /*
 182          * perform opcode specific processing
 183          */
 184         switch (op) {
 185         case QUERY:
 186                 if ((buflen -= QFIXEDSZ) < 0)
 187                         return (-1);
 188                 if ((n = dn_comp((u_char *)dname, cp, buflen,
 189                     dnptrs, lastdnptr)) < 0)
 190                         return (-1);
 191                 cp += n;
 192                 buflen -= n;
 193                 putshort(type, cp);
 194                 cp += sizeof (u_short);
 195                 putshort(class, cp);
 196                 cp += sizeof (u_short);
 197                 hp->qdcount = htons(1);
 198                 if (op == QUERY || data == NULL)
 199                         break;
 200                 /*
 201                  * Make an additional record for completion domain.
 202                  */
 203                 buflen -= RRFIXEDSZ;
 204                 if ((n = dn_comp((u_char *)data, cp, buflen,
 205                     dnptrs, lastdnptr)) < 0)
 206                         return (-1);
 207                 cp += n;
 208                 buflen -= n;
 209                 putshort(T_NULL, cp);
 210                 cp += sizeof (u_short);
 211                 putshort(class, cp);
 212                 cp += sizeof (u_short);
 213                 putlong(0, cp);
 214                 cp += sizeof (u_long);
 215                 putshort(0, cp);
 216                 cp += sizeof (u_short);
 217                 hp->arcount = htons(1);
 218                 break;
 219 
 220         case IQUERY:
 221                 /*
 222                  * Initialize answer section
 223                  */
 224                 if (buflen < 1 + RRFIXEDSZ + datalen)
 225                         return (-1);
 226                 *cp++ = '\0';   /* no domain name */
 227                 putshort(type, cp);
 228                 cp += sizeof (u_short);
 229                 putshort(class, cp);
 230                 cp += sizeof (u_short);
 231                 putlong(0, cp);
 232                 cp += sizeof (u_long);
 233                 putshort(datalen, cp);
 234                 cp += sizeof (u_short);
 235                 if (datalen) {
 236 #ifdef SYSV
 237                         (void) memcpy((void *)cp, (void *)data, datalen);
 238 #else
 239                         bcopy(data, cp, datalen);
 240 #endif
 241                         cp += datalen;
 242                 }
 243                 hp->ancount = htons(1);
 244                 break;
 245 
 246 #ifdef ALLOW_UPDATES
 247         /*
 248          * For UPDATEM/UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA
 249          * (Record to be modified is followed by its replacement in msg.)
 250          */
 251         case UPDATEM:
 252         case UPDATEMA:
 253 
 254         case UPDATED:
 255                 /*
 256                  * The res code for UPDATED and UPDATEDA is the same; user
 257                  * calls them differently: specifies data for UPDATED; server
 258                  * ignores data if specified for UPDATEDA.
 259                  */
 260         case UPDATEDA:
 261                 buflen -= RRFIXEDSZ + datalen;
 262                 if ((n = dn_comp((u_char *)dname, cp, buflen,
 263                     dnptrs, lastdnptr)) < 0)
 264                         return (-1);
 265                 cp += n;
 266                 putshort(type, cp);
 267                 cp += sizeof (u_short);
 268                 putshort(class, cp);
 269                 cp += sizeof (u_short);
 270                 putlong(0, cp);
 271                 cp += sizeof (u_long);
 272                 putshort(datalen, cp);
 273                 cp += sizeof (u_short);
 274                 if (datalen) {
 275 #ifdef SYSV
 276                         memcpy((void *)cp, (void *)data, datalen);
 277 #else
 278                         bcopy(data, cp, datalen);
 279 #endif
 280                         cp += datalen;
 281                 }
 282                 if ((op == UPDATED) || (op == UPDATEDA)) {
 283                         hp->ancount = htons(0);
 284                         break;
 285                 }
 286                 /* Else UPDATEM/UPDATEMA, so drop into code for UPDATEA */
 287 
 288         case UPDATEA:   /* Add new resource record */
 289                 buflen -= RRFIXEDSZ + datalen;
 290                 if ((n = dn_comp((u_char *)dname, cp, buflen,
 291                     dnptrs, lastdnptr)) < 0)
 292                         return (-1);
 293                 cp += n;
 294                 putshort(newrr->r_type, cp);
 295                 cp += sizeof (u_short);
 296                 putshort(newrr->r_class, cp);
 297                 cp += sizeof (u_short);
 298                 putlong(0, cp);
 299                 cp += sizeof (u_long);
 300                 putshort(newrr->r_size, cp);
 301                 cp += sizeof (u_short);
 302                 if (newrr->r_size) {
 303 #ifdef SYSV
 304                         memcpy((void *)cp, newrr->r_data, newrr->r_size);
 305 #else
 306                         bcopy(newrr->r_data, cp, newrr->r_size);
 307 #endif
 308                         cp += newrr->r_size;
 309                 }
 310                 hp->ancount = htons(0);
 311                 break;
 312 
 313 #endif /* ALLOW_UPDATES */
 314         }
 315         return ((char *)cp - buf);
 316 }