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, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <stdio.h>
  30 #include <string.h>
  31 #include <stdarg.h>
  32 #include <syslog.h>
  33 #include <errno.h>
  34 #include <sys/types.h>
  35 #include <sys/sysmacros.h>
  36 #include <time.h>
  37 #include <locale.h>
  38 #include <stdlib.h>
  39 #include <unistd.h>
  40 #include <errno.h>
  41 #include <assert.h>
  42 #include <sys/socket.h>
  43 #include <net/if.h>
  44 #include <netinet/in.h>
  45 #include <netinet/if_ether.h>
  46 #include <arpa/inet.h>
  47 #include <netinet/dhcp.h>
  48 #include <netdb.h>
  49 #include <sys/mman.h>
  50 #include <locale.h>
  51 #include <tnf/probe.h>
  52 #include <resolv.h>
  53 
  54 #include "dhcpd.h"
  55 #include "per_dnet.h"
  56 #include "interfaces.h"
  57 
  58 #ifdef  DEBUG
  59 /*
  60  * Datastore debugging functions.
  61  *
  62  * A simple datastore simulation, using the magic DBG_MEMORY_NET network,
  63  * is provided, to test the program with minimal datastore overhead.
  64  * A concatenated reclist is used to speed record manipulation.
  65  * Note that other networks continue to pass-thru to libdhcpsvc, to allow
  66  * live comparison.
  67  */
  68 
  69 /* Simple datastore database. */
  70 typedef struct db {
  71         lease_t dn_lease;
  72         char    dn_cid[128];
  73         char    dn_macro[2];
  74         uchar_t dn_cid_len;
  75         uchar_t dn_flags;
  76 } dbg_t;
  77 
  78 typedef struct reclist {
  79         dn_rec_list_t d_reclist;
  80         dn_rec_t d_rec;
  81 } dbg_rec_t;
  82 
  83 #define         DBG_MAXTABLE    16
  84 static dsvc_handle_t dbg_handle[DBG_MAXTABLE];  /* simulated handle */
  85 static rwlock_t dbg_lock[DBG_MAXTABLE];         /* locks */
  86 static uint32_t dbg_size = 4096;                /* table size */
  87 static uint32_t dbg_msize;                      /* mapped size */
  88 static uint32_t dbg_mask = 0xFFFF;              /* table mask */
  89 
  90 static uint32_t dbg_cid = 0;                    /* starting cid */
  91 static uint32_t dbg_flags = 0;                  /* starting flags */
  92 static uint32_t dbg_lease = 0;                  /* starting lease */
  93 static char     dbg_macro = '1';                /* macro */
  94 #endif  /* DEBUG */
  95 
  96 int
  97 dhcp_open_dd(dsvc_handle_t *handp, dsvc_datastore_t *ddp, dsvc_contype_t type,
  98     const char *name, uint_t flags)
  99 {
 100 #ifndef DEBUG
 101         return (open_dd(handp, ddp, type, name, flags));
 102 
 103 #else   /* DEBUG */
 104         int ret;
 105         int hind;
 106         int net;
 107         int pgmsk = sysconf(_SC_PAGESIZE) - 1;
 108 
 109         if (dbg_net && memcmp(name, dbg_net, strlen(dbg_net)) == 0) {
 110                 for (net = 0, hind = strlen(dbg_net); name[hind] != '.'; hind++)
 111                         net += (net * 10) + (name[hind] - '0');
 112                 if (net > DBG_MAXTABLE)
 113                         return (DSVC_NO_TABLE);
 114 
 115                 if (dbg_handle[net] == NULL) {
 116                         dbg_msize = (sizeof (dbg_t) * dbg_size + pgmsk) &
 117                             ~pgmsk;
 118                         /* LINTED [alignment ok] */
 119                         dbg_handle[net] = (dsvc_handle_t)mmap(0, dbg_msize,
 120                             PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
 121                         if ((char *)dbg_handle[net] == MAP_FAILED) {
 122                                 dbg_handle[net] = NULL;
 123                                 return (DSVC_INVAL);
 124                         }
 125                 }
 126                 *handp = (void *)net;
 127                 ret = DSVC_SUCCESS;
 128         } else
 129                 ret = open_dd(handp, ddp, type, name, flags);
 130 
 131         return (ret);
 132 #endif  /* DEBUG */
 133 }
 134 
 135 int
 136 dhcp_close_dd(dsvc_handle_t *handp)
 137 {
 138 #ifndef DEBUG
 139         return (close_dd(handp));
 140 
 141 #else   /* DEBUG */
 142         int ret;
 143         int hind = (int)*handp;
 144 
 145         if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
 146                 if (dbg_handle[hind] != NULL) {
 147                         (void) munmap((char *)dbg_handle[hind], dbg_msize);
 148                         dbg_handle[hind] = (dsvc_handle_t)NULL;
 149                         ret = DSVC_SUCCESS;
 150                 }
 151         } else
 152                 ret = close_dd(handp);
 153 
 154         return (ret);
 155 #endif  /* DEBUG */
 156 }
 157 
 158 /*
 159  * Detach the element from a list, and return it. If the list is empty, NULL is
 160  * returned.
 161  */
 162 dn_rec_list_t *
 163 detach_dnrec_from_list(dn_rec_list_t *prevp, dn_rec_list_t *elemp,
 164     dn_rec_list_t **listpp)
 165 {
 166         if (prevp == NULL) {
 167                 if (elemp == *listpp && elemp != NULL) {
 168                         /* head of the list */
 169                         *listpp = (*listpp)->dnl_next;
 170                         elemp->dnl_next = NULL;
 171                 }
 172         } else if (prevp->dnl_next != NULL) {
 173                 /* somewhere in the middle */
 174                 prevp->dnl_next = elemp->dnl_next;
 175                 elemp->dnl_next = NULL;
 176         } else
 177                 assert(elemp == NULL);
 178 
 179         return (elemp);
 180 }
 181 
 182 /*
 183  * Attach an unattached element (elemp) to a list (*listpp).
 184  */
 185 static void
 186 attach_dnrec_to_list(dn_rec_list_t *elemp, dn_rec_list_t **listpp)
 187 {
 188         if (*listpp != NULL)
 189                 elemp->dnl_next = *listpp;
 190         *listpp = elemp;
 191 }
 192 
 193 /*
 194  * dhcp_lookup_dd: perform lookup_dd.
 195  */
 196 static int
 197 dhcp_lookup_dd(dsvc_handle_t hand, boolean_t partial, uint_t query,
 198     int count, const void *targetp, void **recordsp, uint_t *nrecordsp)
 199 {
 200 #ifndef DEBUG
 201         return (lookup_dd(hand, partial, query, count, targetp, recordsp,
 202             nrecordsp));
 203 
 204 #else   /* DEBUG */
 205         int             ret;
 206         int             hind = (int)hand;
 207         int             ind;
 208         dn_rec_t        *inp;
 209         dn_rec_list_t   **outp = (dn_rec_list_t **)recordsp;
 210         dbg_t           *dbp;
 211         dbg_t           *endp;
 212         dbg_rec_t       *rp;
 213         dn_rec_t        *recp;
 214         dn_rec_list_t   *reclp;
 215         dbg_t           *dbg_db;
 216 
 217         if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
 218                 dbg_db = (dbg_t *)dbg_handle[hind];
 219 
 220                 if (outp)
 221                         *outp = NULL;
 222                 if (nrecordsp)
 223                         *nrecordsp = 0;
 224                 inp = (dn_rec_t *)targetp;
 225 
 226                 (void) rw_rdlock(&dbg_lock[hind]);
 227                 /*
 228                  * Simple linear search, aided by the fact that
 229                  * the server currently checks flags.
 230                  */
 231                 if (DSVC_QISEQ(query, DN_QCIP)) {
 232                         ind = inp->dn_cip.s_addr & dbg_mask;
 233                         dbp = &dbg_db[ind];
 234                         endp = &dbg_db[ind + 1];
 235                 } else {
 236                         ind = 0;
 237                         dbp = dbg_db;
 238                         endp = &dbg_db[dbg_size];
 239                 }
 240                 for (; dbp < endp; dbp++, ind++) {
 241                         /*
 242                          * Initialize record. fields will be zero'd
 243                          * when initially mmap'd.
 244                          */
 245                         if (dbp->dn_cid_len == 0) {
 246                                 /* Skip server address to avoid arp issues. */
 247                                 if (ind == (ntohl(server_ip.s_addr) % 256))
 248                                         continue;
 249                                 if (dbg_cid)
 250                                         (void) snprintf(dbp->dn_cid,
 251                                             sizeof (dbp->dn_cid), "%8X",
 252                                             dbg_cid++);
 253                                 dbp->dn_flags = dbg_flags;
 254                                 dbp->dn_lease = dbg_lease;
 255                                 dbp->dn_macro[0] = dbg_macro;
 256                                 dbp->dn_macro[1] = '\0';
 257                                 dbp->dn_cid_len = 1;
 258                         }
 259                         if (DSVC_QISEQ(query, DN_QCID) &&
 260                             (inp->dn_cid[0] != dbp->dn_cid[0] ||
 261                             memcmp(dbp->dn_cid, inp->dn_cid, inp->dn_cid_len)))
 262                                 continue;
 263 
 264                         rp = (dbg_rec_t *)smalloc(sizeof (dbg_rec_t));
 265                         reclp = &rp->d_reclist;
 266                         recp = &rp->d_rec;
 267 
 268                         if (nrecordsp)
 269                                 (*nrecordsp)++;
 270 
 271                         reclp->dnl_rec = recp;
 272                         recp->dn_lease = dbp->dn_lease;
 273                         recp->dn_sip.s_addr = ntohl(owner_ip->s_addr);
 274                         recp->dn_cip.s_addr = 0xd000000 + (hind << 16) + ind;
 275                         recp->dn_cid_len = dbp->dn_cid_len;
 276                         recp->dn_flags = dbp->dn_flags;
 277                         recp->dn_macro[0] = dbp->dn_macro[0];
 278                         recp->dn_macro[1] = '\0';
 279                         if ((recp->dn_cid[0] = dbp->dn_cid[0]) != '\0')
 280                                 (void) memcpy(recp->dn_cid, dbp->dn_cid,
 281                                     dbp->dn_cid_len);
 282                         if (*outp == NULL)
 283                                 *outp = reclp;
 284                         else {
 285                                 reclp->dnl_next = *outp;
 286                                 *outp = reclp;
 287                         }
 288                         if (count > 0 && nrecordsp && *nrecordsp >= count)
 289                                 break;
 290                 }
 291                 (void) rw_unlock(&dbg_lock[hind]);
 292                 ret = DSVC_SUCCESS;
 293         } else
 294                 ret = lookup_dd(hand, partial, query, count, targetp,
 295                     recordsp, nrecordsp);
 296 
 297         return (ret);
 298 #endif  /* DEBUG */
 299 }
 300 
 301 int
 302 dhcp_modify_dd_entry(dsvc_handle_t hand, const void *origp, void *newp)
 303 {
 304 #ifndef DEBUG
 305         return (modify_dd_entry(hand, origp, newp));
 306 
 307 #else   /* DEBUG */
 308         int             ret;
 309         int             hind = (int)hand;
 310         int             ind;
 311         dn_rec_t        *dnp;
 312         dbg_t           *dbp;
 313         dbg_t           *dbg_db;
 314 
 315         if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
 316                 dbg_db = (dbg_t *)dbg_handle[hind];
 317 
 318                 dnp = (dn_rec_t *)newp;
 319                 ind = dnp->dn_cip.s_addr & dbg_mask;
 320                 dbp = &dbg_db[ind];
 321                 (void) rw_wrlock(&dbg_lock[hind]);
 322                 dbp->dn_lease = dnp->dn_lease;
 323                 dbp->dn_cid_len = dnp->dn_cid_len;
 324                 dbp->dn_flags = dnp->dn_flags;
 325                 /*
 326                  * Performance: avoid routine call when NULL string
 327                  * is being copied.
 328                  */
 329                 if ((dbp->dn_cid[0] = dnp->dn_cid[0]) != '\0')
 330                         (void) memcpy(dbp->dn_cid, dnp->dn_cid,
 331                             dnp->dn_cid_len);
 332                 (void) rw_unlock(&dbg_lock[hind]);
 333                 ret = DSVC_SUCCESS;
 334         } else
 335                 ret = modify_dd_entry(hand, origp, newp);
 336 
 337         return (ret);
 338 #endif  /* DEBUG */
 339 }
 340 
 341 void
 342 dhcp_free_dd_list(dsvc_handle_t hand, void *listp)
 343 {
 344 #ifndef DEBUG
 345         free_dd_list(hand, listp);
 346 
 347 #else   /* DEBUG */
 348         dn_rec_list_t *ptr;
 349         int hind = (int)hand;
 350 
 351         if (dbg_net && hind >= 0 && hind < DBG_MAXTABLE) {
 352                 while ((ptr = listp) != NULL) {
 353                         listp = ptr->dnl_next;
 354                         free(ptr);
 355                 }
 356         } else
 357                 free_dd_list(hand, listp);
 358 #endif  /* DEBUG */
 359 }
 360 
 361 /*
 362  * cmp_lrusort: qsort() comparison routine to sort lru list.
 363  */
 364 static int
 365 cmp_lrusort(const void *a, const void *b)
 366 {
 367         dn_rec_list_t *r1 = *(dn_rec_list_t **)a;
 368         dn_rec_list_t *r2 = *(dn_rec_list_t **)b;
 369 
 370         if (r1->dnl_rec->dn_lease < r2->dnl_rec->dn_lease)
 371                 return (-1);
 372         else if (r1->dnl_rec->dn_lease == r2->dnl_rec->dn_lease)
 373                 return (0);
 374         else
 375                 return (1);
 376 }
 377 
 378 /*
 379  * get_lrusort: quick sort of eligible lru container entries.
 380  */
 381 static dn_rec_list_t *
 382 get_lrusort(dsvc_dnet_t *pnd, dn_rec_list_t *lrup, uint_t *lrecords)
 383 {
 384         size_t nel;
 385         size_t size = *lrecords * sizeof (dn_rec_list_t *);
 386         dn_rec_list_t *from, **to, *next, *freerec = NULL;
 387         dn_rec_list_t *lrupage;
 388         dn_rec_t *rp;
 389         time_t reuse_time = time(NULL) - min_lru;
 390         uint_t records = 0;
 391 #ifndef NDEBUG
 392         int cnt = 0;
 393 #endif  /* !NDEBUG */
 394 
 395         (void) mutex_lock(&pnd->lrupage_mtx);
 396         if (pnd->lrupage == NULL || pnd->lrusize < size) {
 397                 if (pnd->lrupage != NULL)
 398                         free(pnd->lrupage);
 399                 pnd->lrupage = (dn_rec_list_t **)smalloc(size);
 400                 pnd->lrusize = size;
 401         }
 402         if ((to = pnd->lrupage) == NULL) {
 403                 pnd->lrusize = 0;
 404                 (void) mutex_unlock(&pnd->lrupage_mtx);
 405                 return (lrup);
 406         }
 407 
 408         /*
 409          * Build a list of entries, discarding those which are in use.
 410          */
 411         *to = NULL;
 412         for (from = lrup; from != NULL; from = next) {
 413                 next = from->dnl_next;
 414                 rp = from->dnl_rec;
 415                 if (rp->dn_lease > reuse_time ||
 416                     (rp->dn_flags & DN_FAUTOMATIC) ||
 417                     rp->dn_lease == DHCP_PERM) {
 418                         from->dnl_next = freerec;
 419                         freerec = from;
 420                 } else {
 421                         records++;
 422                         *(to++) = from;
 423                 }
 424                 assert(++cnt <= *lrecords);
 425         }
 426 
 427         /*
 428          * Sort any usable elements, and relink.
 429          */
 430         nel = (int)(to - pnd->lrupage);
 431         if (nel > 0) {
 432                 if (nel > 1)
 433                         qsort(pnd->lrupage, nel, sizeof (dn_rec_list_t *),
 434                             cmp_lrusort);
 435                 for (to = pnd->lrupage; nel > 0; to++, nel--)
 436                         (*to)->dnl_next = *(to + 1);
 437                 to--;
 438                 (*to)->dnl_next = NULL;
 439         }
 440 
 441         /*
 442          * Free any unusable elements, return any usable elements.
 443          */
 444         if (freerec)
 445                 dhcp_free_dd_list(pnd->dh, freerec);
 446         *lrecords = records;
 447 
 448         lrupage = *(pnd->lrupage);
 449         (void) mutex_unlock(&pnd->lrupage_mtx);
 450         return (lrupage);
 451 }
 452 
 453 /*
 454  * dhcp_lookup_dd_classify: perform lookup_dd(), or use existing records
 455  * if supplied, and classify the results based on the type of search criteria
 456  * being employed. Centralized policy for DN_FMANUAL and DN_FUNUSABLE flag
 457  * processing are implemented here. Classification is specialized
 458  * based on these specific search criteria:
 459  *
 460  *      S_CID           A CID match is requested. Perform DN_FMANUAL and
 461  *                      DN_FUNUSABLE processing.
 462  *      S_FREE          A search for free records. Only examine first
 463  *                      matching record.
 464  *      S_LRU           A search for lru records. Perform sort if needed,
 465  *                      and only examine first matching record.
 466  *
 467  * A matching record is detached and returned if found (ok ||
 468  * manual + unusable). Other successful matches are returned in recordsp as
 469  * a cache.
 470  */
 471 void *
 472 dhcp_lookup_dd_classify(dsvc_dnet_t *pnd, boolean_t partial, uint_t query,
 473     int count, const dn_rec_t *targetp, void **recordsp, int searchtype)
 474 {
 475         int             err;
 476         uint_t          rec_cnt = 0, manual = 0;
 477         dn_rec_t        *dnp;
 478         dn_rec_list_t   *nlp = NULL, *dnlp = NULL;
 479         dn_rec_list_t   *unulp = NULL;          /* list of unusables, !manual */
 480         dn_rec_list_t   *unu_m_lp = NULL;       /* list of unusable + manual */
 481         dn_rec_list_t   *m_lp = NULL;           /* list of manual records */
 482         dn_rec_list_t   *cachep = NULL;         /* match cache */
 483         struct in_addr  swapaddr;
 484         char            ntoab[INET_ADDRSTRLEN];
 485 
 486         /*
 487          * Lookup records matching the specified criteria, or use
 488          * records from a previous lookup supplied for classification.
 489          */
 490         if (*recordsp == NULL) {
 491 
 492                 TNF_PROBE_1_DEBUG(classify, "classify classify",
 493                     "classify_query%debug 'in func classify'",
 494                     tnf_long, query, query);
 495 
 496                 err = dhcp_lookup_dd(pnd->dh, partial, query, count, targetp,
 497                     (void **)recordsp, &rec_cnt);
 498 
 499                 TNF_PROBE_1_DEBUG(classify_cid_end, "classify classify_end",
 500                     "classify_end%debug 'in func classify'",
 501                     tnf_long, rec_cnt, rec_cnt);
 502 
 503                 /*
 504                  * If any error occurs, mark the dsvc_dnet_t table
 505                  * for immediate close and reopen. Let the protocol
 506                  * perform recover, rather than attempting time-consuming
 507                  * in-place error recovery.
 508                  */
 509                 if (err != DSVC_SUCCESS) {
 510                         (void) mutex_lock(&pnd->pnd_mtx);
 511                         pnd->flags |= DHCP_PND_ERROR;
 512                         hash_Dtime(pnd->hand, 0);
 513                         (void) mutex_unlock(&pnd->pnd_mtx);
 514 #ifdef  DEBUG
 515                         dhcpmsg(LOG_DEBUG, "classify failure %s\n",
 516                                 dhcpsvc_errmsg(err));
 517 #endif  /* DEBUG */
 518                         *recordsp = NULL;
 519                         return (NULL);
 520                 }
 521 
 522                 /*
 523                  * For LRU classification, sort returned records based
 524                  * on dn_lease field. Discards records with valid lease
 525                  * times; adjusts rec_cnt accordingly.
 526                  */
 527                 if (searchtype & S_LRU)
 528                         *recordsp = get_lrusort(pnd, *recordsp, &rec_cnt);
 529 
 530         }
 531 
 532         /*
 533          * Record classification: scan through all records, performing
 534          * DN_FUNUSABLE and DN_FMANUAL processing. Note that most of the
 535          * work has been performed by the datastore query. Remove the matching
 536          * entry from the singlely-linked record list, for return. Free any
 537          * non-matching entries prior to the match. Pass back any additional
 538          * entries after the match in the recordsp pointer for possible re-use
 539          * by the caching code.
 540          */
 541 
 542         for (nlp = detach_dnrec_from_list(NULL, *recordsp,
 543             (dn_rec_list_t **)recordsp); nlp != NULL;
 544             nlp = detach_dnrec_from_list(NULL, *recordsp,
 545             (dn_rec_list_t **)recordsp)) {
 546                 /*
 547                  * If we find that there is a DN_FMANUAL entry that is
 548                  * DN_FUNUSABLE, we fail the request, when performing a
 549                  * CID search, even though there may be other CID matches. In
 550                  * the CID case, those other CID matches are errors, because
 551                  * there should be one and only one record for a client if that
 552                  * record is marked as being DN_FMANUALly assigned. We tell
 553                  * the user how many of those CID matches there are. If there
 554                  * are no DN_FMANUAL records, the first matching record which
 555                  * is USABLE wins.
 556                  */
 557                 dnp = nlp->dnl_rec;
 558                 if (dnp->dn_flags & DN_FUNUSABLE) {
 559                         if ((searchtype & (S_CID|S_FREE|S_LRU)) == S_CID) {
 560                                 char    cidbuf[DHCP_MAX_OPT_SIZE];
 561                                 uint_t  blen = sizeof (cidbuf);
 562 
 563                                 (void) octet_to_hexascii(targetp->dn_cid,
 564                                     targetp->dn_cid_len,
 565                                     cidbuf, &blen);
 566 
 567                                 swapaddr.s_addr = htonl(dnp->dn_cip.s_addr);
 568 
 569                                 dhcpmsg(LOG_NOTICE, "(%1$s,%2$s) "
 570                                     "currently marked as unusable.\n", cidbuf,
 571                                     inet_ntop(AF_INET, &swapaddr, ntoab,
 572                                     sizeof (ntoab)));
 573                         }
 574 
 575                         /* build list of unusable records */
 576                         if (dnp->dn_flags & DN_FMANUAL) {
 577                                 attach_dnrec_to_list(nlp, &unu_m_lp);
 578                                 manual++;
 579                         } else
 580                                 attach_dnrec_to_list(nlp, &unulp);
 581                 } else {
 582                         if (dnp->dn_flags & DN_FMANUAL) {
 583                                 attach_dnrec_to_list(nlp, &m_lp);
 584                                 manual++;
 585                         } else
 586                                 attach_dnrec_to_list(nlp, &cachep);
 587                         /*
 588                          * These searches do not require examining all
 589                          * matches.
 590                          */
 591                         if (searchtype & (S_FREE|S_LRU))
 592                                 break;
 593                 }
 594         }
 595 
 596         /*
 597          * Warnings are printed for CID searches which end with
 598          * DN_FUNUSABLE|DN_FMANUAL match(es).
 599          */
 600         if (m_lp != NULL || unu_m_lp != NULL) {
 601                 if (manual > 1) {
 602                         char    cidbuf[DHCP_MAX_OPT_SIZE];
 603                         uint_t  blen = sizeof (cidbuf);
 604 
 605                         (void) octet_to_hexascii(targetp->dn_cid,
 606                                 targetp->dn_cid_len,
 607                                 cidbuf, &blen);
 608                         dhcpmsg(LOG_WARNING,
 609                             "Manual allocation (%1$s) has %2$d other MANUAL"
 610                             " records. It should have 0.\n", cidbuf,
 611                             manual - 1);
 612                 }
 613                 if (unu_m_lp != NULL) {
 614                         dnlp = detach_dnrec_from_list(NULL, unu_m_lp,
 615                             &unu_m_lp);
 616                 } else
 617                         dnlp = detach_dnrec_from_list(NULL, m_lp, &m_lp);
 618         }
 619 
 620         /* Free any unusable entries */
 621         if (unulp != NULL)
 622                 dhcp_free_dd_list(pnd->dh, unulp);
 623 
 624         /* any other... */
 625         if (dnlp == NULL)
 626                 dnlp = detach_dnrec_from_list(NULL, cachep, &cachep);
 627 
 628         /*
 629          * Return any unused elements for possible caching use. These are
 630          * the  additional manual + unusable (as punishment for having
 631          * multiple items), manual, and and any others.
 632          */
 633         if (cachep != NULL)
 634                 attach_dnrec_to_list(cachep, (dn_rec_list_t **)recordsp);
 635         if (m_lp != NULL)
 636                 attach_dnrec_to_list(m_lp, (dn_rec_list_t **)recordsp);
 637         if (unu_m_lp != NULL)
 638                 attach_dnrec_to_list(unu_m_lp, (dn_rec_list_t **)recordsp);
 639 
 640         /*
 641          * Return one of the matching record(s).
 642          */
 643         return (dnlp);
 644 }
 645 
 646 /*
 647  * Error message function. If debugging off, then logging goes to
 648  * syslog.
 649  *
 650  * Must be MT SAFE - called by various threads as well as the main thread.
 651  */
 652 
 653 /*VARARGS2*/
 654 void
 655 dhcpmsg(int errlevel, const char *fmtp, ...)
 656 {
 657         char            buff[BUFSIZ], errbuf[BUFSIZ];
 658         const char *f = buff;
 659         va_list         ap;
 660 
 661         if (debug < 0)
 662                 return;
 663 
 664         va_start(ap, fmtp);
 665 
 666         if (debug > 0)  {
 667                 if (errlevel != LOG_ERR)
 668                         (void) snprintf(errbuf, sizeof (errbuf),
 669                             "%lx: ", time(NULL));
 670                 else
 671                         (void) snprintf(errbuf, sizeof (errbuf),
 672                             "%lx: (%s)", time(NULL), strerror(errno));
 673                 (void) snprintf(buff, sizeof (buff), "%s %s", errbuf,
 674                     gettext(fmtp));
 675                 (void) vfprintf(stderr, f, ap);
 676         } else if (debug == 0)
 677                 (void) vsyslog(errlevel, gettext(fmtp), ap);
 678 
 679         va_end(ap);
 680 }
 681 
 682 /*
 683  * smalloc()  --  safe malloc()
 684  *
 685  * Always returns a valid pointer(if it returns at all).  The allocated
 686  * memory is initialized to all zeros.  If malloc() returns an error, a
 687  * message is printed using the syslog() function and the program aborts
 688  * with a status of 1.
 689  *
 690  * Must be MT SAFE - called by threads other than the main thread.
 691  */
 692 void *
 693 smalloc(uint_t nbytes)
 694 {
 695         char            *retvalue;
 696 
 697         if ((retvalue = calloc(nbytes, sizeof (char))) == NULL) {
 698                 dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n",
 699                     strerror(errno));
 700                 exit(1);
 701         }
 702         return (retvalue);
 703 }
 704 
 705 /*
 706  * srealloc()  --  safe realloc()
 707  *
 708  * Always returns a valid pointer(if it returns at all).
 709  * If realloc() returns an error, a message is printed using the syslog()
 710  * function and the program aborts with a status of 1.
 711  * Unlike smalloc(), does not initialize the buffer to all zeros.
 712  *
 713  * Must be MT SAFE - called by threads other than the main thread.
 714  */
 715 void *
 716 srealloc(void *arg, uint_t nbytes)
 717 {
 718         if ((arg = realloc(arg, nbytes)) == NULL) {
 719                 dhcpmsg(LOG_ERR, "Cannot allocate memory (%s), exiting\n",
 720                     strerror(errno));
 721                 exit(1);
 722         }
 723         return (arg);
 724 }
 725 
 726 /*
 727  * Matches the speficied ip address with our owner_ip addresses.
 728  * Returns NULL if no match is found.
 729  */
 730 struct in_addr *
 731 match_ownerip(in_addr_t new)
 732 {
 733         struct in_addr *oip = owner_ip;
 734 
 735         while (oip->s_addr != INADDR_ANY && oip->s_addr != new)
 736                 oip++;
 737         return ((oip->s_addr != INADDR_ANY) ? oip : NULL);
 738 }
 739 
 740 /*
 741  * qualify_hostname()  --  concatenate host  "."  domain  "."  NULL
 742  */
 743 int
 744 qualify_hostname(char *fqname, const char *host, const char *domain,
 745     int host_length, int domain_length)
 746 {
 747         char *fqptr;
 748 
 749         if (domain_length + host_length + 2 > NS_MAXDNAME) {
 750                 dhcpmsg(LOG_ERR, "qualify_hostname: FQDN too long\n");
 751                 return (-1);
 752         }
 753 
 754         fqptr = fqname;
 755 
 756         (void) memcpy(fqptr, host, host_length);
 757         fqptr += host_length;
 758 
 759         *fqptr = '.';
 760         fqptr++;
 761 
 762         (void) memcpy(fqptr, domain, domain_length);
 763         fqptr += domain_length;
 764 
 765         *fqptr = '.';
 766         *(fqptr+1) = '\0';
 767 
 768         return (0);
 769 }