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 (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <stdio.h>
  26 #include <stdlib.h>
  27 #include <alloca.h>
  28 #include <stdarg.h>
  29 #include <sys/types.h>
  30 #include <sys/sysmacros.h>
  31 #include <assert.h>
  32 #include <sys/socket.h>
  33 #include <sys/byteorder.h>
  34 #include <string.h>
  35 #include <time.h>
  36 #include <sys/socket.h>
  37 #include <syslog.h>
  38 #include <sys/errno.h>
  39 #include <net/if.h>
  40 #include <netinet/in.h>
  41 #include <arpa/inet.h>
  42 #include <netdb.h>
  43 #include <netinet/dhcp.h>
  44 #include <dhcp_symbol.h>
  45 #include <nss_dbdefs.h>
  46 #include <dlfcn.h>
  47 #include "dhcpd.h"
  48 #include "per_dnet.h"
  49 #include "interfaces.h"
  50 #include <locale.h>
  51 #include <resolv.h>
  52 
  53 static void dhcp_offer(dsvc_clnt_t *, PKT_LIST *);
  54 static void dhcp_req_ack(dsvc_clnt_t *, PKT_LIST *);
  55 static void dhcp_dec_rel(dsvc_clnt_t *, PKT_LIST *, int);
  56 static void dhcp_inform(dsvc_clnt_t *, PKT_LIST *);
  57 static PKT *gen_reply_pkt(dsvc_clnt_t *, PKT_LIST *, int, uint_t *,
  58     uchar_t **, struct in_addr *);
  59 static void set_lease_option(ENCODE **, lease_t);
  60 static int config_lease(PKT_LIST *, dn_rec_t *, ENCODE **, lease_t, boolean_t);
  61 static int is_option_requested(PKT_LIST *, ushort_t);
  62 static void add_request_list(IF *, PKT_LIST *, ENCODE **, struct in_addr *);
  63 static char *disp_clnt_msg(PKT_LIST *, char *, int);
  64 static void add_dnet_cache(dsvc_dnet_t *, dn_rec_list_t *);
  65 static void purge_dnet_cache(dsvc_dnet_t *, dn_rec_t *);
  66 
  67 static boolean_t addr_avail(dsvc_dnet_t *, dsvc_clnt_t *, dn_rec_list_t **,
  68     struct in_addr, boolean_t);
  69 static boolean_t name_avail(char *, dsvc_clnt_t *, PKT_LIST *,
  70     dn_rec_list_t **, ENCODE *, struct in_addr **);
  71 static boolean_t entry_available(dsvc_clnt_t *, dn_rec_t *);
  72 static boolean_t do_nsupdate(struct in_addr, ENCODE *, PKT_LIST *);
  73 
  74 extern int dns_puthostent(struct hostent *, time_t);
  75 
  76 /*
  77  * Offer cache.
  78  *
  79  * The DHCP server maintains a cache of DHCP OFFERs it has extended to DHCP
  80  * clients. It does so because:
  81  *      a) Subsequent requests get the same answer, and the same IP address
  82  *         isn't offered to a different client.
  83  *
  84  *      b) No ICMP validation is required the second time through, nor is a
  85  *         database lookup required.
  86  *
  87  *      c) If the client accepts the OFFER and sends a REQUEST, we can simply
  88  *         lookup the record by client IP address, the one field guaranteed to
  89  *         be unique within the dhcp network table.
  90  *
  91  * We don't explicitly delete entries from the offer cache. We let them time
  92  * out on their own. This is done to ensure the server responds correctly when
  93  * many pending client requests are queued (duplicates). We don't want to ICMP
  94  * validate an IP address we just allocated.
  95  *
  96  * The offer cache (and any database records cached in select_offer()) will
  97  * diverge from the database for the length of the D_OFFER lifetime.
  98  * SIGHUP flushes the offer cache, allowing management tools to inform the
  99  * server of changes in a timely manner.
 100  */
 101 
 102 /*
 103  * Dispatch the DHCP packet based on its type.
 104  */
 105 void
 106 dhcp(dsvc_clnt_t *pcd, PKT_LIST *plp)
 107 {
 108         if (plp->opts[CD_DHCP_TYPE]->len != 1) {
 109                 dhcpmsg(LOG_ERR,
 110                     "Garbled DHCP Message type option from client: %s\n",
 111                     pcd->cidbuf);
 112                 return;
 113         }
 114 
 115         pcd->state = *plp->opts[CD_DHCP_TYPE]->value;
 116         switch (pcd->state) {
 117         case DISCOVER:
 118 #ifdef  DEBUG
 119                 dhcpmsg(LOG_DEBUG, "dhcp() - processing OFFER...\n");
 120 #endif  /* DEBUG */
 121                 dhcp_offer(pcd, plp);
 122 #ifdef  DEBUG
 123                 dhcpmsg(LOG_DEBUG, "dhcp() - processed OFFER.\n");
 124 #endif  /* DEBUG */
 125                 break;
 126         case REQUEST:
 127 #ifdef  DEBUG
 128                 dhcpmsg(LOG_DEBUG, "dhcp() - processing REQUEST...\n");
 129 #endif  /* DEBUG */
 130                 dhcp_req_ack(pcd, plp);
 131 #ifdef  DEBUG
 132                 dhcpmsg(LOG_DEBUG, "dhcp() - processed REQUEST.\n");
 133 #endif  /* DEBUG */
 134                 break;
 135         case DECLINE:
 136 #ifdef  DEBUG
 137                 dhcpmsg(LOG_DEBUG, "dhcp() - processing DECLINE...\n");
 138 #endif  /* DEBUG */
 139                 dhcp_dec_rel(pcd, plp, DECLINE);
 140 #ifdef  DEBUG
 141                 dhcpmsg(LOG_DEBUG, "dhcp() - processed DECLINE.\n");
 142 #endif  /* DEBUG */
 143                 break;
 144         case RELEASE:
 145 #ifdef  DEBUG
 146                 dhcpmsg(LOG_DEBUG, "dhcp() - processing RELEASE...\n");
 147 #endif  /* DEBUG */
 148                 dhcp_dec_rel(pcd, plp, RELEASE);
 149 #ifdef  DEBUG
 150                 dhcpmsg(LOG_DEBUG, "dhcp() - processed RELEASE.\n");
 151 #endif  /* DEBUG */
 152                 break;
 153         case INFORM:
 154 #ifdef  DEBUG
 155                 dhcpmsg(LOG_DEBUG, "dhcp() - processing INFORM...\n");
 156 #endif  /* DEBUG */
 157                 dhcp_inform(pcd, plp);
 158 #ifdef  DEBUG
 159                 dhcpmsg(LOG_DEBUG, "dhcp() - processed INFORM.\n");
 160 #endif  /* DEBUG */
 161                 break;
 162         default:
 163                 dhcpmsg(LOG_INFO,
 164                     "Unexpected DHCP message type: %d from client: %s.\n",
 165                     pcd->state, pcd->cidbuf);
 166                 break;
 167         }
 168 }
 169 
 170 /*
 171  * Responding to a DISCOVER message. icmp echo check (if done) is synchronous.
 172  * Previously known requests are in the OFFER cache.
 173  */
 174 static void
 175 dhcp_offer(dsvc_clnt_t *pcd, PKT_LIST *plp)
 176 {
 177         IF              *ifp = pcd->ifp;
 178         boolean_t       result;
 179         struct in_addr  nsip, ncip;
 180         dsvc_dnet_t     *pnd = pcd->pnd;
 181         uint_t          replen;
 182         int             used_pkt_len;
 183         PKT             *rep_pktp = NULL;
 184         uchar_t         *optp;
 185         ENCODE          *ecp, *vecp, *macro_ecp, *macro_vecp,
 186             *class_ecp, *class_vecp,
 187             *cid_ecp, *cid_vecp,
 188             *net_ecp, *net_vecp;
 189         MACRO           *net_mp, *pkt_mp, *class_mp, *cid_mp;
 190         char            *class_id;
 191         time_t          now = time(NULL);
 192         lease_t         newlease, oldlease = 0;
 193         int             err = 0;
 194         boolean_t       existing_allocation = B_FALSE;
 195         boolean_t       existing_offer = B_FALSE;
 196         char            sipstr[INET_ADDRSTRLEN], cipstr[INET_ADDRSTRLEN];
 197         char            class_idbuf[DSYM_CLASS_SIZE];
 198         dn_rec_t        *dnp, dn, ndn;
 199         uint32_t        query;
 200         dn_rec_list_t   *dncp = NULL, *dnlp = NULL;
 201         boolean_t       unreserve = B_FALSE;
 202 
 203         class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
 204 
 205         /*
 206          * Purge offers when expired or the database has been re-read.
 207          *
 208          * Multi-threading: to better distribute garbage collection
 209          * and data structure aging tasks, each thread must actively
 210          * implement policy, rather then specialized, non-scalable
 211          * threads which halt the server and update all data
 212          * structures.
 213          *
 214          * The test below checks whether the offer has expired,
 215          * due to aging, or re-reading of the dhcptab, via timeout
 216          * or explicit signal.
 217          */
 218         if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
 219             PCD_OFFER_TIMEOUT(pcd, now))
 220                 purge_offer(pcd, B_TRUE, B_TRUE);
 221 
 222         if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
 223                 /*
 224                  * We've already validated this IP address in the past, and
 225                  * due to the OFFER hash table, we would not have offered this
 226                  * IP address to another client, so use the offer-cached record.
 227                  */
 228                 existing_offer = B_TRUE;
 229                 dnlp = pcd->dnlp;
 230                 dnp = dnlp->dnl_rec;
 231                 ncip.s_addr = htonl(dnp->dn_cip.s_addr);
 232         } else {
 233                 /* Try to find an existing usable entry for the client. */
 234                 DSVC_QINIT(query);
 235                 DSVC_QEQ(query, DN_QCID);
 236                 (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
 237                 dn.dn_cid_len = pcd->cid_len;
 238 
 239                 /* No bootp records, thank you. */
 240                 DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
 241                 dn.dn_flags = DN_FBOOTP_ONLY;
 242 
 243                 /*
 244                  * We don't limit this search by SIP, because this client
 245                  * may be owned by another server, and we need to detect this
 246                  * since that record may be MANUAL.
 247                  */
 248                 dncp = NULL;
 249                 dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
 250                     -1, &dn, (void **)&dncp, S_CID);
 251 
 252                 while (dnlp != NULL) {
 253 
 254                         dnp = dnlp->dnl_rec;
 255                         if (match_ownerip(htonl(dnp->dn_sip.s_addr)) == NULL) {
 256                                 /*
 257                                  * An IP address, but not ours! It's up to the
 258                                  * primary to respond to DISCOVERs on this
 259                                  * address.
 260                                  */
 261                                 if (verbose) {
 262                                         char    *m1, *m2;
 263 
 264                                         if (dnp->dn_flags & DN_FMANUAL) {
 265                                                 m1 = "MANUAL";
 266                                                 m2 = " No other IP address "
 267                                                     "will be allocated.";
 268                                         } else {
 269                                                 m1 = "DYNAMIC";
 270                                                 m2 = "";
 271                                         }
 272 
 273                                         nsip.s_addr = htonl(dnp->dn_sip.s_addr);
 274                                         (void) inet_ntop(AF_INET, &nsip, sipstr,
 275                                             sizeof (sipstr));
 276                                         ncip.s_addr = htonl(dnp->dn_cip.s_addr);
 277                                         (void) inet_ntop(AF_INET, &ncip, cipstr,
 278                                             sizeof (cipstr));
 279                                         dhcpmsg(LOG_INFO, "Client: %1$s has "
 280                                             "%2$s %3$s owned by server: "
 281                                             "%4$s.%5$s\n", pcd->cidbuf,
 282                                             m1, cipstr, sipstr, m2);
 283                                 }
 284 
 285                                 /* We give up if that IP address is manual */
 286                                 if (dnp->dn_flags & DN_FMANUAL)
 287                                         goto leave_offer;
 288                         } else {
 289                                 uint_t bits = DN_FUNUSABLE | DN_FMANUAL;
 290                                 if ((dnp->dn_flags & bits) == bits) {
 291                                         ncip.s_addr = htonl(dnp->dn_cip.s_addr);
 292                                         (void) inet_ntop(AF_INET, &ncip, cipstr,
 293                                             sizeof (cipstr));
 294                                         dhcpmsg(LOG_WARNING, "Client: %1$s "
 295                                             "MANUAL record %2$s is UNUSABLE. "
 296                                             "No other IP address will be "
 297                                             "allocated.\n", pcd->cidbuf,
 298                                             cipstr);
 299                                         goto leave_offer;
 300                                 } else
 301                                         break;  /* success */
 302                         }
 303 
 304                         free_dnrec_list(dnlp);
 305                         dnlp = detach_dnrec_from_list(NULL, dncp, &dncp);
 306                 }
 307 
 308                 if (dnlp == NULL) {
 309                         /*
 310                          * select_offer() ONLY selects IP addresses owned
 311                          * by us. Only log a notice if we own any IP addresses
 312                          * at all. Otherwise, this is an informational server.
 313                          */
 314                         if (!select_offer(pnd, plp, pcd, &dnlp)) {
 315                                 if (pnd->naddrs > 0) {
 316                                         dhcpmsg(LOG_NOTICE,
 317                                             "No more IP addresses on %1$s "
 318                                             "network (%2$s)\n", pnd->network,
 319                                             pcd->cidbuf);
 320                                 }
 321                                 goto leave_offer;
 322                         }
 323                         dnp = dnlp->dnl_rec;
 324                 } else
 325                         existing_allocation = B_TRUE;
 326 
 327                 ncip.s_addr = htonl(dnp->dn_cip.s_addr);
 328                 (void) inet_ntop(AF_INET, &ncip, cipstr, sizeof (cipstr));
 329 
 330                 /*
 331                  * ICMP echo validate the address.
 332                  */
 333                 if (!noping) {
 334                         /*
 335                          * If icmp echo validation fails, let the plp fall by
 336                          * the wayside.
 337                          */
 338                         if (icmp_echo_check(&ncip, &result) != 0) {
 339                                 dhcpmsg(LOG_ERR, "ICMP ECHO check cannot be "
 340                                     "registered for: %s, ignoring\n", cipstr);
 341                                 unreserve = B_TRUE;
 342                                 goto leave_offer;
 343                         }
 344                         if (result) {
 345                                 dhcpmsg(LOG_WARNING,
 346                                     "ICMP ECHO reply to OFFER candidate: "
 347                                     "%s, disabling.\n", cipstr);
 348 
 349                                 ndn = *dnp;     /* struct copy */
 350                                 ndn.dn_flags |= DN_FUNUSABLE;
 351 
 352                                 if ((err = dhcp_modify_dd_entry(pnd->dh, dnp,
 353                                     &ndn)) != DSVC_SUCCESS) {
 354                                         dhcpmsg(LOG_ERR,
 355                                             "ICMP ECHO reply to OFFER "
 356                                             "candidate: %1$s. No "
 357                                             "modifiable dhcp network "
 358                                             "record. (%2$s)\n", cipstr,
 359                                             dhcpsvc_errmsg(err));
 360                                 } else {
 361                                         /* Keep the cached entry current. */
 362                                         *dnp = ndn;    /* struct copy */
 363                                 }
 364 
 365                                 logtrans(P_DHCP, L_ICMP_ECHO, 0, ncip,
 366                                     server_ip, plp);
 367 
 368                                 unreserve = B_TRUE;
 369 
 370                                 goto leave_offer;
 371                         }
 372                 }
 373         }
 374 
 375         /*
 376          * At this point, we've ICMP validated (if requested) the IP
 377          * address, and can go about producing an OFFER for the client.
 378          */
 379 
 380         ecp = vecp = NULL;
 381         net_vecp = net_ecp = NULL;
 382         macro_vecp = macro_ecp = NULL;
 383         class_vecp = class_ecp = NULL;
 384         cid_vecp = cid_ecp = NULL;
 385         if (!no_dhcptab) {
 386                 open_macros();
 387 
 388                 /*
 389                  * Macros are evaluated this way: First apply parameters from
 390                  * a client class macro (if present), then apply those from the
 391                  * network macro (if present), then apply those from the
 392                  * dhcp network macro (if present), and finally apply those
 393                  * from a client id macro (if present).
 394                  */
 395 
 396                 /*
 397                  * First get a handle on network, dhcp network table macro,
 398                  * and client id macro values.
 399                  */
 400                 if ((net_mp = get_macro(pnd->network)) != NULL)
 401                         net_ecp = net_mp->head;
 402                 if ((pkt_mp = get_macro(dnp->dn_macro)) != NULL)
 403                         macro_ecp = pkt_mp->head;
 404                 if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
 405                         cid_ecp = cid_mp->head;
 406 
 407                 if (class_id != NULL) {
 408                         /* Get a handle on the class id macro (if it exists). */
 409                         if ((class_mp = get_macro(class_id)) != NULL) {
 410                                 /*
 411                                  * Locate the ENCODE list for encapsulated
 412                                  * options associated with our class id within
 413                                  * the class id macro.
 414                                  */
 415                                 class_vecp = vendor_encodes(class_mp, class_id);
 416                                 class_ecp = class_mp->head;
 417                         }
 418 
 419                         /*
 420                          * Locate the ENCODE list for encapsulated options
 421                          * associated with our class id within the network,
 422                          * dhcp network, and client macros.
 423                          */
 424                         if (net_mp != NULL)
 425                                 net_vecp = vendor_encodes(net_mp, class_id);
 426                         if (pkt_mp != NULL)
 427                                 macro_vecp = vendor_encodes(pkt_mp, class_id);
 428                         if (cid_mp != NULL)
 429                                 cid_vecp = vendor_encodes(cid_mp, class_id);
 430 
 431                         /*
 432                          * Combine the encapsulated option encode lists
 433                          * associated with our class id in the order defined
 434                          * above (class, net, dhcp network, client id)
 435                          */
 436                         vecp = combine_encodes(class_vecp, net_vecp, ENC_COPY);
 437                         vecp = combine_encodes(vecp, macro_vecp, ENC_DONT_COPY);
 438                         vecp = combine_encodes(vecp, cid_vecp, ENC_DONT_COPY);
 439                 }
 440 
 441                 /*
 442                  * Combine standard option encode lists in the order defined
 443                  * above (class, net, dhcp network, and client id).
 444                  */
 445                 if (class_ecp != NULL)
 446                         ecp = combine_encodes(class_ecp, net_ecp, ENC_COPY);
 447                 else
 448                         ecp = dup_encode_list(net_ecp);
 449 
 450                 ecp = combine_encodes(ecp, macro_ecp, ENC_DONT_COPY);
 451                 ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
 452 
 453                 /* If dhcptab configured to return hostname, do so. */
 454                 if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) != NULL) {
 455                         struct          hostent h, *hp;
 456                         char            hbuf[NSS_BUFLEN_HOSTS];
 457                         ENCODE          *hecp;
 458                         hp = gethostbyaddr_r((char *)&ncip, sizeof (ncip),
 459                             AF_INET, &h, hbuf, sizeof (hbuf), &err);
 460                         if (hp != NULL) {
 461                                 hecp = make_encode(DSYM_STANDARD,
 462                                     CD_HOSTNAME, strlen(hp->h_name),
 463                                     hp->h_name, ENC_COPY);
 464                                 replace_encode(&ecp, hecp, ENC_DONT_COPY);
 465                         }
 466                 }
 467 
 468                 /* If dhcptab configured to echo client class, do so. */
 469                 if (plp->opts[CD_CLASS_ID] != NULL &&
 470                     find_encode(ecp, DSYM_INTERNAL, CD_BOOL_ECHO_VCLASS) !=
 471                     NULL) {
 472                         ENCODE          *echo_ecp;
 473                         DHCP_OPT        *op = plp->opts[CD_CLASS_ID];
 474                         echo_ecp = make_encode(DSYM_STANDARD, CD_CLASS_ID,
 475                             op->len, op->value, ENC_COPY);
 476                         replace_encode(&ecp, echo_ecp, ENC_DONT_COPY);
 477                 }
 478         }
 479 
 480         if ((ifp->flags & IFF_NOARP) == 0)
 481                 (void) set_arp(ifp, &ncip, NULL, 0, DHCP_ARP_DEL);
 482 
 483         /*
 484          * For OFFERs, we don't check the client's lease nor LeaseNeg,
 485          * regardless of whether the client has an existing allocation
 486          * or not. Lease expiration (w/o LeaseNeg) only occur during
 487          * RENEW/REBIND or INIT-REBOOT client states, not SELECTing state.
 488          */
 489         if (existing_allocation) {
 490                 if (dnp->dn_lease == DHCP_PERM ||
 491                     (dnp->dn_flags & DN_FAUTOMATIC)) {
 492                         oldlease = DHCP_PERM;
 493                 } else {
 494                         if ((lease_t)dnp->dn_lease < (lease_t)now)
 495                                 oldlease = (lease_t)0;
 496                         else {
 497                                 oldlease = (lease_t)dnp->dn_lease -
 498                                     (lease_t)now;
 499                         }
 500                 }
 501         }
 502 
 503         /* First get a generic reply packet. */
 504         rep_pktp = gen_reply_pkt(pcd, plp, OFFER, &replen, &optp, &ifp->addr);
 505 
 506         /* Set the client's IP address */
 507         rep_pktp->yiaddr.s_addr = htonl(dnp->dn_cip.s_addr);
 508 
 509         /* Calculate lease time. */
 510         newlease = config_lease(plp, dnp, &ecp, oldlease, B_TRUE);
 511 
 512         /*
 513          * Client is requesting specific options. let's try and ensure it
 514          * gets what it wants, if at all possible.
 515          */
 516         if (plp->opts[CD_REQUEST_LIST] != NULL)
 517                 add_request_list(ifp, plp, &ecp, &ncip);
 518 
 519         /* Now load all the asked for / configured options */
 520         used_pkt_len = load_options(DHCP_DHCP_CLNT | DHCP_SEND_LEASE, plp,
 521             rep_pktp, replen, optp, ecp, vecp);
 522 
 523         free_encode_list(ecp);
 524         free_encode_list(vecp);
 525         if (!no_dhcptab)
 526                 close_macros();
 527 
 528         if (used_pkt_len < sizeof (PKT))
 529                 used_pkt_len = sizeof (PKT);
 530 
 531         if (send_reply(ifp, rep_pktp, used_pkt_len, &ncip) == 0) {
 532                 if (newlease == DHCP_PERM)
 533                         newlease = htonl(newlease);
 534                 else
 535                         newlease = htonl(now + newlease);
 536                 (void) update_offer(pcd, &dnlp, newlease, NULL, B_TRUE);
 537                 existing_offer = B_TRUE;
 538         } else {
 539                 unreserve = B_TRUE;
 540         }
 541 
 542 leave_offer:
 543         if (unreserve)
 544                 purge_offer(pcd, B_FALSE, B_TRUE);
 545         if (rep_pktp != NULL)
 546                 free(rep_pktp);
 547         if (dncp != NULL)
 548                 dhcp_free_dd_list(pnd->dh, dncp);
 549         if (dnlp != NULL && !existing_offer)
 550                 dhcp_free_dd_list(pnd->dh, dnlp);
 551 }
 552 
 553 /*
 554  * Responding to REQUEST message.
 555  *
 556  * Very similar to dhcp_offer(), except that we need to be more
 557  * discriminating.
 558  *
 559  * The ciaddr field is TRUSTED. A INIT-REBOOTing client will place its
 560  * notion of its IP address in the requested IP address option. INIT
 561  * clients will place the value in the OFFERs yiaddr in the requested
 562  * IP address option. INIT-REBOOT packets are differentiated from INIT
 563  * packets in that the server id option is missing. ciaddr will only
 564  * appear from clients in the RENEW/REBIND states.
 565  *
 566  * Error messages may be generated. Database write failures are no longer
 567  * fatal, since we'll only respond to the client if the write succeeds.
 568  */
 569 static void
 570 dhcp_req_ack(dsvc_clnt_t *pcd, PKT_LIST *plp)
 571 {
 572         dn_rec_t        dn, ndn, *dnp;
 573         struct in_addr  serverid, ciaddr, claddr, nreqaddr, cipaddr,
 574             ncipaddr, sipaddr;
 575         struct in_addr  dest_in;
 576         dsvc_dnet_t     *pnd = pcd->pnd;
 577         uint_t          replen;
 578         int             actual_len;
 579         int             pkt_type = ACK;
 580         DHCP_MSG_CATEGORIES     log;
 581         PKT             *rep_pktp = NULL;
 582         uchar_t         *optp;
 583         ENCODE          *ecp, *vecp,
 584             *class_ecp, *class_vecp,
 585             *net_ecp, *net_vecp,
 586             *macro_ecp, *macro_vecp,
 587             *cid_ecp, *cid_vecp;
 588         MACRO           *class_mp, *pkt_mp, *net_mp, *cid_mp;
 589         char            *class_id;
 590         char            nak_mesg[DHCP_SCRATCH];
 591         time_t          now;
 592         lease_t         newlease, oldlease;
 593         boolean_t       negot;
 594         int             err = 0;
 595         int             write_error = DSVC_SUCCESS, clnt_state;
 596         ushort_t        boot_secs;
 597         char            ntoaa[INET_ADDRSTRLEN], ntoab[INET_ADDRSTRLEN],
 598             ntoac[INET_ADDRSTRLEN];
 599         char            class_idbuf[DSYM_CLASS_SIZE];
 600         boolean_t       hostname_update = B_FALSE;
 601         dn_rec_list_t   *nlp, *dncp = NULL, *dnlp = NULL;
 602         uint32_t        query;
 603         IF              *ifp = pcd->ifp;
 604         boolean_t       existing_offer = B_FALSE;
 605 
 606         ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
 607         boot_secs = ntohs(plp->pkt->secs);
 608         now = time(NULL);
 609 
 610         class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
 611 
 612         /* Determine type of REQUEST we've got. */
 613         if (plp->opts[CD_SERVER_ID] != NULL) {
 614                 if (plp->opts[CD_SERVER_ID]->len != sizeof (struct in_addr)) {
 615                         dhcpmsg(LOG_ERR, "Garbled DHCP Server ID option from "
 616                             "client: '%1$s'. Len is %2$d, when it should be "
 617                             "%3$d \n", pcd->cidbuf,
 618                             plp->opts[CD_SERVER_ID]->len,
 619                             sizeof (struct in_addr));
 620                         goto leave_ack;
 621                 }
 622 
 623                 /*
 624                  * Request in response to an OFFER. ciaddr must not
 625                  * be set. Requested IP address option will hold address
 626                  * we offered the client.
 627                  */
 628                 clnt_state = INIT_STATE;
 629                 (void) memcpy((void *)&serverid,
 630                     plp->opts[CD_SERVER_ID]->value, sizeof (struct in_addr));
 631 
 632                 if (plp->opts[CD_REQUESTED_IP_ADDR] == NULL) {
 633                         if (verbose) {
 634                                 dhcpmsg(LOG_NOTICE, "%1$s: REQUEST on %2$s is "
 635                                     "missing requested IP option.\n",
 636                                     pcd->cidbuf, pcd->pnd->network);
 637                         }
 638                         goto leave_ack;
 639                 }
 640                 if (plp->opts[CD_REQUESTED_IP_ADDR]->len !=
 641                     sizeof (struct in_addr)) {
 642                         dhcpmsg(LOG_ERR, "Garbled Requested IP option from "
 643                             "client: '%1$s'. Len is %2$d, when it should be "
 644                             "%3$d \n",
 645                             pcd->cidbuf, plp->opts[CD_REQUESTED_IP_ADDR]->len,
 646                             sizeof (struct in_addr));
 647                         goto leave_ack;
 648                 }
 649                 (void) memcpy((void *)&nreqaddr,
 650                     plp->opts[CD_REQUESTED_IP_ADDR]->value,
 651                     sizeof (struct in_addr));
 652 
 653                 if (serverid.s_addr != ifp->addr.s_addr) {
 654                         /*
 655                          * Another server address was selected.
 656                          *
 657                          * If the server address is handled by another
 658                          * thread of our process, do nothing in the
 659                          * hope that the other thread will eventually
 660                          * receive a REQUEST with its server address.
 661                          *
 662                          * If a server address was selected which is
 663                          * not handled by this process, see if we made
 664                          * an offer, and clear it if we did. If offer
 665                          * expired before client responded, then no
 666                          * need to do anything.
 667                          */
 668                         if (is_our_address(serverid.s_addr)) {
 669                                 if (verbose) {
 670                                         dhcpmsg(LOG_INFO,
 671                                             "Client: %1$s chose %2$s from "
 672                                             "server: %3$s, which is being "
 673                                             "handled by another thread\n",
 674                                             pcd->cidbuf,
 675                                             inet_ntop(AF_INET, &nreqaddr, ntoaa,
 676                                             sizeof (ntoaa)),
 677                                             inet_ntop(AF_INET, &serverid, ntoab,
 678                                             sizeof (ntoab)));
 679                                 }
 680                         } else {
 681                                 purge_offer(pcd, B_FALSE, B_TRUE);
 682                                 if (verbose) {
 683                                         dhcpmsg(LOG_INFO,
 684                                             "Client: %1$s chose %2$s from "
 685                                             "server: %3$s, not %4$s\n",
 686                                             pcd->cidbuf,
 687                                             inet_ntop(AF_INET, &nreqaddr, ntoaa,
 688                                             sizeof (ntoaa)),
 689                                             inet_ntop(AF_INET, &serverid, ntoab,
 690                                             sizeof (ntoab)),
 691                                             inet_ntop(AF_INET, &ifp->addr,
 692                                             ntoac, sizeof (ntoac)));
 693                                 }
 694                         }
 695                         goto leave_ack;
 696                 }
 697 
 698                 /*
 699                  * See comment at the top of the file for description of
 700                  * OFFER cache.
 701                  *
 702                  * If the offer expires before the client got around to
 703                  * requesting, and we can't confirm the address is still free,
 704                  * we'll silently ignore the client, until it drops back and
 705                  * tries to discover again. We will print a message in
 706                  * verbose mode however. If the Offer hasn't timed out, we
 707                  * bump it up again in case we have a bounce of queued up
 708                  * INIT requests to respond to.
 709                  */
 710                 if (pcd->off_ip.s_addr == htonl(INADDR_ANY) ||
 711                     PCD_OFFER_TIMEOUT(pcd, now)) {
 712                         /*
 713                          * Hopefully, the timeout value is fairly long to
 714                          * prevent this.
 715                          */
 716                         purge_offer(pcd, B_TRUE, B_TRUE);
 717                         if (verbose) {
 718                                 dhcpmsg(LOG_INFO,
 719                                     "Offer on %1$s expired for client: %2$s\n",
 720                                     pcd->pnd->network, pcd->cidbuf);
 721                         }
 722                         goto leave_ack;
 723                 } else
 724                         (void) update_offer(pcd, NULL, 0, NULL, B_TRUE);
 725 
 726                 /*
 727                  * The client selected us. Create a ACK, and send
 728                  * it off to the client, commit to permanent
 729                  * storage the new binding.
 730                  */
 731                 existing_offer = B_TRUE;
 732                 dnlp = pcd->dnlp;
 733                 dnp = dnlp->dnl_rec;
 734                 ndn = *dnp;     /* struct copy */
 735                 ndn.dn_lease = pcd->lease;
 736                 ncipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
 737 
 738                 /*
 739                  * If client thinks we offered it a different address, then
 740                  * ignore it.
 741                  */
 742                 if (memcmp((char *)&ncipaddr,
 743                     plp->opts[CD_REQUESTED_IP_ADDR]->value,
 744                     sizeof (struct in_addr)) != 0) {
 745                         if (verbose) {
 746                                 dhcpmsg(LOG_INFO, "Client %1$s believes "
 747                                     "offered IP address %2$s is different than "
 748                                     "what was offered.\n", pcd->cidbuf,
 749                                     inet_ntop(AF_INET, &ncipaddr, ntoab,
 750                                     sizeof (ntoab)));
 751                         }
 752                         goto leave_ack;
 753                 }
 754 
 755                 /*
 756                  * Clear out any temporary ARP table entry we may have
 757                  * created during the offer.
 758                  */
 759                 if ((ifp->flags & IFF_NOARP) == 0)
 760                         (void) set_arp(ifp, &ncipaddr, NULL, 0, DHCP_ARP_DEL);
 761         } else {
 762                 /*
 763                  * Either a client in the INIT-REBOOT state, or one in
 764                  * either RENEW or REBIND states. The latter will have
 765                  * ciaddr set, whereas the former will place its concept
 766                  * of its IP address in the requested IP address option.
 767                  */
 768                 if (ciaddr.s_addr == htonl(INADDR_ANY)) {
 769                         clnt_state = INIT_REBOOT_STATE;
 770                         /*
 771                          * Client isn't sure of its IP address. It's
 772                          * attempting to verify its address, thus requested
 773                          * IP option better be present, and correct.
 774                          */
 775                         if (plp->opts[CD_REQUESTED_IP_ADDR] == NULL) {
 776                                 dhcpmsg(LOG_ERR,
 777                                     "Client: %s REQUEST is missing "
 778                                     "requested IP option.\n", pcd->cidbuf);
 779                                 goto leave_ack;
 780                         }
 781                         if (plp->opts[CD_REQUESTED_IP_ADDR]->len !=
 782                             sizeof (struct in_addr)) {
 783                                 dhcpmsg(LOG_ERR, "Garbled Requested IP option "
 784                                     "from client: '%1$s'. Len is %2$d, when it "
 785                                     "should be %3$d \n", pcd->cidbuf,
 786                                     plp->opts[CD_REQUESTED_IP_ADDR]->len,
 787                                     sizeof (struct in_addr));
 788                                 goto leave_ack;
 789                         }
 790                         (void) memcpy(&claddr,
 791                             plp->opts[CD_REQUESTED_IP_ADDR]->value,
 792                             sizeof (struct in_addr));
 793 
 794                         DSVC_QINIT(query);
 795                         DSVC_QEQ(query, DN_QCID);
 796                         (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
 797                         dn.dn_cid_len = pcd->cid_len;
 798 
 799                         /* No bootp records, thank you. */
 800                         DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
 801                         dn.dn_flags = DN_FBOOTP_ONLY;
 802 
 803                 } else {
 804                         clnt_state = RENEW_REBIND_STATE;
 805                         /*
 806                          * Client knows its IP address. It is trying to
 807                          * RENEW/REBIND (extend its lease). We trust ciaddr,
 808                          * and use it to locate the client's record. If we
 809                          * can't find the client's record, then we keep
 810                          * silent. If the client id of the record doesn't
 811                          * match this client, then the database is
 812                          * inconsistent, and we'll ignore it.
 813                          */
 814                         DSVC_QINIT(query);
 815                         DSVC_QEQ(query, DN_QCID|DN_QCIP);
 816                         (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
 817                         dn.dn_cid_len = pcd->cid_len;
 818                         dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
 819 
 820                         /* No bootp records, thank you. */
 821                         DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
 822                         dn.dn_flags = DN_FBOOTP_ONLY;
 823 
 824                         claddr.s_addr = ciaddr.s_addr;
 825                 }
 826 
 827                 dncp = NULL;
 828                 dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
 829                     -1, &dn, (void **)&dncp, S_CID);
 830 
 831                 if (dnlp != NULL) {
 832                         dnp = dnlp->dnl_rec;
 833                         if (dnp->dn_flags & DN_FUNUSABLE)
 834                                 goto leave_ack;
 835 
 836                         sipaddr.s_addr = htonl(dnp->dn_sip.s_addr);
 837                         cipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
 838 
 839                         /*
 840                          * If this address is not owned by this server and
 841                          * the client is trying to verify the address, then
 842                          * ignore the client. If the client is simply trying
 843                          * to rebind, then don't respond until after
 844                          * renog_secs passes, to give the server that *OWNS*
 845                          * the address time to respond first.
 846                          */
 847                         if (match_ownerip(sipaddr.s_addr) == NULL) {
 848                                 if (clnt_state == INIT_REBOOT_STATE) {
 849                                         if (verbose) {
 850                                                 dhcpmsg(LOG_NOTICE, "Client: "
 851                                                     "%1$s is requesting "
 852                                                     "verification of %2$s "
 853                                                     "owned by %3$s\n",
 854                                                     pcd->cidbuf,
 855                                                     inet_ntop(AF_INET, &cipaddr,
 856                                                     ntoab, sizeof (ntoab)),
 857                                                     inet_ntop(AF_INET, &sipaddr,
 858                                                     ntoac, sizeof (ntoac)));
 859                                         }
 860                                         goto leave_ack;
 861                                 } else {
 862                                         /* RENEW/REBIND - wait for primary */
 863                                         if (boot_secs < (ushort_t)renog_secs)
 864                                                 goto leave_ack;
 865                                 }
 866 
 867                         }
 868                         if (claddr.s_addr != htonl(dnp->dn_cip.s_addr)) {
 869                                 /*
 870                                  * Client has the wrong IP address. Nak.
 871                                  */
 872                                 (void) snprintf(nak_mesg, sizeof (nak_mesg),
 873                                     "Incorrect IP address.");
 874                                 pkt_type = NAK;
 875                         } else {
 876                                 if (!(dnp->dn_flags & DN_FAUTOMATIC) &&
 877                                     (lease_t)dnp->dn_lease < (lease_t)now) {
 878                                         (void) snprintf(nak_mesg,
 879                                             sizeof (nak_mesg),
 880                                             "Lease has expired.");
 881                                         pkt_type = NAK;
 882                                 }
 883                         }
 884                 } else {
 885                         if (clnt_state == RENEW_REBIND_STATE) {
 886                                 dhcpmsg(LOG_ERR, "Client: %1$s is trying to "
 887                                     "renew %2$s, an IP address it has not "
 888                                     "leased.\n", pcd->cidbuf, inet_ntop(AF_INET,
 889                                     &ciaddr, ntoab, sizeof (ntoab)));
 890                                 goto leave_ack;
 891                         }
 892                         /*
 893                          * There is no such client registered for this
 894                          * address. Check if their address is on the correct
 895                          * net. If it is, then we'll assume that some other,
 896                          * non-database sharing DHCP server knows about this
 897                          * client. If the client is on the wrong net, NAK'em.
 898                          */
 899                         if ((claddr.s_addr & pnd->subnet.s_addr) ==
 900                             pnd->net.s_addr) {
 901                                 /* Right net, but no record of client. */
 902                                 if (verbose) {
 903                                         dhcpmsg(LOG_INFO,
 904                                             "Client: %1$s is trying to verify "
 905                                             "unrecorded address: %2$s, "
 906                                             "ignored.\n", pcd->cidbuf,
 907                                             inet_ntop(AF_INET, &claddr,
 908                                             ntoab, sizeof (ntoab)));
 909                                 }
 910                                 goto leave_ack;
 911                         } else {
 912                                 if (ciaddr.s_addr == 0L) {
 913                                         (void) snprintf(nak_mesg,
 914                                             sizeof (nak_mesg),
 915                                             "No valid configuration exists on "
 916                                             "network: %s", pnd->network);
 917                                         pkt_type = NAK;
 918                                 } else {
 919                                         if (verbose) {
 920                                                 dhcpmsg(LOG_INFO,
 921                                                     "Client: %1$s is not "
 922                                                     "recorded as having "
 923                                                     "address: %2$s\n",
 924                                                     pcd->cidbuf,
 925                                                     inet_ntop(AF_INET, &ciaddr,
 926                                                     ntoab, sizeof (ntoab)));
 927                                         }
 928                                         goto leave_ack;
 929                                 }
 930                         }
 931                 }
 932         }
 933 
 934         /*
 935          * Produce the appropriate response.
 936          */
 937         if (pkt_type == NAK) {
 938                 rep_pktp = gen_reply_pkt(pcd, plp, NAK, &replen, &optp,
 939                     &ifp->addr);
 940                 /*
 941                  * Setting yiaddr to the client's ciaddr abuses the
 942                  * semantics of yiaddr, So we set this to 0L.
 943                  *
 944                  * We twiddle the broadcast flag to force the
 945                  * server/relay agents to broadcast the NAK.
 946                  *
 947                  * Exception: If a client's lease has expired, and it
 948                  * is still trying to renegotiate its lease, AND ciaddr
 949                  * is set, AND ciaddr is on a "remote" net, unicast the
 950                  * NAK. Gross, huh? But SPA could make this happen with
 951                  * super short leases.
 952                  */
 953                 rep_pktp->yiaddr.s_addr = 0L;
 954                 if (ciaddr.s_addr != 0L &&
 955                     (ciaddr.s_addr & pnd->subnet.s_addr) != pnd->net.s_addr) {
 956                         dest_in.s_addr = ciaddr.s_addr;
 957                 } else {
 958                         rep_pktp->flags |= htons(BCAST_MASK);
 959                         dest_in.s_addr = INADDR_BROADCAST;
 960                 }
 961 
 962                 *optp++ = CD_MESSAGE;
 963                 *optp++ = (uchar_t)strlen(nak_mesg);
 964                 (void) memcpy(optp, nak_mesg, strlen(nak_mesg));
 965                 optp += strlen(nak_mesg);
 966                 *optp = CD_END;
 967                 actual_len = BASE_PKT_SIZE + (uint_t)(optp - rep_pktp->options);
 968                 if (actual_len < sizeof (PKT))
 969                         actual_len = sizeof (PKT);
 970 
 971                 (void) send_reply(ifp, rep_pktp, actual_len, &dest_in);
 972 
 973                 logtrans(P_DHCP, L_NAK, 0, dest_in, server_ip, plp);
 974         } else {
 975                 rep_pktp = gen_reply_pkt(pcd, plp, ACK, &replen, &optp,
 976                     &ifp->addr);
 977 
 978                 /* Set the client's IP address */
 979                 rep_pktp->yiaddr.s_addr = htonl(dnp->dn_cip.s_addr);
 980                 dest_in.s_addr = htonl(dnp->dn_cip.s_addr);
 981 
 982                 /*
 983                  * Macros are evaluated this way: First apply parameters
 984                  * from a client class macro (if present), then apply
 985                  * those from the network macro (if present), then apply
 986                  * those from the server macro (if present), and finally
 987                  * apply those from a client id macro (if present).
 988                  */
 989                 ecp = vecp = NULL;
 990                 class_vecp = class_ecp = NULL;
 991                 net_vecp = net_ecp = NULL;
 992                 macro_vecp = macro_ecp = NULL;
 993                 cid_vecp = cid_ecp = NULL;
 994 
 995                 if (!no_dhcptab) {
 996                         open_macros();
 997                         if ((net_mp = get_macro(pnd->network)) != NULL)
 998                                 net_ecp = net_mp->head;
 999                         if ((pkt_mp = get_macro(dnp->dn_macro)) != NULL)
1000                                 macro_ecp = pkt_mp->head;
1001                         if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
1002                                 cid_ecp = cid_mp->head;
1003                         if (class_id != NULL) {
1004                                 if ((class_mp = get_macro(class_id)) != NULL) {
1005                                         class_vecp = vendor_encodes(class_mp,
1006                                             class_id);
1007                                         class_ecp = class_mp->head;
1008                                 }
1009                                 if (net_mp != NULL) {
1010                                         net_vecp = vendor_encodes(net_mp,
1011                                             class_id);
1012                                 }
1013                                 if (pkt_mp != NULL)
1014                                         macro_vecp = vendor_encodes(pkt_mp,
1015                                             class_id);
1016                                 if (cid_mp != NULL) {
1017                                         cid_vecp = vendor_encodes(cid_mp,
1018                                             class_id);
1019                                 }
1020                                 vecp = combine_encodes(class_vecp, net_vecp,
1021                                     ENC_COPY);
1022                                 vecp = combine_encodes(vecp, macro_vecp,
1023                                     ENC_DONT_COPY);
1024                                 vecp = combine_encodes(vecp, cid_vecp,
1025                                     ENC_DONT_COPY);
1026                         }
1027                         if (class_ecp != NULL) {
1028                                 ecp = combine_encodes(class_ecp, net_ecp,
1029                                     ENC_COPY);
1030                         } else
1031                                 ecp = dup_encode_list(net_ecp);
1032 
1033                         ecp = combine_encodes(ecp, macro_ecp, ENC_DONT_COPY);
1034                         ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
1035 
1036                         ncipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
1037                         /*
1038                          * If the server is configured to do host name updates
1039                          * and the REQUEST packet contains a hostname request,
1040                          * see whether we can honor it.
1041                          *
1042                          * First, determine (via name_avail()) whether the host
1043                          * name is unassigned or belongs to an unleased IP
1044                          * address under our control.  If not, we won't do a
1045                          * host name update on behalf of the client.
1046                          *
1047                          * Second, if we own the IP address and it is in the
1048                          * correct network table, see whether an update is
1049                          * necessary (or, in the lucky case, whether the name
1050                          * requested already belongs to that address), in which
1051                          * case we need do nothing more than return the option.
1052                          */
1053                         if ((nsutimeout_secs != DHCP_NO_NSU) &&
1054                             (plp->opts[CD_HOSTNAME] != NULL)) {
1055                                 char            hname[MAXHOSTNAMELEN + 1];
1056                                 int             hlen;
1057                                 struct in_addr  ia, *iap = &ia;
1058 
1059                                 /* turn hostname option into a string */
1060                                 hlen = plp->opts[CD_HOSTNAME]->len;
1061                                 hlen = MIN(hlen, MAXHOSTNAMELEN);
1062                                 (void) memcpy(hname,
1063                                     plp->opts[CD_HOSTNAME]->value, hlen);
1064                                 hname[hlen] = '\0';
1065 
1066                                 nlp = NULL;
1067                                 if (name_avail(hname, pcd, plp, &nlp, ecp,
1068                                     &iap)) {
1069                                         ENCODE  *hecp;
1070 
1071                                         /*
1072                                          * If we pass this test, it means either
1073                                          * no address is currently associated
1074                                          * with the requested host name (iap is
1075                                          * NULL) or the address doesn't match
1076                                          * the one to be leased;  in either case
1077                                          * an update attempt is needed.
1078                                          *
1079                                          * Otherwise (in the else case), we need
1080                                          * only send the response - the name and
1081                                          * address already match.
1082                                          */
1083                                         if ((iap == NULL) || (iap->s_addr !=
1084                                             dnp->dn_cip.s_addr)) {
1085                                                 if (do_nsupdate(dnp->dn_cip,
1086                                                     ecp, plp)) {
1087                                                         hecp = make_encode(
1088                                                             DSYM_STANDARD,
1089                                                             CD_HOSTNAME,
1090                                                             strlen(hname),
1091                                                             hname,
1092                                                             ENC_COPY);
1093                                                         replace_encode(&ecp,
1094                                                             hecp,
1095                                                             ENC_DONT_COPY);
1096                                                         hostname_update =
1097                                                             B_TRUE;
1098                                                 }
1099                                         } else {
1100                                                 hecp = make_encode(
1101                                                     DSYM_STANDARD,
1102                                                     CD_HOSTNAME,
1103                                                     strlen(hname), hname,
1104                                                     ENC_COPY);
1105                                                 replace_encode(&ecp, hecp,
1106                                                     ENC_DONT_COPY);
1107                                                 hostname_update = B_TRUE;
1108                                         }
1109                                         if (nlp != NULL)
1110                                                 dhcp_free_dd_list(pnd->dh, nlp);
1111                                 }
1112                         }
1113 
1114                         /*
1115                          * If dhcptab configured to return hostname, do so.
1116                          */
1117                         if ((hostname_update == B_FALSE) &&
1118                             (find_encode(ecp, DSYM_INTERNAL,
1119                             CD_BOOL_HOSTNAME) != NULL)) {
1120                                 struct          hostent h, *hp;
1121                                 ENCODE          *hecp;
1122                                 char            hbuf[NSS_BUFLEN_HOSTS];
1123                                 hp = gethostbyaddr_r((char *)&ncipaddr,
1124                                     sizeof (struct in_addr), AF_INET, &h, hbuf,
1125                                     sizeof (hbuf), &err);
1126                                 if (hp != NULL) {
1127                                         hecp = make_encode(DSYM_STANDARD,
1128                                             CD_HOSTNAME, strlen(hp->h_name),
1129                                             hp->h_name, ENC_COPY);
1130                                         replace_encode(&ecp, hecp,
1131                                             ENC_DONT_COPY);
1132                                 }
1133                         }
1134 
1135                         /*
1136                          * If dhcptab configured to echo client class, do so.
1137                          */
1138                         if (plp->opts[CD_CLASS_ID] != NULL &&
1139                             find_encode(ecp, DSYM_INTERNAL,
1140                             CD_BOOL_ECHO_VCLASS) != NULL) {
1141                                 ENCODE          *echo_ecp;
1142                                 DHCP_OPT        *op = plp->opts[CD_CLASS_ID];
1143                                 echo_ecp = make_encode(DSYM_STANDARD,
1144                                     CD_CLASS_ID, op->len, op->value,
1145                                     ENC_COPY);
1146                                 replace_encode(&ecp, echo_ecp, ENC_DONT_COPY);
1147                         }
1148                 }
1149 
1150                 if (dnp->dn_flags & DN_FAUTOMATIC || dnp->dn_lease == DHCP_PERM)
1151                         oldlease = DHCP_PERM;
1152                 else {
1153                         if (plp->opts[CD_SERVER_ID] != NULL) {
1154                                 /*
1155                                  * Offered absolute Lease time is cached
1156                                  * in the lease field of the record. If
1157                                  * that's expired, then they'll get the
1158                                  * policy value again here. Must have been
1159                                  * LONG time between DISC/REQ!
1160                                  */
1161                                 if ((lease_t)dnp->dn_lease < (lease_t)now)
1162                                         oldlease = (lease_t)0;
1163                                 else
1164                                         oldlease = dnp->dn_lease - now;
1165                         } else
1166                                 oldlease = dnp->dn_lease - now;
1167                 }
1168 
1169                 if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_LEASENEG) !=
1170                     NULL)
1171                         negot = B_TRUE;
1172                 else
1173                         negot = B_FALSE;
1174 
1175                 /*
1176                  * Modify changed fields in new database record.
1177                  */
1178                 ndn = *dnp;     /* struct copy */
1179                 (void) memcpy(ndn.dn_cid, pcd->cid, pcd->cid_len);
1180                 ndn.dn_cid_len = pcd->cid_len;
1181 
1182                 /*
1183                  * This is a little longer than we offered (not taking into
1184                  * account the secs field), but since I trust the UNIX
1185                  * clock better than the PC's, it is a good idea to give
1186                  * the PC a little more time than it thinks, just due to
1187                  * clock slop on PC's.
1188                  */
1189                 newlease = config_lease(plp, &ndn, &ecp, oldlease, negot);
1190 
1191                 if (newlease != DHCP_PERM)
1192                         ndn.dn_lease = now + newlease;
1193                 else
1194                         ndn.dn_lease = DHCP_PERM;
1195 
1196 
1197                 /*
1198                  * It is critical to write the database record if the
1199                  * client is in the INIT state, so we don't reply to the
1200                  * client if this fails. However, if the client is simply
1201                  * trying to verify its address or extend its lease, then
1202                  * we'll reply regardless of the status of the write,
1203                  * although we'll return the old lease time.
1204                  *
1205                  * If the client is in the INIT_REBOOT state, and the
1206                  * lease time hasn't changed, we don't bother with the
1207                  * write, since nothing has changed.
1208                  */
1209                 if (clnt_state == INIT_STATE || oldlease != newlease) {
1210 
1211                         write_error = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
1212 
1213                         /* Keep state of the cached entry current. */
1214                         if (write_error == DSVC_SUCCESS) {
1215                                 *dnp = ndn;    /* struct copy */
1216                         }
1217                 } else {
1218                         if (verbose) {
1219                                 dhcpmsg(LOG_INFO,
1220                                     "Database write unnecessary for "
1221                                     "DHCP client: "
1222                                     "%1$s, %2$s\n", pcd->cidbuf,
1223                                     inet_ntop(AF_INET, &ncipaddr,
1224                                     ntoab, sizeof (ntoab)));
1225                         }
1226                 }
1227                 if (write_error == DSVC_SUCCESS ||
1228                     clnt_state == INIT_REBOOT_STATE) {
1229 
1230                         if (write_error != DSVC_SUCCESS)
1231                                 set_lease_option(&ecp, oldlease);
1232                         else {
1233                                 /* Note that the conversation has completed. */
1234                                 pcd->state = ACK;
1235                         }
1236 
1237                         if (plp->opts[CD_REQUEST_LIST])
1238                                 add_request_list(ifp, plp, &ecp, &ncipaddr);
1239 
1240                         /* Now load all the asked for / configured options */
1241                         actual_len = load_options(DHCP_DHCP_CLNT |
1242                             DHCP_SEND_LEASE, plp, rep_pktp, replen, optp, ecp,
1243                             vecp);
1244 
1245                         if (actual_len < sizeof (PKT))
1246                                 actual_len = sizeof (PKT);
1247                         if (verbose) {
1248                                 dhcpmsg(LOG_INFO,
1249                                     "Client: %1$s maps to IP: %2$s\n",
1250                                     pcd->cidbuf,
1251                                     inet_ntop(AF_INET, &ncipaddr,
1252                                     ntoab, sizeof (ntoab)));
1253                         }
1254                         (void) send_reply(ifp, rep_pktp, actual_len, &dest_in);
1255 
1256                         if (clnt_state == INIT_STATE)
1257                                 log = L_ASSIGN;
1258                         else
1259                                 log = L_REPLY;
1260 
1261                         logtrans(P_DHCP, log, ndn.dn_lease, ncipaddr,
1262                             server_ip, plp);
1263                 }
1264 
1265                 free_encode_list(ecp);
1266                 free_encode_list(vecp);
1267                 if (!no_dhcptab)
1268                         close_macros();
1269         }
1270 
1271 leave_ack:
1272         if (rep_pktp != NULL)
1273                 free(rep_pktp);
1274         if (dncp != NULL)
1275                 dhcp_free_dd_list(pnd->dh, dncp);
1276         if (dnlp != NULL && !existing_offer)
1277                 dhcp_free_dd_list(pnd->dh, dnlp);
1278 }
1279 
1280 /* Reacting to a client's DECLINE or RELEASE. */
1281 static void
1282 dhcp_dec_rel(dsvc_clnt_t *pcd, PKT_LIST *plp, int type)
1283 {
1284         char            *fmtp;
1285         dn_rec_t        *dnp, dn, ndn;
1286         dsvc_dnet_t     *pnd;
1287         struct in_addr  ip;
1288         int             err = 0;
1289         DHCP_MSG_CATEGORIES     log;
1290         dn_rec_list_t   *dncp, *dnlp = NULL;
1291         uint32_t        query;
1292         char            ipb[INET_ADDRSTRLEN];
1293         char            clnt_msg[DHCP_MAX_OPT_SIZE];
1294 
1295         pnd = pcd->pnd;
1296 
1297         if (type == DECLINE) {
1298                 if (plp->opts[CD_REQUESTED_IP_ADDR] &&
1299                     plp->opts[CD_REQUESTED_IP_ADDR]->len ==
1300                     sizeof (struct in_addr)) {
1301                         (void) memcpy((char *)&ip,
1302                             plp->opts[CD_REQUESTED_IP_ADDR]->value,
1303                             sizeof (struct in_addr));
1304                 }
1305         } else
1306                 ip.s_addr = plp->pkt->ciaddr.s_addr;
1307 
1308         (void) inet_ntop(AF_INET, &ip, ipb, sizeof (ipb));
1309 
1310         /* Look for a matching IP address and Client ID */
1311 
1312         DSVC_QINIT(query);
1313         DSVC_QEQ(query, DN_QCID|DN_QCIP);
1314         (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
1315         dn.dn_cid_len = pcd->cid_len;
1316         dn.dn_cip.s_addr = ntohl(ip.s_addr);
1317 
1318         dncp = NULL;
1319         dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query, -1,
1320             &dn, (void **)&dncp, S_CID);
1321         assert(dncp == NULL);
1322 
1323         if (dnlp == NULL) {
1324                 if (verbose) {
1325                         if (type == DECLINE) {
1326                                 fmtp = "Unregistered client: %1$s is "
1327                                     "DECLINEing address: %2$s.\n";
1328                         } else {
1329                                 fmtp = "Unregistered client: %1$s is "
1330                                     "RELEASEing address: %2$s.\n";
1331                         }
1332                         dhcpmsg(LOG_INFO, fmtp, pcd->cidbuf, ipb);
1333                 }
1334                 return;
1335         }
1336 
1337         dnp = dnlp->dnl_rec;
1338         ndn = *dnp; /* struct copy */
1339 
1340         /* If the entry is not one of ours, then give up. */
1341         if (match_ownerip(htonl(ndn.dn_sip.s_addr)) == NULL) {
1342                 if (verbose) {
1343                         if (type == DECLINE) {
1344                                 fmtp = "Client: %1$s is DECLINEing: "
1345                                     "%2$s not owned by this server.\n";
1346                         } else {
1347                                 fmtp = "Client: %1$s is RELEASEing: "
1348                                     "%2$s not owned by this server.\n";
1349                         }
1350                         dhcpmsg(LOG_INFO, fmtp, pcd->cidbuf, ipb);
1351                 }
1352                 goto leave_dec_rel;
1353         }
1354 
1355         if (type == DECLINE) {
1356                 log = L_DECLINE;
1357                 dhcpmsg(LOG_ERR, "Client: %1$s DECLINED address: %2$s.\n",
1358                     pcd->cidbuf, ipb);
1359                 if (plp->opts[CD_MESSAGE]) {
1360                         dhcpmsg(LOG_ERR, "DECLINE: client message: %s\n",
1361                             disp_clnt_msg(plp, clnt_msg, sizeof (clnt_msg)));
1362                 }
1363                 ndn.dn_flags |= DN_FUNUSABLE;
1364         } else {
1365                 log = L_RELEASE;
1366                 if (ndn.dn_flags & DN_FMANUAL) {
1367                         dhcpmsg(LOG_ERR,
1368                             "Client: %1$s is trying to RELEASE manual "
1369                             "address: %2$s\n", pcd->cidbuf, ipb);
1370                         goto leave_dec_rel;
1371                 }
1372                 if (verbose) {
1373                         dhcpmsg(LOG_INFO,
1374                             "Client: %1$s RELEASED address: %2$s\n",
1375                             pcd->cidbuf, ipb);
1376                         if (plp->opts[CD_MESSAGE]) {
1377                                 dhcpmsg(LOG_INFO,
1378                                     "RELEASE: client message: %s\n",
1379                                     disp_clnt_msg(plp, clnt_msg,
1380                                     sizeof (clnt_msg)));
1381                         }
1382                 }
1383         }
1384 
1385         /* Clear out the cid and lease fields */
1386         if (!(ndn.dn_flags & DN_FMANUAL)) {
1387                 ndn.dn_cid[0] = '\0';
1388                 ndn.dn_cid_len = 1;
1389                 ndn.dn_lease = (lease_t)0;
1390         }
1391 
1392         /* Ignore write errors. */
1393         err = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
1394         if (err != DSVC_SUCCESS) {
1395                 dhcpmsg(LOG_NOTICE,
1396                     "%1$s: ERROR modifying database: %2$s for client %3$s\n",
1397                     log == L_RELEASE ? "RELEASE" : "DECLINE",
1398                     dhcpsvc_errmsg(err), ipb);
1399         } else {
1400                 if (type == RELEASE) {
1401                         /*
1402                          * performance: save select_offer() lots of work by
1403                          * caching this perfectly good ip address in freerec.
1404                          */
1405                         *(dnlp->dnl_rec) = ndn; /* struct copy */
1406                         add_dnet_cache(pnd, dnlp);
1407                         dnlp = NULL;
1408                 }
1409         }
1410 
1411         logtrans(P_DHCP, log, ndn.dn_lease, ip, server_ip, plp);
1412 
1413 leave_dec_rel:
1414 
1415         if (dnlp != NULL)
1416                 dhcp_free_dd_list(pnd->dh, dnlp);
1417 }
1418 
1419 /*
1420  * Responding to an INFORM message.
1421  *
1422  * INFORM messages are received from clients that already have their network
1423  * parameters (such as IP address and subnet mask), but wish to receive
1424  * other configuration parameters. The server will not check for an existing
1425  * lease as clients may have obtained their network parameters by some
1426  * means other than DHCP. Similarly, the DHCPACK generated in response to
1427  * the INFORM message will not include lease time information. All other
1428  * configuration parameters are returned.
1429  */
1430 static void
1431 dhcp_inform(dsvc_clnt_t *pcd, PKT_LIST *plp)
1432 {
1433         uint_t          replen;
1434         int             used_pkt_len;
1435         PKT             *rep_pktp = NULL;
1436         uchar_t         *optp;
1437         ENCODE          *ecp, *vecp, *class_ecp, *class_vecp,
1438             *cid_ecp, *cid_vecp, *net_ecp, *net_vecp;
1439         MACRO           *net_mp, *class_mp, *cid_mp;
1440         dsvc_dnet_t     *pnd;
1441         char            *class_id;
1442         char            class_idbuf[DSYM_CLASS_SIZE];
1443         IF              *ifp = pcd->ifp;
1444 
1445         pnd = pcd->pnd;
1446         class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
1447 
1448         /*
1449          * Macros are evaluated this way: First apply parameters from
1450          * a client class macro (if present), then apply those from the
1451          * network macro (if present),  and finally apply those from a
1452          * client id macro (if present).
1453          */
1454         ecp = vecp = NULL;
1455         net_vecp = net_ecp = NULL;
1456         class_vecp = class_ecp = NULL;
1457         cid_vecp = cid_ecp = NULL;
1458 
1459         if (!no_dhcptab) {
1460                 open_macros();
1461                 if ((net_mp = get_macro(pnd->network)) != NULL)
1462                         net_ecp = net_mp->head;
1463                 if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
1464                         cid_ecp = cid_mp->head;
1465                 if (class_id != NULL) {
1466                         if ((class_mp = get_macro(class_id)) != NULL) {
1467                                 class_vecp = vendor_encodes(class_mp,
1468                                     class_id);
1469                                 class_ecp = class_mp->head;
1470                         }
1471                         if (net_mp != NULL)
1472                                 net_vecp = vendor_encodes(net_mp, class_id);
1473                         if (cid_mp != NULL)
1474                                 cid_vecp = vendor_encodes(cid_mp, class_id);
1475                         vecp = combine_encodes(class_vecp, net_vecp,
1476                             ENC_COPY);
1477                         vecp = combine_encodes(vecp, cid_vecp, ENC_DONT_COPY);
1478                 }
1479 
1480                 ecp = combine_encodes(class_ecp, net_ecp, ENC_COPY);
1481                 ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
1482         }
1483 
1484         /* First get a generic reply packet. */
1485         rep_pktp = gen_reply_pkt(pcd, plp, ACK, &replen, &optp, &ifp->addr);
1486 
1487         /*
1488          * Client is requesting specific options. let's try and ensure it
1489          * gets what it wants, if at all possible.
1490          */
1491         if (plp->opts[CD_REQUEST_LIST] != NULL)
1492                 add_request_list(ifp, plp, &ecp, &plp->pkt->ciaddr);
1493 
1494         /*
1495          * Explicitly set the ciaddr to be that which the client gave
1496          * us.
1497          */
1498         rep_pktp->ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
1499 
1500         /*
1501          * Now load all the asked for / configured options. DON'T send
1502          * any lease time info!
1503          */
1504         used_pkt_len = load_options(DHCP_DHCP_CLNT, plp, rep_pktp, replen, optp,
1505             ecp, vecp);
1506 
1507         free_encode_list(ecp);
1508         free_encode_list(vecp);
1509         if (!no_dhcptab)
1510                 close_macros();
1511 
1512         if (used_pkt_len < sizeof (PKT))
1513                 used_pkt_len = sizeof (PKT);
1514 
1515         (void) send_reply(ifp, rep_pktp, used_pkt_len, &plp->pkt->ciaddr);
1516 
1517         logtrans(P_DHCP, L_INFORM, 0, plp->pkt->ciaddr, server_ip, plp);
1518 
1519 leave_inform:
1520         if (rep_pktp != NULL)
1521                 free(rep_pktp);
1522 }
1523 
1524 static char *
1525 disp_clnt_msg(PKT_LIST *plp, char *bufp, int len)
1526 {
1527         uchar_t tlen;
1528 
1529         bufp[0] = '\0'; /* null string */
1530 
1531         if (plp && plp->opts[CD_MESSAGE]) {
1532                 tlen = ((uchar_t)len < plp->opts[CD_MESSAGE]->len) ?
1533                     (len - 1) : plp->opts[CD_MESSAGE]->len;
1534                 (void) memcpy(bufp, plp->opts[CD_MESSAGE]->value, tlen);
1535                 bufp[tlen] = '\0';
1536         }
1537         return (bufp);
1538 }
1539 
1540 /*
1541  * serverip expected in host order
1542  */
1543 static PKT *
1544 gen_reply_pkt(dsvc_clnt_t *pcd, PKT_LIST *plp, int type, uint_t *len,
1545     uchar_t **optpp, struct in_addr *serverip)
1546 {
1547         PKT             *reply_pktp;
1548         uint16_t        plen;
1549 
1550         /*
1551          * We need to determine the packet size. Perhaps the client has told
1552          * us?
1553          */
1554         if (plp->opts[CD_MAX_DHCP_SIZE]) {
1555                 if (plp->opts[CD_MAX_DHCP_SIZE]->len != sizeof (uint16_t)) {
1556                         dhcpmsg(LOG_ERR, "Garbled MAX DHCP message size option "
1557                             "from\nclient: '%1$s'. Len is %2$d, when it should "
1558                             "be %3$d. Defaulting to %4$d.\n",
1559                             pcd->cidbuf,
1560                             plp->opts[CD_MAX_DHCP_SIZE]->len,
1561                             sizeof (uint16_t), DHCP_DEF_MAX_SIZE);
1562                         plen = DHCP_DEF_MAX_SIZE;
1563                 } else {
1564                         (void) memcpy(&plen, plp->opts[CD_MAX_DHCP_SIZE]->value,
1565                             sizeof (uint16_t));
1566                         plen = ntohs(plen);
1567                         if (plen < DHCP_DEF_MAX_SIZE)
1568                                 plen = DHCP_DEF_MAX_SIZE;
1569                 }
1570         } else {
1571                 /*
1572                  * Define size to be a fixed length. Too hard to add up all
1573                  * possible class id, macro, and hostname/lease time options
1574                  * without doing just about as much work as constructing the
1575                  * whole reply packet.
1576                  */
1577                 plen = DHCP_MAX_REPLY_SIZE;
1578         }
1579 
1580         /* Generate a generically initialized BOOTP packet */
1581         reply_pktp = gen_bootp_pkt(plen, plp->pkt);
1582 
1583         reply_pktp->op = BOOTREPLY;
1584         *optpp = reply_pktp->options;
1585 
1586         /*
1587          * Set pkt type.
1588          */
1589         *(*optpp)++ = (uchar_t)CD_DHCP_TYPE;
1590         *(*optpp)++ = (uchar_t)1;
1591         *(*optpp)++ = (uchar_t)type;
1592 
1593         /*
1594          * All reply packets have server id set.
1595          */
1596         *(*optpp)++ = (uchar_t)CD_SERVER_ID;
1597         *(*optpp)++ = (uchar_t)4;
1598 #if     defined(_LITTLE_ENDIAN)
1599         *(*optpp)++ = (uchar_t)(serverip->s_addr & 0xff);
1600         *(*optpp)++ = (uchar_t)((serverip->s_addr >>  8) & 0xff);
1601         *(*optpp)++ = (uchar_t)((serverip->s_addr >> 16) & 0xff);
1602         *(*optpp)++ = (uchar_t)((serverip->s_addr >> 24) & 0xff);
1603 #else
1604         *(*optpp)++ = (uchar_t)((serverip->s_addr >> 24) & 0xff);
1605         *(*optpp)++ = (uchar_t)((serverip->s_addr >> 16) & 0xff);
1606         *(*optpp)++ = (uchar_t)((serverip->s_addr >>  8) & 0xff);
1607         *(*optpp)++ = (uchar_t)(serverip->s_addr & 0xff);
1608 #endif  /* _LITTLE_ENDIAN */
1609 
1610         *len = plen;
1611         return (reply_pktp);
1612 }
1613 
1614 /*
1615  * If the client requests it, and either it isn't currently configured
1616  * or hasn't already been added, provide the option now.  Will also work
1617  * for NULL ENCODE lists, but initializing them to point to the requested
1618  * options.
1619  *
1620  * If nsswitch contains host name services which hang, big problems occur
1621  * with dhcp server, since the main thread hangs waiting for that name
1622  * service's timeout.
1623  *
1624  * NOTE: this function should be called only after all other parameter
1625  * merges have taken place (combine_encode).
1626  */
1627 static void
1628 add_request_list(IF *ifp, PKT_LIST *plp, ENCODE **ecp, struct in_addr *ip)
1629 {
1630         ENCODE  *ep, *ifecp, *end_ecp = NULL;
1631         struct hostent  h, *hp;
1632         char hbuf[NSS_BUFLEN_HOSTS];
1633         int herrno;
1634 
1635         /* Find the end. */
1636         if (*ecp) {
1637                 for (ep = *ecp; ep->next; ep = ep->next)
1638                         /* null */;
1639                 end_ecp = ep;
1640         }
1641 
1642         /* HOSTNAME */
1643         if (is_option_requested(plp, CD_HOSTNAME) &&
1644             (find_encode(*ecp, DSYM_STANDARD, CD_HOSTNAME) == NULL) &&
1645             (find_encode(*ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) == NULL)) {
1646                 hp = gethostbyaddr_r((char *)ip, sizeof (struct in_addr),
1647                     AF_INET, &h, hbuf, sizeof (hbuf), &herrno);
1648                 if (hp != NULL) {
1649                         if (end_ecp) {
1650                                 end_ecp->next = make_encode(DSYM_STANDARD,
1651                                     CD_HOSTNAME, strlen(hp->h_name),
1652                                     hp->h_name, ENC_COPY);
1653                                 end_ecp = end_ecp->next;
1654                         } else {
1655                                 end_ecp = make_encode(DSYM_STANDARD,
1656                                     CD_HOSTNAME, strlen(hp->h_name),
1657                                     hp->h_name, ENC_COPY);
1658                         }
1659                 }
1660         }
1661 
1662         /*
1663          * all bets off for the following if thru a relay agent.
1664          */
1665         if (plp->pkt->giaddr.s_addr != 0L)
1666                 return;
1667 
1668         /* SUBNET MASK */
1669         if (is_option_requested(plp, CD_SUBNETMASK) && find_encode(*ecp,
1670             DSYM_STANDARD, CD_SUBNETMASK) == NULL) {
1671                 ifecp = find_encode(ifp->ecp, DSYM_STANDARD, CD_SUBNETMASK);
1672                 if (end_ecp) {
1673                         end_ecp->next = dup_encode(ifecp);
1674                         end_ecp = end_ecp->next;
1675                 } else
1676                         end_ecp = dup_encode(ifecp);
1677         }
1678 
1679         /* BROADCAST ADDRESS */
1680         if (is_option_requested(plp, CD_BROADCASTADDR) && find_encode(*ecp,
1681             DSYM_STANDARD, CD_BROADCASTADDR) == NULL) {
1682                 ifecp = find_encode(ifp->ecp, DSYM_STANDARD,
1683                     CD_BROADCASTADDR);
1684                 if (end_ecp) {
1685                         end_ecp->next = dup_encode(ifecp);
1686                         end_ecp = end_ecp->next;
1687                 } else
1688                         end_ecp = dup_encode(ifecp);
1689         }
1690 
1691         /* IP MTU */
1692         if (is_option_requested(plp, CD_MTU) && find_encode(*ecp,
1693             DSYM_STANDARD, CD_MTU) == NULL) {
1694                 ifecp = find_encode(ifp->ecp, DSYM_STANDARD, CD_MTU);
1695                 if (end_ecp) {
1696                         end_ecp->next = dup_encode(ifecp);
1697                         end_ecp = end_ecp->next;
1698                 } else
1699                         end_ecp = dup_encode(ifecp);
1700         }
1701 
1702         if (*ecp == NULL)
1703                 *ecp = end_ecp;
1704 }
1705 
1706 /*
1707  * Is a specific option requested? Returns True if so, False otherwise.
1708  */
1709 static int
1710 is_option_requested(PKT_LIST *plp, ushort_t code)
1711 {
1712         uchar_t c, *tp;
1713         DHCP_OPT *cp = plp->opts[CD_REQUEST_LIST];
1714 
1715         for (c = 0, tp = (uchar_t *)cp->value; c < cp->len; c++, tp++) {
1716                 if (*tp == (uchar_t)code)
1717                         return (B_TRUE);
1718         }
1719         return (B_FALSE);
1720 }
1721 
1722 /*
1723  * Locates lease option, if possible, otherwise allocates an encode and
1724  * appends it to the end. Changes current lease setting.
1725  *
1726  * TODO: ugh. We don't address the case where the Lease time changes, but
1727  * T1 and T2 don't. We don't want T1 or T2 to be greater than the lease
1728  * time! Perhaps T1 and T2 should be a percentage of lease time... Later..
1729  */
1730 static void
1731 set_lease_option(ENCODE **ecpp, lease_t lease)
1732 {
1733         ENCODE  *ep, *prev_ep, *lease_ep;
1734 
1735         lease = htonl(lease);
1736 
1737         if (ecpp != NULL && (lease_ep = find_encode(*ecpp, DSYM_STANDARD,
1738             CD_LEASE_TIME)) != NULL && lease_ep->len == sizeof (lease_t)) {
1739                 (void) memcpy(lease_ep->data, (void *)&lease, sizeof (lease_t));
1740         } else {
1741                 if (*ecpp != NULL) {
1742                         for (prev_ep = ep = *ecpp; ep != NULL; ep = ep->next)
1743                                 prev_ep = ep;
1744                         prev_ep->next = make_encode(DSYM_STANDARD,
1745                             CD_LEASE_TIME, sizeof (lease_t), &lease, ENC_COPY);
1746                 } else {
1747                         *ecpp = make_encode(DSYM_STANDARD, CD_LEASE_TIME,
1748                             sizeof (lease_t), &lease, ENC_COPY);
1749                         (*ecpp)->next = NULL;
1750                 }
1751         }
1752 }
1753 /*
1754  * Sets appropriate option in passed ENCODE list for lease. Returns
1755  * calculated relative lease time.
1756  */
1757 static int
1758 config_lease(PKT_LIST *plp, dn_rec_t *dnp, ENCODE **ecpp, lease_t oldlease,
1759     boolean_t negot)
1760 {
1761         lease_t         newlease, rel_current;
1762         ENCODE          *lease_ecp;
1763 
1764         if (ecpp != NULL && (lease_ecp = find_encode(*ecpp, DSYM_STANDARD,
1765             CD_LEASE_TIME)) != NULL && lease_ecp->len == sizeof (lease_t)) {
1766                 (void) memcpy((void *)&rel_current, lease_ecp->data,
1767                     sizeof (lease_t));
1768                 rel_current = htonl(rel_current);
1769         } else
1770                 rel_current = (lease_t)DEFAULT_LEASE;
1771 
1772         if (dnp->dn_flags & DN_FAUTOMATIC || !negot) {
1773                 if (dnp->dn_flags & DN_FAUTOMATIC)
1774                         newlease = ntohl(DHCP_PERM);
1775                 else {
1776                         /* sorry! */
1777                         if (oldlease)
1778                                 newlease = oldlease;
1779                         else
1780                                 newlease = rel_current;
1781                 }
1782         } else {
1783                 /*
1784                  * lease is not automatic and is negotiable!
1785                  * If the dhcp-network lease is bigger than the current
1786                  * policy value, then let the client benefit from this
1787                  * situation.
1788                  */
1789                 if (oldlease > rel_current)
1790                         rel_current = oldlease;
1791 
1792                 if (plp->opts[CD_LEASE_TIME] &&
1793                     plp->opts[CD_LEASE_TIME]->len == sizeof (lease_t)) {
1794                         /*
1795                          * Client is requesting a lease renegotiation.
1796                          */
1797                         (void) memcpy((void *)&newlease,
1798                             plp->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
1799 
1800                         newlease = ntohl(newlease);
1801 
1802                         /*
1803                          * Note that this comparison handles permanent
1804                          * leases as well. Limit lease to configured value.
1805                          */
1806                         if (newlease > rel_current)
1807                                 newlease = rel_current;
1808                 } else
1809                         newlease = rel_current;
1810         }
1811 
1812         set_lease_option(ecpp, newlease);
1813 
1814         return (newlease);
1815 }
1816 
1817 /*
1818  * If a packet has the classid set, return the value, else return null.
1819  */
1820 char *
1821 get_class_id(PKT_LIST *plp, char *bufp, int len)
1822 {
1823         uchar_t *ucp, ulen;
1824         char    *retp;
1825 
1826         if (plp->opts[CD_CLASS_ID]) {
1827                 /*
1828                  * If the class id is set, see if there is a macro by this
1829                  * name. If so, then "OR" the ENCODE settings of the class
1830                  * macro with the packet macro. Settings in the packet macro
1831                  * OVERRIDE settings in the class macro.
1832                  */
1833                 ucp = plp->opts[CD_CLASS_ID]->value;
1834                 ulen = plp->opts[CD_CLASS_ID]->len;
1835                 if (len < ulen)
1836                         ulen = len;
1837                 (void) memcpy(bufp, ucp, ulen);
1838                 bufp[ulen] = '\0';
1839 
1840                 retp = bufp;
1841         } else
1842                 retp = NULL;
1843 
1844         return (retp);
1845 }
1846 
1847 /*
1848  * Checks whether an offer ip address in the per net inet address
1849  * cache.
1850  *
1851  * pnd - per net structure
1852  * reservep - address to check, in network order.
1853  */
1854 static boolean_t
1855 check_offer(dsvc_dnet_t *pnd, struct in_addr *reservep)
1856 {
1857         dsvc_clnt_t     tpcd;
1858 
1859         tpcd.off_ip.s_addr = reservep->s_addr;
1860 
1861         return (hash_Lookup(pnd->itable, reservep, sizeof (struct in_addr),
1862             clnt_netcmp, &tpcd, B_FALSE) == NULL ? B_TRUE : B_FALSE);
1863 }
1864 
1865 /*
1866  * Adds or updates an offer to the per client data structure. The client
1867  * struct is hashed by clientid into the per net ctable hash table, and
1868  * by offer address in the itable hash table, which is used to reserve the
1869  * ip address. Lease time is expected to be set by caller.
1870  * Will update existing OFFER if already provided.
1871  *
1872  * This implementation does not consider the fact that an offer can be
1873  * sent out via more than one interface, so dsvc_clnt_t.ifp should
1874  * really be a list or the itable's entries should be lists of
1875  * dsvc_clnt_ts. As long as we don't change this, we assume that the
1876  * client will eventually REQUEST the last offer we have sent out
1877  * because when we receive the same DISCOVER via multiple interfaces,
1878  * we always update the same offer cache entry so its ifp is always
1879  * the interface we received the last DISCOVER on.
1880  *
1881  * pcd - per client data struct.
1882  * dnlp - pointer to pointer to current container entry. Performance: caching
1883  * reduces datastore activity, structure copying.
1884  * nlease - new lease time.
1885  * reservep - new offer address (expected in network order).
1886  * purge_cache - Multithreading: avoid redundant cache purging in
1887  * select_offer().
1888  */
1889 boolean_t
1890 update_offer(dsvc_clnt_t *pcd, dn_rec_list_t **dnlp, lease_t nlease,
1891         struct in_addr *reservep, boolean_t purge_cache)
1892 {
1893         char            ntoab[INET_ADDRSTRLEN];
1894         boolean_t       insert = B_TRUE;
1895         boolean_t       update = B_FALSE;
1896         boolean_t       offer = B_FALSE;
1897         dsvc_dnet_t     *pnd = pcd->pnd;
1898         IF              *ifp = pcd->ifp;
1899         dn_rec_t        *dnp = NULL;
1900         struct in_addr  off_ip;
1901 
1902         /* Save the original datastore record. */
1903         if (dnlp != NULL && *dnlp != NULL) {
1904                 if (pcd->dnlp != NULL && pcd->dnlp != *dnlp)
1905                         dhcp_free_dd_list(pnd->dh, pcd->dnlp);
1906                 pcd->dnlp = *dnlp;
1907         }
1908         if (pcd->dnlp != NULL)
1909                 dnp = pcd->dnlp->dnl_rec;
1910 
1911         /* Determine the offer address. */
1912         if (reservep == NULL && dnp != NULL)
1913                 off_ip.s_addr = htonl(dnp->dn_cip.s_addr);
1914         else if (reservep != NULL)
1915                 off_ip.s_addr = reservep->s_addr;
1916         else {
1917                 dhcpmsg(LOG_DEBUG,
1918                     "Neither offer IP nor IP to reserve present\n");
1919                 assert(B_FALSE);
1920                 return (B_FALSE);
1921         }
1922 
1923         /* If updating, release the old offer address. */
1924         if (pcd->off_ip.s_addr == htonl(INADDR_ANY)) {
1925                 offer = B_TRUE;
1926         } else {
1927                 update = B_TRUE;
1928                 if (pcd->off_ip.s_addr != off_ip.s_addr) {
1929                         purge_offer(pcd, B_FALSE, purge_cache);
1930                         offer = B_TRUE;
1931                 } else
1932                         insert = B_FALSE;
1933         }
1934 
1935         if (nlease != 0)
1936                 pcd->lease = nlease;
1937 
1938         /* Prepare to insert pcd into the offer hash table. */
1939         pcd->mtime = reinit_time;
1940 
1941         pcd->off_ip.s_addr = off_ip.s_addr;
1942 
1943         assert(pcd->off_ip.s_addr != htonl(INADDR_ANY));
1944 
1945         if (insert) {
1946                 if ((pcd->ihand = hash_Insert(pnd->itable, &pcd->off_ip,
1947                     sizeof (struct in_addr), clnt_netcmp, pcd, pcd)) == NULL) {
1948                         if (reservep == NULL) {
1949                                 dhcpmsg(LOG_WARNING, "Duplicate offer of %1$s "
1950                                     "to client: %2$s\n",
1951                                     inet_ntop(AF_INET, &pcd->off_ip, ntoab,
1952                                     sizeof (ntoab)), pcd->cidbuf);
1953                         }
1954                         pcd->off_ip.s_addr = htonl(INADDR_ANY);
1955                         dhcp_free_dd_list(pnd->dh, pcd->dnlp);
1956                         if (dnlp != NULL && *dnlp != NULL &&
1957                             pcd->dnlp == *dnlp) {
1958                                 *dnlp = NULL;
1959                         }
1960                         pcd->dnlp = NULL;
1961                         return (B_FALSE);
1962                 }
1963         } else
1964                 hash_Dtime(pcd->ihand, time(NULL) + off_secs);
1965 
1966         if (offer) {
1967                 (void) mutex_lock(&ifp->ifp_mtx);
1968                 ifp->offers++;
1969                 (void) mutex_unlock(&ifp->ifp_mtx);
1970         }
1971 
1972         if (debug) {
1973                 if (reservep != NULL) {
1974                         dhcpmsg(LOG_INFO, "Reserved offer: %s\n",
1975                             inet_ntop(AF_INET, &pcd->off_ip,
1976                             ntoab, sizeof (ntoab)));
1977                 } else if (update) {
1978                         dhcpmsg(LOG_INFO, "Updated offer: %s\n",
1979                             inet_ntop(AF_INET, &pcd->off_ip,
1980                             ntoab, sizeof (ntoab)));
1981                 } else {
1982                         dhcpmsg(LOG_INFO, "Added offer: %s\n",
1983                             inet_ntop(AF_INET, &pcd->off_ip,
1984                             ntoab, sizeof (ntoab)));
1985                 }
1986         }
1987         return (B_TRUE);
1988 }
1989 
1990 /*
1991  * Deletes an offer.
1992  *
1993  * pcd - per client struct
1994  * expired - has offer expired, or been purged
1995  * purge_cache - Multi-threading: avoid redundant cache purging in
1996  * select_offer().
1997  */
1998 void
1999 purge_offer(dsvc_clnt_t *pcd, boolean_t expired, boolean_t purge_cache)
2000 {
2001         char            ntoab[INET_ADDRSTRLEN];
2002         dsvc_dnet_t     *pnd = pcd->pnd;
2003         IF              *ifp = pcd->ifp;
2004 
2005         if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
2006                 if (debug) {
2007                         if (expired == B_TRUE)
2008                                 dhcpmsg(LOG_INFO, "Freeing offer: %s\n",
2009                                     inet_ntop(AF_INET, &pcd->off_ip,
2010                                     ntoab, sizeof (ntoab)));
2011                         else
2012                                 dhcpmsg(LOG_INFO, "Purging offer: %s\n",
2013                                     inet_ntop(AF_INET, &pcd->off_ip,
2014                                     ntoab, sizeof (ntoab)));
2015                 }
2016 
2017                 /*
2018                  * The offer cache ensures that recently granted offer
2019                  * addresses won't attempt to be reused from the dnet
2020                  * caches. When purging one of these offers, be sure to
2021                  * remove the associated record from the dnet cache,
2022                  * to avoid collisions.
2023                  */
2024                 if (pcd->state == ACK && pcd->dnlp != NULL) {
2025                         if (purge_cache)
2026                                 purge_dnet_cache(pnd, pcd->dnlp->dnl_rec);
2027                         dhcp_free_dd_list(pnd->dh, pcd->dnlp);
2028                         pcd->dnlp = NULL;
2029                 }
2030 
2031 
2032                 /* Prepare to delete pcd from the offer hash table. */
2033                 (void) hash_Delete(pnd->itable, &pcd->off_ip,
2034                     sizeof (struct in_addr), clnt_netcmp, pcd, NULL);
2035 
2036                 pcd->off_ip.s_addr = htonl(INADDR_ANY);
2037 
2038                 (void) mutex_lock(&ifp->ifp_mtx);
2039                 if (ifp->offers > 0)
2040                         ifp->offers--;
2041                 if (expired)
2042                         ifp->expired++;
2043                 (void) mutex_unlock(&ifp->ifp_mtx);
2044         }
2045 }
2046 
2047 /*
2048  * Allocate a new entry in the dhcp-network db for the cid, taking into
2049  * account requested IP address. Verify address.
2050  *
2051  * The network portion of the address doesn't have to be the same as ours,
2052  * just owned by us. We also make sure we don't select a record which is
2053  * currently in use, by reserving the address in the offer cache. Database
2054  * records are cached up to the D_OFFER lifetime to improve performance.
2055  *
2056  * Returns:     1 if there's a usable entry for the client, 0
2057  *              if not. Places the record in the dn_rec_list_t structure
2058  *              pointer handed in.
2059  */
2060 /*ARGSUSED*/
2061 boolean_t
2062 select_offer(dsvc_dnet_t *pnd, PKT_LIST *plp, dsvc_clnt_t *pcd,
2063         dn_rec_list_t **dnlpp)
2064 {
2065         struct in_addr  req_ip, *req_ipp = &req_ip, tip;
2066         boolean_t       found = B_FALSE;
2067         time_t          now;
2068         dn_rec_t        dn, *dnp;
2069         dn_rec_list_t   *dncp, *dnsp, *tlp;
2070         int             nrecords;
2071         uint32_t        query;
2072         int             retry;
2073         boolean_t       io_done, is_bootp;
2074         struct in_addr  *oip;
2075 
2076         if (plp->opts[CD_DHCP_TYPE] == NULL)
2077                 is_bootp = B_TRUE;
2078         else
2079                 is_bootp = B_FALSE;
2080 
2081         *dnlpp = NULL;
2082         if (!is_bootp) {
2083                 /*
2084                  * Is the DHCP client requesting a specific address? Is so, and
2085                  * we can satisfy him, do so.
2086                  */
2087                 if (plp->opts[CD_REQUESTED_IP_ADDR] != NULL) {
2088                         (void) memcpy((void *)&req_ip,
2089                             plp->opts[CD_REQUESTED_IP_ADDR]->value,
2090                             sizeof (struct in_addr));
2091 
2092                         if ((req_ip.s_addr & pnd->subnet.s_addr) ==
2093                             pnd->net.s_addr)
2094                                 found = B_TRUE;
2095 
2096                 } else if (plp->opts[CD_HOSTNAME] != NULL) {
2097                         char            hname[MAXHOSTNAMELEN + 1];
2098                         int             hlen;
2099 
2100                         /* turn hostname option into a string */
2101                         hlen = plp->opts[CD_HOSTNAME]->len;
2102                         hlen = MIN(hlen, MAXHOSTNAMELEN);
2103                         (void) memcpy(hname, plp->opts[CD_HOSTNAME]->value,
2104                             hlen);
2105                         hname[hlen] = '\0';
2106 
2107                         dhcpmsg(LOG_DEBUG,
2108                             "select_offer:  hostname request for %s\n", hname);
2109                         if (name_avail(hname, pcd, plp, dnlpp, NULL,
2110                             &req_ipp) && req_ipp) {
2111                                 if ((req_ip.s_addr & pnd->subnet.s_addr) ==
2112                                     pnd->net.s_addr) {
2113                                         found = B_TRUE;
2114                                 } else if (*dnlpp != NULL) {
2115                                         dhcp_free_dd_list(pnd->dh, *dnlpp);
2116                                         *dnlpp = NULL;
2117                                 }
2118                                 dhcpmsg(LOG_DEBUG, "select_offer:  hostname %s "
2119                                     "available, req_ip %x\n", hname,
2120                                     ntohl(req_ip.s_addr));
2121                         } else
2122                                 dhcpmsg(LOG_DEBUG, "select_offer:  name_avail "
2123                                     "false or no address for %s\n", hname);
2124                 }
2125         }
2126 
2127         /*
2128          *  Check the offer list and table entry.
2129          */
2130         if (found && *dnlpp == NULL)
2131                 found = addr_avail(pnd, pcd, dnlpp, req_ip, B_FALSE);
2132 
2133         if (!found) {
2134                 /*
2135                  * Try to find a free entry. Look for an AVAILABLE entry
2136                  * (cid == 0x00, len == 1), owned by us.
2137                  * The outer loop runs through the server ips owned by us.
2138                  *
2139                  * Multi-threading: to improve performance, the following
2140                  * algorithm coordinates accesses to the underlying table,
2141                  * so only one thread is initiating lookups per network.
2142                  * This is crucial, as lookup operations are expensive,
2143                  * and not sufficiently malleable to allow partitioned
2144                  * lookups (e.g. all that can be asked for are n free or
2145                  * server-owned entries, multiple threads will retrieve
2146                  * the same records).
2147                  *
2148                  * The three iterations through the inner loop attempt to use
2149                  *
2150                  * 1) the next cached entry
2151                  * 2) all cached entries
2152                  * 3) all free or per-server entries in the underlying table
2153                  *
2154                  * Since many threads are consuming the cached entries,
2155                  * any thread may find itself in the role of having to
2156                  * refresh the cache. We always read at least enough
2157                  * entries to satisfy all current threads. Reading all
2158                  * records is prohibitively expensive, and should only
2159                  * be done as a last resort.
2160                  *
2161                  * As always,  to better distribute garbage
2162                  * collection and data structure aging tasks, each
2163                  * thread must actively implement policy, checking
2164                  * for offer expiration (which invalidates the cache).
2165                  */
2166 
2167                 for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
2168                         /*
2169                          * Initialize query.
2170                          */
2171                         DSVC_QINIT(query);
2172                         DSVC_QEQ(query, DN_QCID|DN_QSIP);
2173                         dn.dn_cid[0] = '\0';
2174                         dn.dn_cid_len = 1;
2175                         dn.dn_sip.s_addr = ntohl(oip->s_addr);
2176 
2177                         /*
2178                          * Decide whether a bootp record is required.
2179                          */
2180                         dn.dn_flags = 0;
2181                         DSVC_QEQ(query, DN_QFBOOTP_ONLY);
2182                         if (is_bootp)
2183                                 dn.dn_flags = DN_FBOOTP_ONLY;
2184 
2185                         /*
2186                          * These flags are used counter-intuitively.
2187                          * This says that the setting of the bit
2188                          * (off) in the dn.dn_flags matches the
2189                          * setting in the record (off).
2190                          */
2191                         DSVC_QEQ(query, DN_QFUNUSABLE|DN_QFMANUAL);
2192 
2193                         for (retry = 0; !found && retry < 3; retry++) {
2194                                 now = time(NULL);
2195                                 (void) mutex_lock(&pnd->thr_mtx);
2196                                 nrecords = pnd->nthreads < DHCP_MIN_RECORDS ?
2197                                     DHCP_MIN_RECORDS : pnd->nthreads;
2198                                 (void) mutex_unlock(&pnd->thr_mtx);
2199 
2200                                 /*
2201                                  * Purge cached records when expired or database
2202                                  * re-read.
2203                                  */
2204 
2205                                 (void) mutex_lock(&pnd->free_mtx);
2206                                 dncp = pnd->freerec;
2207                                 if (dncp != NULL &&
2208                                     PND_FREE_TIMEOUT(pnd, now)) {
2209                                 pnd->freerec = NULL;
2210                                 dhcp_free_dd_list(pnd->dh, dncp);
2211                                 dncp = NULL;
2212                                 }
2213 
2214                                 if (dncp != NULL) {
2215                                         if (retry == 0) {
2216                                                 /* Try the next cached record */
2217                                                 pnd->freerec = dncp->dnl_next;
2218                                                 dncp->dnl_next = NULL;
2219                                         } else if (retry == 1) {
2220                                                 /*
2221                                                  * Try all remaining cached
2222                                                  * records
2223                                                  */
2224                                                 pnd->freerec = NULL;
2225                                         }
2226                                 }
2227                                 if (retry > 1) {
2228                                 /* Try all possible records in datastore. */
2229                                         pnd->freerec = NULL;
2230                                         nrecords = -1;
2231                                         if (dncp != NULL) {
2232                                                 dhcp_free_dd_list(
2233                                                     pnd->dh, dncp);
2234                                                 dncp = NULL;
2235                                         }
2236                                 }
2237                                 (void) mutex_unlock(&pnd->free_mtx);
2238 
2239                                 io_done = (dncp == NULL);
2240                                 *dnlpp = dhcp_lookup_dd_classify(pcd->pnd,
2241                                     nrecords == -1 ? B_FALSE : B_TRUE, query,
2242                                     nrecords, &dn, (void **)&dncp,
2243                                     S_CID | S_FREE);
2244                                 if (*dnlpp != NULL) {
2245                                         dnp = (*dnlpp)->dnl_rec;
2246                                         tip.s_addr = htonl(dnp->dn_cip.s_addr);
2247                                         (void) update_offer(pcd, NULL, 0,
2248                                             &tip, B_TRUE);
2249                                         found = B_TRUE;
2250                                 }
2251 
2252                                 (void) mutex_lock(&pnd->free_mtx);
2253                                 if (io_done) {
2254                                         /*
2255                                          * Note time when records were read.
2256                                          */
2257                                         if (dncp != NULL) {
2258                                                 now = time(NULL);
2259                                                 pnd->free_mtime = reinit_time;
2260                                                 pnd->free_stamp = now +
2261                                                     cache_secs;
2262                                         }
2263                                 }
2264 
2265                                 /* Save any leftover records for later use. */
2266                                 if (dncp != NULL) {
2267                                         for (tlp = dncp;
2268                                             tlp != NULL && tlp->dnl_next;
2269                                             tlp = tlp->dnl_next)
2270                                                 /* null statement */;
2271                                         tlp->dnl_next = pnd->freerec;
2272                                         pnd->freerec = dncp;
2273                                 }
2274                                 (void) mutex_unlock(&pnd->free_mtx);
2275                         }
2276                 }
2277         }
2278 
2279         if (!found && !is_bootp) {
2280                 /*
2281                  * Struck out. No usable available addresses. Let's look for
2282                  * the LRU expired address. Only makes sense for dhcp
2283                  * clients. First we'll try the next record from
2284                  * the lru list (this assumes lru database search capability).
2285                  * Next we'll try all records. Finally we'll go get all
2286                  * free records.
2287                  *
2288                  * Multi-threading: to improve performance, the following
2289                  * algorithm coordinates accesses to the underlying table,
2290                  * so only one thread is initiating lookups per network.
2291                  * This is crucial, as lookup operations are expensive,
2292                  * and not sufficiently malleable to allow partitioned
2293                  * lookups (e.g. all that can be asked for are n free or
2294                  * server-owned entries, multiple threads will retrieve
2295                  * the same records).
2296                  *
2297                  * We only consider clients owned by us.
2298                  * The outer loop runs through the server ips owned by us
2299                  *
2300                  * The three iterations through the inner loop attempt to use
2301                  *
2302                  * 1) the next cached entry
2303                  * 2) all cached entries
2304                  * 3) all free or per-server entries in the underlying table
2305                  *
2306                  * Since many threads are consuming the cached entries,
2307                  * any thread may find itself in the role of having to
2308                  * refresh the cache. We always read at least enough
2309                  * entries to satisfy all current threads. Reading all
2310                  * records is prohibitively expensive, and should only
2311                  * be done as a last resort.
2312                  *
2313                  * As always,  to better distribute garbage
2314                  * collection and data structure aging tasks, each
2315                  * thread must actively implement policy, checking
2316                  * for offer expiration (which invalidates the cache).
2317                  */
2318 
2319                 for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
2320                         /*
2321                          * Initialize query.
2322                          */
2323                         DSVC_QINIT(query);
2324                         DSVC_QEQ(query, DN_QSIP);
2325                         dn.dn_sip.s_addr = ntohl(oip->s_addr);
2326 
2327                         /*
2328                          * These flags are used counter-intuitively.
2329                          * This says that the setting of the bit
2330                          * (off) in the dn.dn_flags matches the
2331                          * setting in the record (off).
2332                          */
2333                         DSVC_QEQ(query, DN_QFBOOTP_ONLY|
2334                             DN_QFMANUAL|DN_QFUNUSABLE);
2335                         dn.dn_flags = 0;
2336 
2337                         for (retry = 0; !found && retry < 3; retry++) {
2338                                 now = time(NULL);
2339                                 (void) mutex_lock(&pnd->thr_mtx);
2340                                 nrecords = pnd->nthreads < DHCP_MIN_RECORDS ?
2341                                     DHCP_MIN_RECORDS : pnd->nthreads;
2342                                 (void) mutex_unlock(&pnd->thr_mtx);
2343 
2344                                 /*
2345                                  * Purge cached records when expired or database
2346                                  * re-read.
2347                                  */
2348 
2349                                 (void) mutex_lock(&pnd->lru_mtx);
2350                                 dnsp = pnd->lrurec;
2351                                 if (dnsp != NULL && PND_LRU_TIMEOUT(pnd, now)) {
2352                                         pnd->lrurec = NULL;
2353                                         dhcp_free_dd_list(pnd->dh, dnsp);
2354                                         dnsp = NULL;
2355                                 }
2356 
2357                                 if (dnsp != NULL) {
2358                                         if (retry == 0) {
2359                                                 /* Try the next cached record */
2360                                                 pnd->lrurec = dnsp->dnl_next;
2361                                                 dnsp->dnl_next = NULL;
2362                                         } else if (retry == 1) {
2363                                                 /*
2364                                                  * Try all remaining cached
2365                                                  * records
2366                                                  */
2367                                                 pnd->lrurec = NULL;
2368                                         }
2369                                 }
2370                                 if (retry > 1) {
2371                                 /* Try all possible records */
2372                                         pnd->lrurec = NULL;
2373                                         nrecords = -1;
2374                                         if (dnsp != NULL) {
2375                                                 dhcp_free_dd_list(pnd->dh,
2376                                                     dnsp);
2377                                                 dnsp = NULL;
2378                                         }
2379                                 }
2380                                 (void) mutex_unlock(&pnd->lru_mtx);
2381 
2382                                 io_done = (dnsp == NULL);
2383                                 *dnlpp = dhcp_lookup_dd_classify(pcd->pnd,
2384                                     nrecords == -1 ? B_FALSE : B_TRUE, query,
2385                                     nrecords, &dn, (void **)&dnsp, S_LRU);
2386                                 if (*dnlpp != NULL) {
2387                                         dnp = (*dnlpp)->dnl_rec;
2388                                         tip.s_addr = htonl(dnp->dn_cip.s_addr);
2389                                         (void) update_offer(pcd, NULL, 0, &tip,
2390                                             B_TRUE);
2391                                         found = B_TRUE;
2392                                 }
2393 
2394                                 (void) mutex_lock(&pnd->lru_mtx);
2395                                 if (io_done) {
2396                                         if (dnsp != NULL) {
2397                                                 now = time(NULL);
2398                                                 pnd->lru_mtime = reinit_time;
2399                                                 pnd->lru_stamp = now +
2400                                                     cache_secs;
2401                                         }
2402                                 }
2403 
2404                                 /*
2405                                  * Save any leftover records for possible
2406                                  * later use
2407                                  */
2408                                 if (dnsp != NULL) {
2409                                         for (tlp = dnsp;
2410                                             tlp != NULL && tlp->dnl_next;
2411                                             tlp = tlp->dnl_next)
2412                                                 /* null statement */;
2413                                         tlp->dnl_next = pnd->lrurec;
2414                                         pnd->lrurec = dnsp;
2415                                 }
2416                                 (void) mutex_unlock(&pnd->lru_mtx);
2417                         }
2418                 }
2419         }
2420 
2421         return (found);
2422 }
2423 
2424 /*
2425  * purge_dnet_cache() - remove conflicting entries from the
2426  * free and lru dnet caches when records are modified. Expensive
2427  * but necessary.
2428  *
2429  * pnd - per net struct
2430  * dnp - pointer to cached/modified entry
2431  */
2432 static void
2433 purge_dnet_cache(dsvc_dnet_t *pnd, dn_rec_t *dnp)
2434 {
2435         dn_rec_list_t   *tlp;
2436         dn_rec_list_t   *plp;
2437 
2438         (void) mutex_lock(&pnd->free_mtx);
2439 
2440         for (plp = tlp = pnd->freerec; tlp != NULL; tlp = tlp->dnl_next) {
2441                 if (tlp->dnl_rec->dn_cip.s_addr == dnp->dn_cip.s_addr) {
2442                         if (tlp == plp) {
2443                                 pnd->freerec = tlp->dnl_next;
2444                         } else {
2445                                 plp->dnl_next = tlp->dnl_next;
2446                         }
2447                         tlp->dnl_next = NULL;
2448                         break;
2449                 }
2450                 plp = tlp;
2451         }
2452         (void) mutex_unlock(&pnd->free_mtx);
2453         if (tlp != NULL)
2454                 dhcp_free_dd_list(pnd->dh, tlp);
2455 
2456         (void) mutex_lock(&pnd->lru_mtx);
2457         for (plp = tlp = pnd->lrurec; tlp != NULL; tlp = tlp->dnl_next) {
2458                 if (tlp->dnl_rec->dn_cip.s_addr == dnp->dn_cip.s_addr) {
2459                         if (tlp == plp) {
2460                                 pnd->lrurec = tlp->dnl_next;
2461                         } else {
2462                                 plp->dnl_next = tlp->dnl_next;
2463                         }
2464                         tlp->dnl_next = NULL;
2465                         break;
2466                 }
2467                 plp = tlp;
2468         }
2469         (void) mutex_unlock(&pnd->lru_mtx);
2470         if (tlp != NULL)
2471                 dhcp_free_dd_list(pnd->dh, tlp);
2472 }
2473 
2474 /*
2475  * add_dnet_cache() - add a free entry back to the free dnet cache.
2476  *
2477  * Performance: this can greatly reduce the amount of work select_offer()
2478  * must perform.
2479  *
2480  * pnd - per net struct
2481  * dnlp - pointer to cached/modified entry.
2482  */
2483 static void
2484 add_dnet_cache(dsvc_dnet_t *pnd, dn_rec_list_t *dnlp)
2485 {
2486         (void) mutex_lock(&pnd->free_mtx);
2487         dnlp->dnl_next = pnd->freerec;
2488         pnd->freerec = dnlp;
2489         (void) mutex_unlock(&pnd->free_mtx);
2490 }
2491 
2492 static char     unowned_net[] = "the DHCP server believes the IP address that"
2493         " corresponds to the requested host name belongs to a network not"
2494         " managed by the DHCP server.\n";
2495 static char     unowned_addr[] = "the DHCP server believes the IP address that"
2496         " corresponds to the requested host name is not managed by the DHCP"
2497         " server.\n";
2498 
2499 /*
2500  * Determine whether the requested IP address is available to the requesting
2501  * client.  To be so, its IP address must be managed by us, be on the ``right''
2502  * network and neither currently leased nor currently under offer to another
2503  * client.
2504  */
2505 static boolean_t
2506 addr_avail(dsvc_dnet_t *pnd, dsvc_clnt_t *pcd, dn_rec_list_t **dnlpp,
2507     struct in_addr req_ip, boolean_t isname)
2508 {
2509         dn_rec_t        dn;
2510         dn_rec_list_t   *dnip;
2511         uint32_t        query;
2512 
2513         *dnlpp = NULL;
2514         /*
2515          * first, check the ICMP list or offer list.
2516          */
2517         if (isname) {
2518                 if (pcd->off_ip.s_addr != req_ip.s_addr &&
2519                     check_offer(pnd, &req_ip) == B_FALSE) {
2520                         /* Offered to someone else. Sorry. */
2521                         dhcpmsg(LOG_DEBUG, "name_avail(F):"
2522                             "  check_offer failed\n");
2523                         return (B_FALSE);
2524                 }
2525         } else {
2526                 if (update_offer(pcd, NULL, 0, &req_ip, B_TRUE) == B_FALSE) {
2527                         /* Offered to someone else. Sorry. */
2528                         if (isname) {
2529                                 dhcpmsg(LOG_DEBUG, "name_avail(F):"
2530                                     "  check_other_offers failed\n");
2531                         }
2532                         return (B_FALSE);
2533                 }
2534         }
2535 
2536         /*
2537          * entry_available() searches for owner_ips
2538          * query on DN_QCIP will suffice here
2539          */
2540         DSVC_QINIT(query);
2541         DSVC_QEQ(query, DN_QCIP);
2542         dn.dn_cip.s_addr = ntohl(req_ip.s_addr);
2543 
2544         dnip = NULL;
2545         *dnlpp = dhcp_lookup_dd_classify(pnd, B_FALSE, query, -1, &dn,
2546             (void **)&dnip, 0);
2547         dhcp_free_dd_list(pnd->dh, dnip);
2548         if (*dnlpp != NULL) {
2549                 /*
2550                  * Ok, the requested IP exists. But is it available?
2551                  */
2552                 if (!entry_available(pcd, (*dnlpp)->dnl_rec)) {
2553                         dhcp_free_dd_list(pnd->dh, *dnlpp);
2554                         *dnlpp = NULL;
2555                         purge_offer(pcd, B_FALSE, B_TRUE);
2556                         return (B_FALSE);
2557                 }
2558         } else {
2559                 if (isname)
2560                         dhcpmsg(LOG_DEBUG, "name_avail(F):  %s", unowned_addr);
2561                 else
2562                         purge_offer(pcd, B_FALSE, B_TRUE);
2563                 return (B_FALSE);
2564         }
2565         return (B_TRUE);
2566 }
2567 
2568 /*
2569  * Determine whether "name" is available.  To be so, it must either not have
2570  * a corresponding IP address, or its IP address must be managed by us and
2571  * neither currently leased nor currently under offer to a client.
2572  *
2573  * To determine this, we first attempt to translate the name to an address.
2574  * If no name-to-address translation exists, it's automatically available.
2575  * Otherwise, we next check for any outstanding offers. Finally, we look
2576  * at the flags in the corresponding per-network table to see whether the
2577  * address is currently leased.
2578  *
2579  * Upon successful completion, we also return the vetted IP address as a
2580  * value result parameter.
2581  */
2582 static boolean_t
2583 name_avail(char *name, dsvc_clnt_t *pcd, PKT_LIST *plp, dn_rec_list_t **dnlpp,
2584     ENCODE *ecp, struct in_addr **iap)
2585 {
2586         struct          hostent h, *hp, *owner_hp;
2587         char            hbuf[NSS_BUFLEN_HOSTS];
2588         char            fqname [NS_MAXDNAME+1];
2589         char            owner [NS_MAXDNAME+1];
2590         int             err, ho_len;
2591         struct in_addr  ia, ma;
2592         dsvc_dnet_t     *pnd;
2593         boolean_t       isopen = B_FALSE;
2594         ENCODE          *ep;
2595 
2596         *dnlpp = NULL;
2597         /*
2598          *      If possible, use a fully-qualified name to do the name-to-
2599          *      address query.  The complication is that the domain name
2600          *      with which to qualify the client's host name resides in a
2601          *      dhcptab macro unavailable at the time of the DHCPOFFER.
2602          *      ecp will be non-NULL if we may have the means to fully-qualify
2603          *      the name given.
2604          */
2605         if (strchr(name, '.') != NULL) {
2606                 (void) strlcpy(fqname, name, sizeof (fqname));
2607                 if (fqname[(strlen(fqname))-1] != '.')
2608                         (void) strcat(fqname, ".");
2609         } else {
2610                 /*
2611                  * Append '.' domain-name '.' to hostname.
2612                  * Note the use of the trailing '.' to avoid any surprises
2613                  * because of the ndots value (see resolv.conf(4) for more
2614                  * information about the latter).
2615                  *
2616                  * First see whether we can dredge up domain-name from the
2617                  * ENCODE list.
2618                  */
2619                 if ((ecp != NULL) && ((ep = find_encode(ecp,
2620                     DSYM_STANDARD, CD_DNSDOMAIN)) != NULL)) {
2621                         DHCP_OPT        *ho = plp->opts[CD_HOSTNAME];
2622 
2623                         /*
2624                          *      name_avail() should never be called unless the
2625                          *      CD_HOSTNAME option is present in the client's
2626                          *      packet.
2627                          */
2628                         assert(ho != NULL);
2629                         ho_len = ho->len;
2630                         if (ho->value[ho_len - 1] == '\0') {
2631                                 /* null at end of the hostname */
2632                                 ho_len = strlen((char *)ho->value);
2633                         }
2634 
2635                         if (qualify_hostname(fqname, (char *)ho->value,
2636                             (char *)ep->data, ho_len, ep->len) == -1)
2637                                 return (B_FALSE);
2638 
2639                         dhcpmsg(LOG_DEBUG, "name_avail:  unqualified name\n"
2640                             "found CD_DNSDOMAIN and qualified:  %s\n", fqname);
2641                 } else {
2642                         /*
2643                          * No DNS domain in the ENCODE list, have to use
2644                          * local domain name.
2645                          */
2646                         if ((resolv_conf.defdname == NULL) ||
2647                             (qualify_hostname(fqname, name,
2648                             resolv_conf.defdname,
2649                             strlen(name),
2650                             strlen(resolv_conf.defdname)) == -1))
2651                                 return (B_FALSE);
2652 
2653                         dhcpmsg(LOG_DEBUG,
2654                             "name_avail:  unqualified name\n"
2655                             "qualified with local domain: %s\n", fqname);
2656                 }
2657         }
2658 
2659         /*
2660          *      Try a forward lookup on the requested name.
2661          *      Consider the name available if we get a definitive
2662          *      ``name doesn't exist'' indication.
2663          */
2664         hp = gethostbyname_r(fqname, &h, hbuf, sizeof (hbuf), &err);
2665         if (hp == NULL)
2666                 if ((err == HOST_NOT_FOUND) || (err == NO_DATA)) {
2667                         *iap = NULL;
2668                         dhcpmsg(LOG_DEBUG,
2669                             "name_avail(T):  gethostbyname_r failed\n");
2670                         return (B_TRUE);
2671                 } else {
2672                         dhcpmsg(LOG_DEBUG,
2673                             "name_avail(F):  gethostbyname_r failed, err %d\n",
2674                             err);
2675                         return (B_FALSE);
2676                 }
2677 
2678         /*
2679          * Check that the address has not been leased to someone else.
2680          * Bear in mind that there may be inactive A records in the DNS
2681          * (since we don't delete them when a lease expires or is released).
2682          * Try a reverse lookup on the address returned in hp.
2683          * If the owner of this address is different to the requested name
2684          * we can infer that owner is a stale A record.
2685          */
2686 
2687         (void) memcpy(&ia, hp->h_addr, sizeof (struct in_addr));
2688         owner_hp = gethostbyaddr_r((char *)&ia, sizeof (struct in_addr),
2689             AF_INET, &h, hbuf, sizeof (hbuf), &err);
2690 
2691         if (owner_hp == NULL) {
2692                 /* If there's no PTR record the address can't be in use */
2693                 if ((err == HOST_NOT_FOUND) || (err == NO_DATA)) {
2694                         *iap = NULL;
2695                         dhcpmsg(LOG_DEBUG,
2696                             "name_avail(T):  gethostbyaddr_r failed\n");
2697                         return (B_TRUE);
2698                 } else {
2699                         dhcpmsg(LOG_DEBUG,
2700                             "name_avail(F):  gethostbyaddr_r failed\n");
2701                         return (B_FALSE);
2702                 }
2703         }
2704 
2705         /* If name returned is not a FQDN, qualify with local domain name */
2706 
2707         if (strchr(owner_hp->h_name, '.') != NULL) {
2708                 (void) strlcpy(owner, owner_hp->h_name, sizeof (owner));
2709                 if (owner[(strlen(owner))-1] != '.')
2710                         (void) strcat(owner, ".");
2711         } else {
2712                 if ((resolv_conf.defdname == NULL) ||
2713                     (qualify_hostname(owner, owner_hp->h_name,
2714                     resolv_conf.defdname,
2715                     strlen(owner_hp->h_name),
2716                     strlen(resolv_conf.defdname)) == -1))
2717                         return (B_FALSE);
2718 
2719                 dhcpmsg(LOG_DEBUG,
2720                     "name_avail: address owner qualified with %s\n",
2721                     resolv_conf.defdname);
2722         }
2723 
2724         if ((strncmp(owner, fqname, NS_MAXDNAME)) != 0) {
2725                 /* Forward lookup found an inactive record - ignore it */
2726                 *iap = NULL;
2727                 dhcpmsg(LOG_DEBUG, "name_avail(T):  'A' record inactive: %s\n",
2728                     owner);
2729                 return (B_TRUE);
2730         }
2731 
2732         /* Get pnd of the current client */
2733         pnd = pcd->pnd;
2734         get_netmask(&ia, &ma);
2735         if (pnd->net.s_addr != (ia.s_addr & ma.s_addr)) {
2736                 /* get pnd of previous owner of the hostname */
2737                 if (open_dnet(&pnd, &ia, &ma) != DSVC_SUCCESS) {
2738                         /* we must not manage the net containing this address */
2739                         dhcpmsg(LOG_DEBUG, "name_avail(F):  %s", unowned_net);
2740                         return (B_FALSE);
2741                 }
2742                 isopen = B_TRUE;
2743         }
2744 
2745         /*
2746          * Test that the address has not been offered to someone else.
2747          */
2748         if (!addr_avail(pnd, pcd, dnlpp, ia, B_TRUE)) {
2749                 if (isopen) {
2750                         close_dnet(pnd, B_FALSE);
2751                 }
2752                 return (B_FALSE);
2753         }
2754         if (isopen)
2755                 close_dnet(pnd, B_FALSE);
2756 
2757         /* LINTED */
2758         **iap = *((struct in_addr *)hp->h_addr);
2759         dhcpmsg(LOG_DEBUG, "name_avail(T)\n");
2760         return (B_TRUE);
2761 }
2762 
2763 static boolean_t
2764 entry_available(dsvc_clnt_t *pcd, dn_rec_t *dnp)
2765 {
2766         char            ntoab[INET_ADDRSTRLEN];
2767         boolean_t       isme = dnp->dn_cid_len == pcd->cid_len &&
2768             memcmp(pcd->cid, dnp->dn_cid, pcd->cid_len) == 0;
2769         (void) inet_ntop(AF_INET, &(dnp->dn_sip), ntoab, sizeof (ntoab));
2770 
2771         if ((dnp->dn_flags & (DN_FMANUAL|DN_FUNUSABLE)) != 0) {
2772                 dhcpmsg(LOG_DEBUG, "entry_available():"
2773                     "  %s is manually allocated or not usable\n",
2774                     ntoab);
2775                 return (B_FALSE);
2776         }
2777 
2778         if (dnp->dn_cid_len != 0 && isme == B_FALSE &&
2779             (dnp->dn_flags & (DN_FAUTOMATIC|DN_FBOOTP_ONLY))) {
2780                 dhcpmsg(LOG_DEBUG, "entry_available():"
2781                     "  %s is a permanent address or reserved for BOOTP\n",
2782                     ntoab);
2783                 return (B_FALSE);
2784         }
2785 
2786         if (dnp->dn_cid_len != 0 && isme == B_FALSE &&
2787             (lease_t)time(NULL) < (lease_t)ntohl(dnp->dn_lease)) {
2788                 dhcpmsg(LOG_DEBUG, "entry_available():"
2789                     "  lease on %s has not expired\n",
2790                     ntoab);
2791                 return (B_FALSE);
2792         }
2793 
2794         if (match_ownerip(htonl(dnp->dn_sip.s_addr)) == NULL) {
2795                 dhcpmsg(LOG_DEBUG, "entry_available():"
2796                     "  %s does not match owner_ip\n",
2797                     ntoab);
2798                 return (B_FALSE);
2799         }
2800 
2801         /* Input IP is good. */
2802         return (B_TRUE);
2803 }
2804 
2805 static char     msft_classid[] = "MSFT ";
2806 static char     no_domain[] = "name service update on behalf of client with ID"
2807 " %s failed because requested name was not fully-qualified and no DNS"
2808 " domain name was specified for this client in the dhcptab\n";
2809 
2810 /*
2811  * Given a host name and IP address, try to do a host name update.
2812  */
2813 static boolean_t
2814 do_nsupdate(struct in_addr ia, ENCODE *ecp, PKT_LIST *plp)
2815 {
2816         struct hostent  *hp;
2817         DHCP_OPT        *ho;
2818         ENCODE          *ep;
2819         char            class_idbuf[DSYM_CLASS_SIZE];
2820         int             puthostent_ret;
2821 
2822         /*
2823          * hostent information is dynamically allocated so that threads spawned
2824          * by dns_puthostent() will have access to it after the calling thread
2825          * has returned.
2826          */
2827         hp = (struct hostent *)smalloc(sizeof (struct hostent));
2828         hp->h_addr_list = (char **)smalloc(2 * sizeof (char **));
2829         hp->h_addr_list[1] = NULL;
2830         hp->h_addr = smalloc(sizeof (struct in_addr));
2831         hp->h_aliases = NULL;
2832         hp->h_addrtype = AF_INET;
2833         hp->h_length = sizeof (struct in_addr);
2834         /*
2835          * Convert address to network order, as that's what hostent's are
2836          * expected to be.
2837          */
2838         /* LINTED */
2839         ((struct in_addr *)hp->h_addr)->s_addr = htonl(ia.s_addr);
2840 
2841         /*
2842          * Is the host name unqualified?  If so, try to qualify it.  If that
2843          * can't be done, explain why the update won't be attempted.
2844          */
2845         ho = plp->opts[CD_HOSTNAME];
2846         if (memchr(ho->value, '.', ho->len) == NULL) {
2847                 /*
2848                  * See whether we can dredge up the DNS domain from the
2849                  * ENCODE list.
2850                  */
2851                 if ((ep = find_encode(ecp, DSYM_STANDARD, CD_DNSDOMAIN)) !=
2852                     NULL) {
2853                         char *fqname;
2854                         int ho_len = ho->len;
2855 
2856                         /*
2857                          *      We need room for
2858                          *
2859                          *      hostname len    +
2860                          *      strlen(".")     +
2861                          *      domainname len  +
2862                          *      strlen(".")     +
2863                          *      trailing '\0'
2864                          *
2865                          *      Note the use of the trailing '.' to avoid any
2866                          *      surprises because of the ndots value (see
2867                          *      resolv.conf(4) for more information about
2868                          *      the latter).
2869                          */
2870                         if (ho->value[ho_len - 1] == '\0') {
2871                                 ho_len = strlen((char *)ho->value);
2872                         }
2873                         fqname = smalloc(ho_len + ep->len + 1 + 1 + 1);
2874                         /* first copy host name, ... */
2875                         (void) memcpy(fqname, ho->value, ho_len);
2876                         /* then '.', ... */
2877                         (void) memcpy(fqname + ho_len, ".", 1);
2878                         /* ... then domain name, */
2879                         (void) memcpy(fqname + ho_len + 1, ep->data, ep->len);
2880                         /* then a trailing '.', ... */
2881                         (void) memcpy(fqname + ho_len + ep->len + 1, ".", 1);
2882                         /* no need to null-terminate - smalloc() did it */
2883 
2884                         hp->h_name = fqname;
2885                         dhcpmsg(LOG_DEBUG, "do_nsupdate:  unqualified name\n"
2886                             "found CD_DNSDOMAIN and qualified:  %s\n", fqname);
2887                 } else {
2888                         char cidbuf[BUFSIZ];
2889 
2890                         (void) disp_cid(plp, cidbuf, sizeof (cidbuf));
2891                         dhcpmsg(LOG_INFO, no_domain, cidbuf);
2892                 }
2893         } else {
2894                 hp->h_name = smalloc(ho->len + 1);
2895                 (void) memcpy(hp->h_name, ho->value, ho->len);
2896                 dhcpmsg(LOG_DEBUG, "do_nsupdate:  fully qualified name:  %s\n",
2897                     hp->h_name);
2898         }
2899 
2900         /* returns -1 or the number of name service updates done */
2901         puthostent_ret = dns_puthostent(hp, nsutimeout_secs);
2902         dhcpmsg(LOG_DEBUG, "do_nsupdate:  dns_puthostent returned %d\n",
2903             puthostent_ret);
2904         if (puthostent_ret == -1) {
2905                 return (B_FALSE);
2906         } else if (puthostent_ret == 0) {
2907                 /*
2908                  *      dns_puthostent() didn't see any errors occur,
2909                  *      but no updates were done;  Microsoft clients
2910                  *      (i.e. clients with a Microsoft class ID) expect
2911                  *      it to succeed, so we lie to them.
2912                  */
2913                 if (((get_class_id(plp, class_idbuf,
2914                     sizeof (class_idbuf))) != NULL) &&
2915                     (strncmp(msft_classid, class_idbuf,
2916                     sizeof (msft_classid)) == 0)) {
2917                         dhcpmsg(LOG_DEBUG, "do_nsupdate:  class ID \"%s\"\n",
2918                             class_idbuf);
2919                         return (B_TRUE);
2920                 } else
2921                         return (B_FALSE);
2922         } else {
2923                 return (B_TRUE);
2924         }
2925 }