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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include        <thread.h>
  29 #include        <stdlib.h>
  30 #include        <netdb.h>
  31 #include        <strings.h>
  32 #include        <alloca.h>
  33 #include        <sys/socket.h>
  34 #include        <netinet/in.h>
  35 #include        <arpa/inet.h>
  36 #include        <arpa/nameser.h>
  37 #include        "res_update.h"
  38 #include        <stdio.h>
  39 #include        <errno.h>
  40 #include        <resolv.h>
  41 #include        <assert.h>
  42 #include        <stdarg.h>
  43 #include        <libnvpair.h>
  44 
  45 #define MAX_RETRIES     5       /* times to loop on TRY_AGAIN errors */
  46 #define LEASEMIN        3600    /* minimum lease time allowed by RFC 1531 */
  47 
  48 static boolean_t        getNS(char *, struct in_addr *);
  49 static void             cacheNS(char *, struct in_addr *, int);
  50 static boolean_t        lookupNS(char *, struct in_addr *);
  51 static boolean_t        send_update(struct hostent *, struct in_addr *);
  52 static unsigned short   parse_ushort(const char **);
  53 static unsigned int     parse_uint(const char **);
  54 static void             freeupdrecs(ns_updque);
  55 static void             freehost(struct hostent *);
  56 static boolean_t        delA(struct __res_state *, char *);
  57 static boolean_t        delPTR(struct __res_state *, char *, char *);
  58 static boolean_t        addA(struct __res_state *, char *, struct in_addr);
  59 static boolean_t        addPTR(struct __res_state *, char *, char *);
  60 static boolean_t        retry_update(struct __res_state *, ns_updrec *);
  61 
  62 extern char             *inet_ntoa_r(struct in_addr, char *);
  63 
  64 /*
  65  *      The parent (calling) thread and the child thread it spawns to do an
  66  *      update use this structure to rendezvous.  The child thread sets the
  67  *      ``done'' variable to B_TRUE when it's completed its work.  The nusers
  68  *      variable lets us arbitrate to see who has to clean up (via the
  69  *      provided childstat_cleanup() function) the dynamically-allocated
  70  *      structure - last one to wake up loses, and has to do the work.
  71  */
  72 struct  childstat {
  73         mutex_t         m;
  74         cond_t          cv;
  75         struct hostent  *hp;
  76         boolean_t       synchflag;
  77         boolean_t       done;
  78         int             ret;
  79         int             nusers;
  80 };
  81 static void     childstat_cleanup(struct childstat *);
  82 
  83 static void     update_thread(void *);
  84 
  85 /*
  86  *      The given environment variable, if present, will contain the name
  87  *      of a file (or the distinguished values "stdout" and "stderr") into
  88  *      which we should place the debugging output from this shared object.
  89  *
  90  *      The debugging output is basically free-form but uses the dprint()
  91  *      function to ensure that each message is tagged with its thread ID,
  92  *      so we have some hope of sorting out later what actually happened.
  93  */
  94 static char     env_filetoken[] = "DHCP_DNS_OUTPUT";
  95 static void     dprint(char *, ...);
  96 static FILE     *debug_fp;
  97 
  98 static boolean_t        dns_config_ok;          /* did res_ninit() work? */
  99 
 100 static nvlist_t *nvl;
 101 
 102 /* CSTYLED */
 103 #pragma init    (init)
 104 
 105 /*
 106  *      This is the shared object startup function, called once when we
 107  *      are dlopen()ed.
 108  */
 109 static void
 110 init(void)
 111 {
 112         char *cp;
 113         struct __res_state res;
 114 
 115         if (cp = getenv(env_filetoken)) {
 116                 if (strcmp(cp, "stdout") == 0)
 117                         debug_fp = stdout;
 118                 else if (strcmp(cp, "stderr") == 0)
 119                         debug_fp = stderr;
 120                 else {
 121                         debug_fp = fopen(cp, "a");
 122                 }
 123                 if (debug_fp)
 124                         (void) setvbuf(debug_fp, NULL, _IOLBF, BUFSIZ);
 125         }
 126 
 127         /*
 128          *      Use res_ninit(3RESOLV) to see whether DNS has been configured
 129          *      on the host running this code.  In practice, life must be very
 130          *      bad for res_ninit() to fail.
 131          */
 132         (void) memset(&res, 0, sizeof (res));
 133         if (res_ninit(&res) == -1) {
 134                 dprint("res_ninit() failed - dns_config_ok FALSE\n");
 135                 dns_config_ok = B_FALSE;
 136         } else {
 137                 dprint("res_ninit() succeeded\n");
 138                 dns_config_ok = B_TRUE;
 139         }
 140         res_ndestroy(&res);
 141 }
 142 
 143 /*
 144  *      This is the interface exported to the outside world.  Control over
 145  *      the hostent structure is assumed to pass to dns_puthostent();  it will
 146  *      free the associated space when done.
 147  */
 148 int
 149 dns_puthostent(struct hostent *hp, time_t timeout)
 150 {
 151         struct childstat *sp;
 152         timestruc_t t;
 153         int ret;
 154         thread_t tid;
 155 
 156 
 157         /*
 158          *      Check the consistency of the hostent structure:
 159          *      both the name and address fields should be valid,
 160          *      h_addrtype must be AF_INET, and h_length must be
 161          *      sizeof (struct in_addr);
 162          */
 163         if (hp == NULL) {
 164                 dprint("hp is NULL - return -1\n");
 165                 return (-1);
 166         }
 167         if (hp->h_addr_list == NULL) {
 168                 dprint("h_addr_list is NULL - return -1\n");
 169                 freehost(hp);
 170                 return (-1);
 171         }
 172         if (hp->h_addr_list[0] == NULL) {
 173                 dprint("h_addr_list is zero-length - return -1\n");
 174                 freehost(hp);
 175                 return (-1);
 176         }
 177         if (hp->h_name == NULL) {
 178                 dprint("h_name is NULL - return -1\n");
 179                 freehost(hp);
 180                 return (-1);
 181         }
 182         if (hp->h_name[0] == '\0') {
 183                 dprint("h_name[0] is NUL - return -1\n");
 184                 freehost(hp);
 185                 return (-1);
 186         }
 187         if (hp->h_addrtype != AF_INET) {
 188                 dprint("h_addrtype (%d) != AF_INET - return -1\n",
 189                     hp->h_addrtype);
 190                 freehost(hp);
 191                 return (-1);
 192         }
 193         if (hp->h_length != sizeof (struct in_addr)) {
 194                 dprint("h_length (%d) != sizeof (struct in_addr) - return -1\n",
 195                     hp->h_length);
 196                 freehost(hp);
 197                 return (-1);
 198         }
 199 
 200         dprint("dns_puthostent(%s, %d)\n", hp->h_name, (int)timeout);
 201 
 202         if (dns_config_ok == B_FALSE) {
 203                 dprint("dns_config_ok FALSE - return -1\n");
 204                 freehost(hp);
 205                 return (-1);
 206         }
 207 
 208         if ((sp = malloc(sizeof (struct childstat))) == NULL) {
 209                 dprint("malloc (sizeof struct childstat) failed\n");
 210                 freehost(hp);
 211                 return (-1);
 212         }
 213 
 214         /*
 215          *      From this point on, both hp and sp are cleaned up and freed via
 216          *      childstat_cleanup(), with bookkeeping done to see whether the
 217          *      parent thread or the child one should be the one in charge of
 218          *      cleaning up.
 219          */
 220         sp->hp = hp;
 221 
 222         if (timeout > 0)
 223                 sp->synchflag = B_TRUE;
 224         else
 225                 sp->synchflag = B_FALSE;
 226         sp->done = B_FALSE;
 227         sp->ret = 0;
 228         sp->nusers = 1;
 229         (void) mutex_init(&sp->m, USYNC_THREAD, 0);
 230         (void) cond_init(&sp->cv, USYNC_THREAD, 0);
 231         (void) time(&t.tv_sec);
 232         t.tv_sec += timeout;
 233         t.tv_nsec = 0;
 234 
 235         if (thr_create(NULL, NULL, (void *(*)(void *))update_thread,
 236             (void *) sp, THR_DAEMON|THR_DETACHED, &tid)) {
 237                 dprint("thr_create failed (errno %d) - return -1\n", errno);
 238                 childstat_cleanup(sp);
 239                 return (-1);
 240         }
 241         else
 242                 dprint("thread %u created\n", tid);
 243 
 244         if (!sp->done) {     /* we might already have finished */
 245                 (void) mutex_lock(&sp->m);
 246 
 247                 /* if asynchronous, and child still working, just return; */
 248                 if ((!sp->done) && (timeout == 0)) {
 249                         sp->nusers--;
 250                         (void) mutex_unlock(&sp->m);
 251                         dprint("done 0, timeout 0\n");
 252                         return (0);
 253                 }
 254 
 255                 /* otherwise, wait for child to finish or time to expire */
 256                 while (!sp->done)
 257                         if (cond_timedwait(&sp->cv, &sp->m, &t) == ETIME) {
 258                                 /*
 259                                  *      Child thread did not return before the
 260                                  *      timeout.  One might think we could
 261                                  *              assert(sp->nusers > 1);
 262                                  *      here, but we can't:  we must protect
 263                                  *      against this sequence of events:
 264                                  *              cond_timedwait() times out
 265                                  *
 266                                  *              child finishes, grabs mutex,
 267                                  *              decrements nusers, sets done,
 268                                  *          and exits.
 269                                  *
 270                                  *              cond_timedwait() reacquires the
 271                                  *              mutex and returns ETIME
 272                                  *
 273                                  *      If this happens, nusers will now be 1,
 274                                  *      even though cond_timedwait() returned
 275                                  *      ETIME.
 276                                  */
 277                                 if (sp->nusers == 1)
 278                                         /* child must have also set done */
 279                                         break;
 280                                 else
 281                                         /* child thread has not returned */
 282                                         sp->nusers--;
 283                                 (void) mutex_unlock(&sp->m);
 284                                 dprint("update for %s timed out\n", hp->h_name);
 285                                 return (0);
 286                         }
 287                 assert(sp->done);
 288                 ret = sp->ret;
 289         }
 290 
 291         childstat_cleanup(sp);
 292         return (ret);
 293 }
 294 
 295 /*
 296  *      This worker thread, spawned by dns_puthostent(), is responsible for
 297  *      seeing that the update work gets done and cleaning up afterward
 298  *      if necessary.
 299  */
 300 static void
 301 update_thread(void *arg)
 302 {
 303         char *p;
 304         int num_updated = 0;
 305         struct in_addr ia;
 306         struct hostent *hp;
 307         struct childstat *sp;
 308 
 309         dprint("update_thread running\n");
 310 
 311         sp = (struct childstat *)arg;
 312 
 313         (void) mutex_lock(&sp->m);
 314         /*
 315          *      Paranoia:  if nusers was 0 and we were asked to do a
 316          *      synchronous update, our parent must have incremented
 317          *      it, called cond_timedwait(), timed out, and decremented it,
 318          *      all before we got this far.  In this case, we do nothing
 319          *      except clean up and exit.
 320          */
 321         if ((++sp->nusers == 1) && sp->synchflag) {
 322                 childstat_cleanup(sp);
 323                 thr_exit(0);
 324         }
 325 
 326         (void) mutex_unlock(&sp->m);
 327 
 328         hp = sp->hp;
 329 
 330         /*
 331          *      h_name should be full-qualified;  find the name servers for
 332          *      its domain ...
 333          */
 334         for (p = hp->h_name; *p != NULL; p++)
 335                 if (*p == '.') {
 336                         if (getNS(++p, &ia)) {
 337                                 char ntoab[INET_ADDRSTRLEN];
 338 
 339                                 (void) inet_ntoa_r(ia, ntoab);
 340                                 dprint("update for %s goes to %s\n",
 341                                     hp->h_name, ntoab);
 342                                 /* ... and send the update to one of them. */
 343                                 if (send_update(hp, &ia)) {
 344                                         dprint("send_update succeeded\n");
 345                                         num_updated = 1;
 346                                 } else {
 347                                         dprint("send_update failed\n");
 348                                         num_updated = 0;
 349                                 }
 350                         } else {
 351                                 dprint("getNS failed\n");
 352                                 num_updated = -1;
 353                         }
 354                         break;
 355                 }
 356         dprint("update for %s returning %d\n", hp->h_name, num_updated);
 357 
 358         (void) mutex_lock(&sp->m);
 359         if (--sp->nusers == 0) {
 360                 /* parent timed out and abandoned us - our turn to clean up */
 361                 childstat_cleanup(sp);
 362         } else {
 363                 sp->done = B_TRUE;
 364                 sp->ret = num_updated;
 365                 (void) cond_signal(&sp->cv);
 366                 (void) mutex_unlock(&sp->m);
 367         }
 368 
 369         thr_exit(0);
 370 }
 371 
 372 /*
 373  *      Find a name server for the supplied domain and return its IP address.
 374  *      Sadly, in order to do this we have to parse the actual DNS reply
 375  *      packet - no functions are provided for doing this work for us.
 376  */
 377 static boolean_t
 378 getNS(char *domain, struct in_addr *iap)
 379 {
 380         HEADER *hp;
 381         union {
 382                 HEADER  h;
 383                 char    buf[NS_PACKETSZ];
 384         } abuf;
 385         int alen;
 386         int count;
 387         int retries;
 388         unsigned char   name[MAXDNAME];
 389         int qdcount, ancount, nscount, arcount;
 390         unsigned char *data;
 391         unsigned char *m_bound;
 392         int type, class, ttl, dlen;
 393         struct hostent *ep;
 394         unsigned char *NS_data;
 395         boolean_t found_NS = B_FALSE;
 396         struct __res_state res;
 397         extern struct hostent *res_gethostbyname(const char *);
 398 
 399         if (lookupNS(domain, iap)) {
 400                 dprint("getNS:  found cached IP address for domain %s\n",
 401                     domain);
 402                 return (B_TRUE);
 403         }
 404         (void) memset(&res, 0, sizeof (res));
 405         if (res_ninit(&res) == -1) {
 406                 dprint("getNS(\"%s\"):  res_ninit failed\n", domain);
 407                 return (B_FALSE);
 408         }
 409         for (retries = 0; retries < MAX_RETRIES; retries++) {
 410                 alen = res_nquery(&res, domain, C_IN, T_NS, (uchar_t *)&abuf,
 411                     sizeof (abuf));
 412 
 413                 if (alen <= 0) {
 414                         /*
 415                          * Look for indicators from libresolv:res_nsend()
 416                          * that we should retry a request.
 417                          */
 418                         if ((errno == ECONNREFUSED) ||
 419                             ((h_errno == TRY_AGAIN) && (errno == ETIMEDOUT))) {
 420                                 dprint("getNS retry:  errno %d, h_errno %d\n",
 421                                     errno, h_errno);
 422                                 continue;
 423                         } else {
 424                                 dprint("getNS(\"%s\"):  res_nquery failed "
 425                                     "(h_errno %d)\n", domain, h_errno);
 426                                 res_ndestroy(&res);
 427                                 return (B_FALSE);
 428                         }
 429                 }
 430         }
 431         if (alen <= 0) {
 432                 dprint("getNS(\"%s\"):  res_nquery failed " "(h_errno %d)\n",
 433                     domain, h_errno);
 434                 res_ndestroy(&res);
 435                 return (B_FALSE);
 436         }
 437 
 438         m_bound = ((unsigned char *)&abuf) + alen;
 439 
 440         hp = (HEADER *)&abuf;
 441         data = (unsigned char *)&hp[1];     /* a DNS paradigm - actually abuf.buf */
 442 
 443         qdcount = ntohs(hp->qdcount);
 444         ancount = ntohs(hp->ancount);
 445         nscount = ntohs(hp->nscount);
 446         arcount = ntohs(hp->arcount);
 447 
 448         dprint("getNS(\"%s\"):\n", domain);
 449         dprint("\tqdcount %d\n", qdcount);
 450         dprint("\tancount %d\n", ancount);
 451         dprint("\tnscount %d\n", nscount);
 452         dprint("\tarcount %d\n", arcount);
 453 
 454         while (--qdcount >= 0) {
 455                 dlen = dn_skipname(data, m_bound);
 456                 if (dlen < 0) {
 457                         dprint("dn_skipname returned < 0\n");
 458                         res_ndestroy(&res);
 459                         return (B_FALSE);
 460                 }
 461                 data += dlen + QFIXEDSZ;
 462         }
 463 
 464         count = ancount;
 465         count += arcount;
 466         while (--count >= 0 && data < m_bound) {
 467                 if ((dlen = dn_expand((unsigned char *) &abuf, m_bound,
 468                                 data, (char *)name, sizeof (name))) < 0) {
 469                         dprint("dn_expand() dom failed\n");
 470                         res_ndestroy(&res);
 471                         return (B_FALSE);
 472                 }
 473                 data += dlen;
 474                 type = parse_ushort((const char **)&data);
 475                 class = parse_ushort((const char **)&data);
 476                 ttl = parse_uint((const char **)&data);
 477                 dlen = parse_ushort((const char **)&data);
 478 
 479                 switch (type) {
 480                         case T_NS:
 481                                 dprint("\ttype T_NS\n");
 482                                 break;
 483                         case T_CNAME:
 484                                 dprint("\ttype T_CNAME\n");
 485                                 break;
 486                         case T_A:
 487                                 dprint("\ttype T_A\n");
 488                                 break;
 489                         case T_SOA:
 490                                 dprint("\ttype T_SOA\n");
 491                                 break;
 492                         case T_MX:
 493                                 dprint("\ttype T_MX\n");
 494                                 break;
 495                         case T_TXT:
 496                                 dprint("\ttype T_TXT\n");
 497                                 break;
 498                         default:
 499                                 dprint("\ttype %d\n", type);
 500                         }
 501                         if (class == C_IN)
 502                                 dprint("\tclass C_IN\n");
 503                         else
 504                                 dprint("\tclass %d\n", class);
 505                         dprint("\tttl %d secs\n", ttl);
 506                         dprint("\tlen %d bytes\n", dlen);
 507 
 508                 switch (type) {
 509                 case T_A:
 510                         (void) memcpy(iap, data, sizeof (struct in_addr));
 511                         cacheNS(domain, iap, ttl);
 512                         res_ndestroy(&res);
 513                         return (B_TRUE);
 514 
 515                 case T_NS:
 516                         found_NS = B_TRUE;
 517                         NS_data = data; /* we may need this name below */
 518                         if (dn_expand((unsigned char *) &abuf, m_bound, data,
 519                             (char *)name, sizeof (name)) < 0) {
 520                                 dprint("\tdn_expand() T_NS failed\n");
 521                                 res_ndestroy(&res);
 522                                 return (B_FALSE);
 523                         }
 524                         dprint("\tname %s\n", name);
 525                         break;
 526                 }
 527                 data += dlen;
 528         }
 529         dprint("getNS:  fell through res_nquery results - no A records\n");
 530 
 531         /*
 532          *      The reply contained NS records, but no A records.  Use
 533          *      res_gethostbyname() to get the name server's address
 534          *      via DNS.
 535          */
 536         if (found_NS) {
 537                 if (dn_expand((unsigned char *) &abuf, m_bound, NS_data,
 538                     (char *)name, sizeof (name)) < 0) {
 539                         dprint("\tdn_expand() T_NS failed\n");
 540                         res_ndestroy(&res);
 541                         return (B_FALSE);
 542                 }
 543 
 544                 if (ep = res_gethostbyname((const char *)name)) {
 545                         (void) memcpy(iap, ep->h_addr, sizeof (struct in_addr));
 546                         cacheNS(domain, iap, ttl);
 547                         res_ndestroy(&res);
 548                         return (B_TRUE);
 549                 } else
 550                         dprint("getNS:  res_gethostbyname(%s) failed\n", name);
 551         } else {
 552                 dprint("getNS:  reply contained no NS records\n");
 553         }
 554 
 555         res_ndestroy(&res);
 556         return (B_FALSE);
 557 }
 558 
 559 /*
 560  *      Cache the <domain, IP address> tuple (which is assumed to not already
 561  *      be cached) for ttl seconds.
 562  */
 563 static void
 564 cacheNS(char *domain, struct in_addr *iap, int ttl)
 565 {
 566         if (ttl > 0) {
 567                 time_t now;
 568 
 569                 if (nvl == NULL &&
 570                     nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) {
 571                         dprint("cacheNS:  nvlist_alloc failed\n");
 572                         return;
 573                 }
 574 
 575                 (void) time(&now);
 576                 now += ttl;
 577                 if ((nvlist_add_int32(nvl, domain, iap->s_addr) != 0) ||
 578                     (nvlist_add_byte_array(nvl, domain, (uchar_t *)&now,
 579                         sizeof (now)) != 0)) {
 580                         dprint("cacheNS:  nvlist_add failed\n");
 581                         nvlist_free(nvl);
 582                         nvl = NULL;
 583                 }
 584         } else
 585                 dprint("cacheNS:  ttl 0 - nothing to cache\n");
 586 }
 587 
 588 /*
 589  *      See whether the <domain, IP address> tuple has been cached.
 590  */
 591 static boolean_t
 592 lookupNS(char *domain, struct in_addr *iap)
 593 {
 594         int32_t i;
 595 
 596         if (nvlist_lookup_int32(nvl, domain, &i) == 0) {
 597                 time_t *ttlptr;
 598                 uint_t nelem = sizeof (*ttlptr);
 599 
 600                 if (nvlist_lookup_byte_array(nvl, domain, (uchar_t **)&ttlptr,
 601                     &nelem) != 0)
 602                         return (B_FALSE);
 603 
 604                 if (*ttlptr >= time(0)) {    /* still OK to use */
 605                         iap->s_addr = i;
 606                         return (B_TRUE);
 607                 } else {
 608                         (void) nvlist_remove_all(nvl, domain);
 609                 }
 610         }
 611 
 612         return (B_FALSE);
 613 }
 614 
 615 /*
 616  *      Do the work of updating DNS to have the <hp->h_name <-> hp->h_addr>
 617  *      pairing.
 618  */
 619 static boolean_t
 620 send_update(struct hostent *hp, struct in_addr *to_server)
 621 {
 622         char *forfqhost;
 623         struct __res_state res;
 624         struct in_addr netaddr;
 625         char revnamebuf[MAXDNAME];
 626 
 627         (void) memset(&res, 0, sizeof (res));
 628         if (res_ninit(&res) == -1) {
 629                 dprint("send_updated res_ninit failed!");
 630                 return (B_FALSE);
 631         }
 632         res.nscount = 1;
 633         res.nsaddr.sin_family = AF_INET;
 634         res.nsaddr.sin_port = htons(NAMESERVER_PORT);
 635         res.nsaddr.sin_addr.s_addr = to_server->s_addr;
 636 
 637         /* If debugging output desired, then ask resolver to do it, too */
 638         if (debug_fp != NULL)
 639                 res.options |= RES_DEBUG;
 640 
 641         if (strchr(hp->h_name, '.') == NULL) {
 642                 dprint("send_update handed non-FQDN:  %s\n", hp->h_name);
 643                 res_ndestroy(&res);
 644                 return (B_FALSE);
 645         }
 646         forfqhost = hp->h_name;
 647 
 648         /* Construct the fully-qualified name for PTR record updates */
 649         /* LINTED - alignment */
 650         netaddr.s_addr = ((struct in_addr *)hp->h_addr)->s_addr;
 651         (void) snprintf(revnamebuf, sizeof (revnamebuf),
 652             "%u.%u.%u.%u.in-addr.ARPA",
 653             netaddr.S_un.S_un_b.s_b4, netaddr.S_un.S_un_b.s_b3,
 654             netaddr.S_un.S_un_b.s_b2, netaddr.S_un.S_un_b.s_b1);
 655         dprint("send_update %s:  revname %s\n", hp->h_name, revnamebuf);
 656 
 657         /*
 658          *      The steps in doing an update:
 659          *              -  delete any A records
 660          *              -  delete any PTR records
 661          *              -  add an A record
 662          *              -  add a PTR record
 663          */
 664 
 665         if (!delA(&res, forfqhost) ||
 666             !delPTR(&res, forfqhost, revnamebuf) ||
 667             !addA(&res, forfqhost, netaddr) ||
 668             !addPTR(&res, forfqhost, revnamebuf)) {
 669                 res_ndestroy(&res);
 670                 return (B_FALSE);
 671         }
 672         res_ndestroy(&res);
 673         return (B_TRUE);
 674 }
 675 
 676 /* delete A records for this fully-qualified name */
 677 static boolean_t
 678 delA(struct __res_state *resp, char *fqdn)
 679 {
 680         ns_updque q;
 681         ns_updrec *updreqp;
 682 
 683         INIT_LIST(q);
 684         updreqp = res_mkupdrec(S_UPDATE, fqdn, C_IN, T_A, 0);
 685         if (updreqp == NULL) {
 686                 dprint("res_mkupdrec (del A) failed\n");
 687                 return (B_FALSE);
 688         }
 689         updreqp->r_opcode = DELETE;
 690         updreqp->r_data = NULL;
 691         updreqp->r_size = 0;
 692         APPEND(q, updreqp, r_link);
 693         if (retry_update(resp, HEAD(q)) != 1) {
 694                 dprint("res_nupdate (del A) failed - errno %d, h_errno %d\n",
 695                     errno, h_errno);
 696                 freeupdrecs(q);
 697                 return (B_FALSE);
 698         }
 699         freeupdrecs(q);
 700         return (B_TRUE);
 701 }
 702 
 703 /* delete PTR records for this address */
 704 static boolean_t
 705 delPTR(struct __res_state *resp, char *fqdn, char *revname)
 706 {
 707         ns_updque q;
 708         ns_updrec *updreqp;
 709 
 710         INIT_LIST(q);
 711         updreqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, 0);
 712         if (updreqp == NULL) {
 713                 dprint("res_mkupdrec (del PTR) failed\n");
 714                 return (B_FALSE);
 715         }
 716         updreqp->r_opcode = DELETE;
 717         updreqp->r_data = (unsigned char *)fqdn;
 718         updreqp->r_size = strlen(fqdn);
 719         APPEND(q, updreqp, r_link);
 720         if (retry_update(resp, HEAD(q)) != 1) {
 721                 dprint("res_nupdate (del PTR) failed - errno %d, h_errno %d\n",
 722                     errno, h_errno);
 723                 freeupdrecs(q);
 724                 return (B_FALSE);
 725         }
 726         freeupdrecs(q);
 727         return (B_TRUE);
 728 }
 729 
 730 /* add an A record for this fqdn <-> addr pair */
 731 static boolean_t
 732 addA(struct __res_state *resp, char *fqdn, struct in_addr na)
 733 {
 734         ns_updque q;
 735         ns_updrec *prereqp, *updreqp;
 736         int ttl = LEASEMIN;
 737         char ntoab[INET_ADDRSTRLEN];
 738 
 739         INIT_LIST(q);
 740         prereqp = res_mkupdrec(S_PREREQ, fqdn, C_IN, T_A, 0);
 741         if (prereqp == NULL) {
 742                 dprint("res_mkupdrec (add A PREREQ) failed\n");
 743                 return (B_FALSE);
 744         }
 745         prereqp->r_opcode = NXRRSET;
 746         prereqp->r_data = NULL;
 747         prereqp->r_size = 0;
 748         APPEND(q, prereqp, r_link);
 749         updreqp = res_mkupdrec(S_UPDATE, fqdn, C_IN, T_A, ttl);
 750         if (updreqp == NULL) {
 751                 dprint("res_mkupdrec (add A UPDATE) failed\n");
 752                 freeupdrecs(q);
 753                 return (B_FALSE);
 754         }
 755 
 756         (void) inet_ntoa_r(na, ntoab);
 757         updreqp->r_opcode = ADD;
 758         updreqp->r_data = (unsigned char *)ntoab;
 759         updreqp->r_size = strlen(ntoab);
 760         APPEND(q, updreqp, r_link);
 761         if (retry_update(resp, HEAD(q)) != 1) {
 762                 dprint("res_nupdate (ADD A) failed - errno %d, h_errno %d\n",
 763                     errno, h_errno);
 764                 freeupdrecs(q);
 765                 return (B_FALSE);
 766         }
 767         freeupdrecs(q);
 768         return (B_TRUE);
 769 }
 770 
 771 /* add a PTR record for this fqdn <-> address pair */
 772 static boolean_t
 773 addPTR(struct __res_state *resp, char *fqdn, char *revname)
 774 {
 775         ns_updque q;
 776         ns_updrec *prereqp, *updreqp;
 777         int ttl = LEASEMIN;
 778 
 779         INIT_LIST(q);
 780         prereqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, 0);
 781         if (prereqp == NULL) {
 782                 dprint("res_mkupdrec (add PTR DELETE) failed\n");
 783                 return (B_FALSE);
 784         }
 785         prereqp->r_opcode = DELETE;
 786         prereqp->r_data = NULL;
 787         prereqp->r_size = 0;
 788         APPEND(q, prereqp, r_link);
 789         updreqp = res_mkupdrec(S_UPDATE, revname, C_IN, T_PTR, ttl);
 790         if (updreqp == NULL) {
 791                 dprint("res_mkupdrec (add PTR ADD) failed\n");
 792                 freeupdrecs(q);
 793                 return (B_FALSE);
 794         }
 795         updreqp->r_opcode = ADD;
 796         updreqp->r_data = (unsigned char *)fqdn;
 797         updreqp->r_size = strlen(fqdn);
 798         APPEND(q, updreqp, r_link);
 799         if (retry_update(resp, HEAD(q)) != 1) {
 800                 dprint("res_nupdate (ADD PTR) failed - errno %d, h_errno %d\n",
 801                     errno, h_errno);
 802                 freeupdrecs(q);
 803                 return (B_FALSE);
 804         }
 805         freeupdrecs(q);
 806 
 807         return (B_TRUE);
 808 }
 809 
 810 /* retry an update request when appropriate */
 811 static boolean_t
 812 retry_update(struct __res_state *resp, ns_updrec *h)
 813 {
 814         int retries;
 815 
 816         for (retries = 0; retries < MAX_RETRIES; retries++)
 817                 if (res_nupdate(resp, h, NULL) == 1) {
 818                         return (B_TRUE);
 819                 } else {
 820                         /*
 821                          * Look for indicators from libresolv:res_nsend()
 822                          * that we should retry a request.
 823                          */
 824                         if ((errno == ECONNREFUSED) ||
 825                             ((h_errno == TRY_AGAIN) && (errno == ETIMEDOUT))) {
 826                                 dprint("retry_update - errno %d, h_errno %d\n",
 827                                     errno, h_errno);
 828                                 continue;
 829                         } else
 830                                 return (B_FALSE);
 831                 }
 832 
 833         return (B_FALSE);
 834 }
 835 
 836 static void
 837 freeupdrecs(ns_updque q)
 838 {
 839         while (!EMPTY(q)) {
 840                 ns_updrec *tmp;
 841 
 842                 tmp = HEAD(q);
 843                 UNLINK(q, tmp, r_link);
 844                 res_freeupdrec(tmp);
 845         }
 846 }
 847 
 848 /*
 849  *      Parse a 16-bit quantity from a DNS reply packet.
 850  */
 851 static unsigned short
 852 parse_ushort(const char **pp)
 853 {
 854         const uchar_t   *p = (const uchar_t *)*pp;
 855         unsigned short  val;
 856 
 857         val = (p[0] << 8) | p[1];
 858         *pp += 2;
 859         return (val);
 860 }
 861 
 862 
 863 /*
 864  *      Parse a 32-bit quantity from a DNS reply packet.
 865  */
 866 static unsigned int
 867 parse_uint(const char **pp)
 868 {
 869         const uchar_t   *p = (const uchar_t *)*pp;
 870         unsigned int   val;
 871 
 872         val =  ((uint_t)p[0] << 24) | ((uint_t)p[1] << 16) |
 873             ((uint_t)p[2] << 8) | (uint_t)p[3];
 874         *pp += 4;
 875         return (val);
 876 }
 877 
 878 /*
 879  *      Clean up a childstat structure's synchronization variables and free
 880  *      the allocated memory.
 881  */
 882 static void
 883 childstat_cleanup(struct childstat *sp)
 884 {
 885         (void) cond_destroy(&sp->cv);
 886         (void) mutex_destroy(&sp->m);
 887         freehost(sp->hp);
 888         free(sp);
 889 }
 890 
 891 /*
 892  *      Format and print a debug message, prepending the thread ID of the
 893  *      thread logging the message.
 894  */
 895 /* PRINTFLIKE1 */
 896 static void
 897 dprint(char *format, ...)
 898 {
 899         va_list ap;
 900 
 901         va_start(ap, format);
 902         if (debug_fp) {
 903                 (void) fprintf(debug_fp, "%u:  ", thr_self());
 904                 (void) vfprintf(debug_fp, format, ap);
 905                 va_end(ap);
 906         }
 907 }
 908 
 909 static void
 910 freehost(struct hostent *hp)
 911 {
 912         free(hp->h_addr);
 913         free(hp->h_addr_list);
 914         free(hp->h_name);
 915         free(hp);
 916 }