xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/misc.c (revision 0:68f95e015346)
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
dhcp_open_dd(dsvc_handle_t * handp,dsvc_datastore_t * ddp,dsvc_contype_t type,const char * name,uint_t flags)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
dhcp_close_dd(dsvc_handle_t * handp)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 *
detach_dnrec_from_list(dn_rec_list_t * prevp,dn_rec_list_t * elemp,dn_rec_list_t ** listpp)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
attach_dnrec_to_list(dn_rec_list_t * elemp,dn_rec_list_t ** listpp)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
dhcp_lookup_dd(dsvc_handle_t hand,boolean_t partial,uint_t query,int count,const void * targetp,void ** recordsp,uint_t * nrecordsp)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
dhcp_modify_dd_entry(dsvc_handle_t hand,const void * origp,void * newp)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
dhcp_free_dd_list(dsvc_handle_t hand,void * listp)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
cmp_lrusort(const void * a,const void * b)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 *
get_lrusort(dsvc_dnet_t * pnd,dn_rec_list_t * lrup,uint_t * lrecords)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 *
dhcp_lookup_dd_classify(dsvc_dnet_t * pnd,boolean_t partial,uint_t query,int count,const dn_rec_t * targetp,void ** recordsp,int searchtype)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
dhcpmsg(int errlevel,const char * fmtp,...)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 *
smalloc(uint_t nbytes)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 *
srealloc(void * arg,uint_t nbytes)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 *
match_ownerip(in_addr_t new)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
qualify_hostname(char * fqname,const char * host,const char * domain,int host_length,int domain_length)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 }
770