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 }