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 }