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