xref: /illumos-gate/usr/src/lib/smbsrv/libsmbns/common/smbns_netbios_cache.c (revision 2a8d6eba033e4713ab12b61178f0513f1f075482)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <synch.h>
29 #include <stdio.h>
30 #include <syslog.h>
31 #include <stdlib.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 
35 #include <smbsrv/libsmbns.h>
36 #include <smbns_netbios.h>
37 
38 #define	NETBIOS_HTAB_SZ	128
39 #define	NETBIOS_HKEY_SZ	(NETBIOS_NAME_SZ + NETBIOS_DOMAIN_NAME_MAX)
40 
41 #define	NETBIOS_NAMEBUF  char namebuf[20]
42 
43 #define	NETBIOS_SAME_IP(addr1, addr2) \
44 	((addr1)->sin.sin_addr.s_addr == (addr2)->sin.sin_addr.s_addr)
45 
46 int smb_netbios_name_debug = 0;
47 
48 typedef char nb_key_t[NETBIOS_HKEY_SZ];
49 static HT_HANDLE *smb_netbios_cache = 0;
50 static rwlock_t nb_cache_lock;
51 
52 static char *smb_strname(struct name_entry *name, char *buf, int bufsize);
53 static void hash_callback(HT_ITEM *item);
54 static int smb_netbios_match(const char *key1, const char *key2, size_t n);
55 static void smb_netbios_cache_key(char *key, unsigned char *name,
56 					unsigned char *scope);
57 
58 int
59 smb_netbios_cache_init()
60 {
61 	(void) rw_wrlock(&nb_cache_lock);
62 	if (smb_netbios_cache == 0) {
63 		smb_netbios_cache = ht_create_table(NETBIOS_HTAB_SZ,
64 		    NETBIOS_HKEY_SZ, HTHF_FIXED_KEY);
65 		if (smb_netbios_cache == 0) {
66 			syslog(LOG_ERR,
67 			    "smbd: cannot create NetBIOS name cache");
68 			(void) rw_unlock(&nb_cache_lock);
69 			return (0);
70 		}
71 		(void) ht_register_callback(smb_netbios_cache, hash_callback);
72 		ht_set_cmpfn(smb_netbios_cache, smb_netbios_match);
73 	}
74 	(void) rw_unlock(&nb_cache_lock);
75 
76 	return (1);
77 }
78 
79 void
80 smb_netbios_cache_fini(void)
81 {
82 	(void) rw_wrlock(&nb_cache_lock);
83 	ht_destroy_table(smb_netbios_cache);
84 	smb_netbios_cache = 0;
85 	(void) rw_unlock(&nb_cache_lock);
86 }
87 
88 void
89 smb_netbios_cache_clean(void)
90 {
91 	(void) rw_wrlock(&nb_cache_lock);
92 	(void) ht_clean_table(smb_netbios_cache);
93 	(void) rw_unlock(&nb_cache_lock);
94 }
95 
96 int
97 smb_netbios_cache_getfirst(nbcache_iter_t *iter)
98 {
99 	HT_ITEM *item;
100 	struct name_entry *entry;
101 
102 	(void) rw_rdlock(&nb_cache_lock);
103 	item = ht_findfirst(smb_netbios_cache, &iter->nbc_hti);
104 	if (item == NULL || item->hi_data == NULL) {
105 		(void) rw_unlock(&nb_cache_lock);
106 		return (-1);
107 	}
108 
109 	entry = (struct name_entry *)item->hi_data;
110 	(void) mutex_lock(&entry->mtx);
111 	iter->nbc_entry = smb_netbios_name_dup(entry, 1);
112 	(void) mutex_unlock(&entry->mtx);
113 
114 	(void) rw_unlock(&nb_cache_lock);
115 
116 	return ((iter->nbc_entry) ? 0 : -1);
117 }
118 
119 int
120 smb_netbios_cache_getnext(nbcache_iter_t *iter)
121 {
122 	HT_ITEM *item;
123 	struct name_entry *entry;
124 
125 	(void) rw_rdlock(&nb_cache_lock);
126 	item = ht_findnext(&iter->nbc_hti);
127 	if (item == NULL || item->hi_data == NULL) {
128 		(void) rw_unlock(&nb_cache_lock);
129 		return (-1);
130 	}
131 
132 	entry = (struct name_entry *)item->hi_data;
133 	(void) mutex_lock(&entry->mtx);
134 	iter->nbc_entry = smb_netbios_name_dup(entry, 1);
135 	(void) mutex_unlock(&entry->mtx);
136 
137 	(void) rw_unlock(&nb_cache_lock);
138 
139 	return ((iter->nbc_entry) ? 0 : -1);
140 }
141 
142 /*
143  * smb_netbios_cache_lookup
144  *
145  * Searches the name cache for the given entry, if found
146  * the entry will be locked before returning to caller
147  * so caller MUST unlock the entry after it's done with it.
148  */
149 struct name_entry *
150 smb_netbios_cache_lookup(struct name_entry *name)
151 {
152 	HT_ITEM *item;
153 	nb_key_t key;
154 	struct name_entry *entry = NULL;
155 	unsigned char hostname[MAXHOSTNAMELEN];
156 
157 	if (NETBIOS_NAME_IS_STAR(name->name)) {
158 		/* Return our address */
159 		if (smb_getnetbiosname((char *)hostname, sizeof (hostname))
160 		    != 0)
161 			return (NULL);
162 
163 		smb_encode_netbios_name(hostname, 0x00, NULL, name);
164 	}
165 
166 	(void) rw_rdlock(&nb_cache_lock);
167 
168 	smb_netbios_cache_key(key, name->name, name->scope);
169 	item = ht_find_item(smb_netbios_cache, key);
170 	if (item) {
171 		entry = (struct name_entry *)item->hi_data;
172 		(void) mutex_lock(&entry->mtx);
173 		if ((entry->attributes & NAME_ATTR_CONFLICT) != 0) {
174 			(void) mutex_unlock(&entry->mtx);
175 			entry = NULL;
176 		}
177 	}
178 
179 	(void) rw_unlock(&nb_cache_lock);
180 	return (entry);
181 }
182 
183 void
184 smb_netbios_cache_unlock_entry(struct name_entry *name)
185 {
186 	if (name)
187 		(void) mutex_unlock(&name->mtx);
188 }
189 
190 /*
191  * smb_netbios_cache_lookup_addr
192  *
193  * lookup the given 'name' in the cache and then checks
194  * if the address also matches with the found entry.
195  * 'name' is supposed to contain only one address.
196  *
197  * The found entry will be locked before returning to caller
198  * so caller MUST unlock the entry after it's done with it.
199  */
200 struct name_entry *
201 smb_netbios_cache_lookup_addr(struct name_entry *name)
202 {
203 	struct name_entry *entry = 0;
204 	struct addr_entry *addr;
205 	struct addr_entry *name_addr;
206 	HT_ITEM *item;
207 	nb_key_t key;
208 
209 	(void) rw_rdlock(&nb_cache_lock);
210 	smb_netbios_cache_key(key, name->name, name->scope);
211 	item = ht_find_item(smb_netbios_cache, key);
212 
213 	if (item && item->hi_data) {
214 		name_addr = &name->addr_list;
215 		entry = (struct name_entry *)item->hi_data;
216 		(void) mutex_lock(&entry->mtx);
217 		addr = &entry->addr_list;
218 		do {
219 			if (NETBIOS_SAME_IP(addr, name_addr)) {
220 				/* note that entry lock isn't released here */
221 				(void) rw_unlock(&nb_cache_lock);
222 				return (entry);
223 			}
224 			addr = addr->forw;
225 		} while (addr != &entry->addr_list);
226 		(void) mutex_unlock(&entry->mtx);
227 	}
228 
229 	(void) rw_unlock(&nb_cache_lock);
230 	return (0);
231 }
232 
233 int
234 smb_netbios_cache_insert(struct name_entry *name)
235 {
236 	struct name_entry *entry;
237 	struct addr_entry *addr;
238 	struct addr_entry *name_addr;
239 	HT_ITEM *item;
240 	nb_key_t key;
241 	int rc;
242 
243 	/* No point in adding a name with IP address 255.255.255.255 */
244 	if (name->addr_list.sin.sin_addr.s_addr == 0xffffffff)
245 		return (0);
246 
247 	(void) rw_wrlock(&nb_cache_lock);
248 	smb_netbios_cache_key(key, name->name, name->scope);
249 	item = ht_find_item(smb_netbios_cache, key);
250 
251 	if (item && item->hi_data) {
252 		/* Name already exists */
253 		entry = (struct name_entry *)item->hi_data;
254 		(void) mutex_lock(&entry->mtx);
255 
256 		name_addr = &name->addr_list;
257 		addr = &entry->addr_list;
258 		if (NETBIOS_SAME_IP(addr, name_addr) &&
259 		    (addr->sin.sin_port == name_addr->sin.sin_port)) {
260 			entry->attributes |=
261 			    name_addr->attributes & NAME_ATTR_LOCAL;
262 			(void) mutex_unlock(&entry->mtx);
263 			(void) rw_unlock(&nb_cache_lock);
264 			return (0);
265 		}
266 
267 		/* Was not primary: looks for others */
268 		for (addr = entry->addr_list.forw;
269 		    addr != &entry->addr_list; addr = addr->forw) {
270 			if (NETBIOS_SAME_IP(addr, name_addr) &&
271 			    (addr->sin.sin_port == name_addr->sin.sin_port)) {
272 				(void) mutex_unlock(&entry->mtx);
273 				(void) rw_unlock(&nb_cache_lock);
274 				return (0);
275 			}
276 		}
277 
278 		if ((addr = malloc(sizeof (struct addr_entry))) != NULL) {
279 			*addr = name->addr_list;
280 			entry->attributes |= addr->attributes;
281 			QUEUE_INSERT_TAIL(&entry->addr_list, addr);
282 			rc = 0;
283 		} else {
284 			rc = -1;
285 		}
286 
287 		(void) mutex_unlock(&entry->mtx);
288 		(void) rw_unlock(&nb_cache_lock);
289 		return (rc);
290 	}
291 
292 	if ((entry = malloc(sizeof (struct name_entry))) == NULL) {
293 		(void) rw_unlock(&nb_cache_lock);
294 		return (-1);
295 	}
296 
297 	*entry = *name;
298 	entry->addr_list.forw = entry->addr_list.back = &entry->addr_list;
299 	entry->attributes |= entry->addr_list.attributes;
300 	(void) mutex_init(&entry->mtx, 0, 0);
301 	if (ht_replace_item(smb_netbios_cache, key, entry) == 0) {
302 		free(entry);
303 		(void) rw_unlock(&nb_cache_lock);
304 		return (-1);
305 	}
306 
307 	(void) rw_unlock(&nb_cache_lock);
308 	return (0);
309 }
310 
311 
312 void
313 smb_netbios_cache_delete(struct name_entry *name)
314 {
315 	nb_key_t key;
316 	HT_ITEM *item;
317 	struct name_entry *entry;
318 
319 	(void) rw_wrlock(&nb_cache_lock);
320 	smb_netbios_cache_key(key, name->name, name->scope);
321 	item = ht_find_item(smb_netbios_cache, key);
322 	if (item && item->hi_data) {
323 		entry = (struct name_entry *)item->hi_data;
324 		(void) mutex_lock(&entry->mtx);
325 		ht_mark_delete(smb_netbios_cache, item);
326 		(void) mutex_unlock(&entry->mtx);
327 	}
328 	(void) rw_unlock(&nb_cache_lock);
329 }
330 
331 /*
332  * smb_netbios_cache_insert_list
333  *
334  * Insert a name with multiple addresses
335  */
336 int
337 smb_netbios_cache_insert_list(struct name_entry *name)
338 {
339 	struct name_entry entry;
340 	struct addr_entry *addr;
341 
342 	addr = &name->addr_list;
343 	do {
344 		smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, name->scope,
345 		    addr->sin.sin_addr.s_addr,
346 		    addr->sin.sin_port,
347 		    name->attributes,
348 		    addr->attributes,
349 		    &entry);
350 		(void) memcpy(entry.name, name->name, NETBIOS_NAME_SZ);
351 		entry.addr_list.refresh_ttl = entry.addr_list.ttl =
352 		    addr->refresh_ttl;
353 		(void) smb_netbios_cache_insert(&entry);
354 		addr = addr->forw;
355 	} while (addr != &name->addr_list);
356 
357 	return (0);
358 }
359 
360 void
361 smb_netbios_cache_update_entry(struct name_entry *entry,
362     struct name_entry *name)
363 {
364 	struct addr_entry *addr;
365 	struct addr_entry *name_addr;
366 
367 	addr = &entry->addr_list;
368 	name_addr = &name->addr_list;
369 
370 	if (IS_UNIQUE(entry->attributes)) {
371 		do {
372 			addr->ttl = name_addr->ttl;
373 			addr = addr->forw;
374 		} while (addr != &entry->addr_list);
375 
376 	} else {
377 		do {
378 			if (NETBIOS_SAME_IP(addr, name_addr) &&
379 			    (addr->sin.sin_port == name_addr->sin.sin_port)) {
380 				addr->ttl = name_addr->ttl;
381 				return;
382 			}
383 			addr = addr->forw;
384 		} while (addr != &entry->addr_list);
385 	}
386 }
387 
388 /*
389  * smb_netbios_cache_status
390  *
391  * Scan the name cache and gather status for
392  * Node Status response for names in the given scope
393  */
394 unsigned char *
395 smb_netbios_cache_status(unsigned char *buf, int bufsize, unsigned char *scope)
396 {
397 	HT_ITERATOR hti;
398 	HT_ITEM *item;
399 	struct name_entry *name;
400 	unsigned char *numnames;
401 	unsigned char *scan;
402 	unsigned char *scan_end;
403 
404 	scan = buf;
405 	scan_end = scan + bufsize;
406 
407 	numnames = scan++;
408 	*numnames = 0;
409 
410 	(void) rw_rdlock(&nb_cache_lock);
411 	item = ht_findfirst(smb_netbios_cache, &hti);
412 	do {
413 		if (item == 0)
414 			break;
415 
416 		if (item->hi_data == 0)
417 			continue;
418 
419 		if ((scan + NETBIOS_NAME_SZ + 2) >= scan_end)
420 			/* no room for adding next entry */
421 			break;
422 
423 		name = (struct name_entry *)item->hi_data;
424 		(void) mutex_lock(&name->mtx);
425 
426 		if (IS_LOCAL(name->attributes) &&
427 		    (strcasecmp((char *)scope, (char *)name->scope) == 0)) {
428 			bcopy(name->name, scan, NETBIOS_NAME_SZ);
429 			scan += NETBIOS_NAME_SZ;
430 			*scan++ = PUBLIC_BITS(name->attributes) >> 8;
431 			*scan++ = PUBLIC_BITS(name->attributes);
432 			(*numnames)++;
433 		}
434 
435 		(void) mutex_unlock(&name->mtx);
436 	} while ((item = ht_findnext(&hti)) != 0);
437 	(void) rw_unlock(&nb_cache_lock);
438 
439 	return (scan);
440 }
441 
442 void
443 smb_netbios_cache_reset_ttl()
444 {
445 	struct addr_entry *addr;
446 	struct name_entry *name;
447 	HT_ITERATOR hti;
448 	HT_ITEM *item;
449 
450 	(void) rw_rdlock(&nb_cache_lock);
451 	item = ht_findfirst(smb_netbios_cache, &hti);
452 	do {
453 		if (item == 0)
454 			break;
455 
456 		if (item->hi_data == 0)
457 			continue;
458 
459 		name = (struct name_entry *)item->hi_data;
460 		(void) mutex_lock(&name->mtx);
461 
462 		addr = &name->addr_list;
463 		do {
464 			if (addr->ttl < 1) {
465 				if (addr->refresh_ttl)
466 					addr->ttl = addr->refresh_ttl;
467 				else
468 					addr->refresh_ttl = addr->ttl =
469 					    TO_SECONDS(DEFAULT_TTL);
470 			}
471 			addr = addr->forw;
472 		} while (addr != &name->addr_list);
473 
474 		(void) mutex_unlock(&name->mtx);
475 	} while ((item = ht_findnext(&hti)) != 0);
476 	(void) rw_unlock(&nb_cache_lock);
477 }
478 
479 /*
480  * Returns TRUE when given name is added to the refresh queue
481  * FALSE if not.
482  */
483 static boolean_t
484 smb_netbios_cache_insrefq(name_queue_t *refq, HT_ITEM *item)
485 {
486 	struct name_entry *name;
487 	struct name_entry *refent;
488 
489 	name = (struct name_entry *)item->hi_data;
490 
491 	if (IS_LOCAL(name->attributes)) {
492 		if (IS_UNIQUE(name->attributes)) {
493 			refent = smb_netbios_name_dup(name, 1);
494 			if (refent)
495 				QUEUE_INSERT_TAIL(&refq->head, refent)
496 
497 			/* next name */
498 			return (B_TRUE);
499 		}
500 	} else {
501 		ht_mark_delete(smb_netbios_cache, item);
502 		refent = smb_netbios_name_dup(name, 0);
503 		if (refent)
504 			QUEUE_INSERT_TAIL(&refq->head, refent)
505 
506 		/* next name */
507 		return (B_TRUE);
508 	}
509 
510 	return (B_FALSE);
511 }
512 
513 /*
514  * smb_netbios_cache_refresh
515  *
516  * Scans the name cache and add all local unique names
517  * and non-local names the passed refresh queue. Non-
518  * local names will also be marked as deleted.
519  *
520  * NOTE that the caller MUST protect the queue using
521  * its mutex
522  */
523 void
524 smb_netbios_cache_refresh(name_queue_t *refq)
525 {
526 	struct name_entry *name;
527 	struct addr_entry *addr;
528 	HT_ITERATOR hti;
529 	HT_ITEM *item;
530 
531 	bzero(&refq->head, sizeof (refq->head));
532 	refq->head.forw = refq->head.back = &refq->head;
533 
534 	(void) rw_rdlock(&nb_cache_lock);
535 	item = ht_findfirst(smb_netbios_cache, &hti);
536 	do { /* name loop */
537 		if (item == 0)
538 			break;
539 
540 		if (item->hi_data == 0)
541 			continue;
542 
543 		name = (struct name_entry *)item->hi_data;
544 		(void) mutex_lock(&name->mtx);
545 
546 		addr = &name->addr_list;
547 		do { /* address loop */
548 			if (addr->ttl > 0) {
549 				addr->ttl--;
550 				if (addr->ttl == 0) {
551 					if (smb_netbios_cache_insrefq(refq,
552 					    item))
553 						break;
554 				}
555 			}
556 			addr = addr->forw;
557 		} while (addr != &name->addr_list);
558 
559 		(void) mutex_unlock(&name->mtx);
560 	} while ((item = ht_findnext(&hti)) != 0);
561 	(void) rw_unlock(&nb_cache_lock);
562 }
563 
564 /*
565  * smb_netbios_cache_delete_locals
566  *
567  * Scans the name cache and add all local names to
568  * the passed delete queue.
569  *
570  * NOTE that the caller MUST protect the queue using
571  * its mutex
572  */
573 void
574 smb_netbios_cache_delete_locals(name_queue_t *delq)
575 {
576 	struct name_entry *entry;
577 	struct name_entry *delent;
578 	HT_ITERATOR hti;
579 	HT_ITEM *item;
580 
581 	bzero(&delq->head, sizeof (delq->head));
582 	delq->head.forw = delq->head.back = &delq->head;
583 
584 	(void) rw_wrlock(&nb_cache_lock);
585 	item = ht_findfirst(smb_netbios_cache, &hti);
586 	do {
587 		if (item == 0)
588 			break;
589 
590 		if (item->hi_data == 0)
591 			continue;
592 
593 		entry = (struct name_entry *)item->hi_data;
594 		(void) mutex_lock(&entry->mtx);
595 
596 		if (IS_LOCAL(entry->attributes)) {
597 			ht_mark_delete(smb_netbios_cache, item);
598 			delent = smb_netbios_name_dup(entry, 1);
599 			if (delent)
600 				QUEUE_INSERT_TAIL(&delq->head, delent)
601 		}
602 
603 		(void) mutex_unlock(&entry->mtx);
604 	} while ((item = ht_findnext(&hti)) != 0);
605 	(void) rw_unlock(&nb_cache_lock);
606 }
607 
608 void
609 smb_netbios_name_freeaddrs(struct name_entry *entry)
610 {
611 	struct addr_entry *addr;
612 
613 	if (entry == 0)
614 		return;
615 
616 	while ((addr = entry->addr_list.forw) != &entry->addr_list) {
617 		QUEUE_CLIP(addr);
618 		free(addr);
619 	}
620 }
621 
622 /*
623  * smb_netbios_cache_count
624  *
625  * Returns the number of names in the cache
626  */
627 int
628 smb_netbios_cache_count()
629 {
630 	int cnt;
631 
632 	(void) rw_rdlock(&nb_cache_lock);
633 	cnt = ht_get_total_items(smb_netbios_cache);
634 	(void) rw_unlock(&nb_cache_lock);
635 
636 	return (cnt);
637 }
638 
639 void
640 smb_netbios_cache_dump(void)
641 {
642 	struct name_entry *name;
643 	HT_ITERATOR hti;
644 	HT_ITEM *item;
645 
646 	(void) rw_rdlock(&nb_cache_lock);
647 	item = ht_findfirst(smb_netbios_cache, &hti);
648 	while (item) {
649 		if (item->hi_data) {
650 			name = (struct name_entry *)item->hi_data;
651 			(void) mutex_lock(&name->mtx);
652 			smb_netbios_name_dump(name);
653 			(void) mutex_unlock(&name->mtx);
654 		}
655 		item = ht_findnext(&hti);
656 	}
657 	(void) rw_unlock(&nb_cache_lock);
658 }
659 
660 void
661 smb_netbios_name_dump(struct name_entry *entry)
662 {
663 	struct addr_entry *addr;
664 	int count = 0;
665 
666 	if (smb_netbios_name_debug == 0)
667 		return;
668 
669 	syslog(LOG_DEBUG, "name='%15.15s<%02X>' scope='%s' attr=0x%x",
670 	    entry->name, entry->name[15],
671 	    entry->scope, entry->attributes);
672 	addr = &entry->addr_list;
673 	do {
674 		syslog(LOG_DEBUG, "addr_list[%d]:", count++);
675 		syslog(LOG_DEBUG, "  attributes     = 0x%x", addr->attributes);
676 		syslog(LOG_DEBUG, "  conflict_timer = %d",
677 		    addr->conflict_timer);
678 		syslog(LOG_DEBUG, "  refresh_ttl    = %d", addr->refresh_ttl);
679 		syslog(LOG_DEBUG, "  ttl            = %d", addr->ttl);
680 		syslog(LOG_DEBUG, "  sin.sin_addr   = %s",
681 		    inet_ntoa(addr->sin.sin_addr));
682 		syslog(LOG_DEBUG, "  sin.sin_port   = %d", addr->sin.sin_port);
683 		syslog(LOG_DEBUG, "  sin.sinlen     = %d", addr->sinlen);
684 		addr = addr->forw;
685 	} while (addr != &entry->addr_list);
686 }
687 
688 void
689 smb_netbios_name_logf(struct name_entry *entry)
690 {
691 	struct addr_entry *addr;
692 	NETBIOS_NAMEBUF;
693 
694 	(void) smb_strname(entry, namebuf, sizeof (namebuf));
695 	syslog(LOG_DEBUG, "%s flags=0x%x\n", namebuf, entry->attributes);
696 	addr = &entry->addr_list;
697 	do {
698 		syslog(LOG_DEBUG, "  %s ttl=%d flags=0x%x",
699 		    inet_ntoa(addr->sin.sin_addr),
700 		    addr->ttl, addr->attributes);
701 		addr = addr->forw;
702 	} while (addr && (addr != &entry->addr_list));
703 }
704 
705 /*
706  * smb_netbios_name_dup
707  *
708  * Duplicate the given name entry. If 'alladdr' is 0 only
709  * copy the primary address otherwise duplicate all the
710  * addresses. NOTE that the duplicate structure is not
711  * like a regular cache entry i.e. it's a contiguous block
712  * of memory and each addr structure doesn't have it's own
713  * allocated memory. So, the returned structure can be freed
714  * by one free call.
715  */
716 struct name_entry *
717 smb_netbios_name_dup(struct name_entry *entry, int alladdr)
718 {
719 	struct addr_entry *addr;
720 	struct addr_entry *dup_addr;
721 	struct name_entry *dup;
722 	int addr_cnt = 0;
723 	int size = 0;
724 
725 	if (alladdr) {
726 		addr = entry->addr_list.forw;
727 		while (addr && (addr != &entry->addr_list)) {
728 			addr_cnt++;
729 			addr = addr->forw;
730 		}
731 	}
732 
733 	size = sizeof (struct name_entry) +
734 	    (addr_cnt * sizeof (struct addr_entry));
735 	dup = (struct name_entry *)malloc(size);
736 	if (dup == 0)
737 		return (0);
738 
739 	bzero(dup, size);
740 
741 	dup->forw = dup->back = dup;
742 	dup->attributes = entry->attributes;
743 	(void) memcpy(dup->name, entry->name, NETBIOS_NAME_SZ);
744 	(void) strlcpy((char *)dup->scope, (char *)entry->scope,
745 	    NETBIOS_DOMAIN_NAME_MAX);
746 	dup->addr_list = entry->addr_list;
747 	dup->addr_list.forw = dup->addr_list.back = &dup->addr_list;
748 
749 	if (alladdr == 0)
750 		return (dup);
751 
752 	/* LINTED - E_BAD_PTR_CAST_ALIGN */
753 	dup_addr = (struct addr_entry *)((unsigned char *)dup +
754 	    sizeof (struct name_entry));
755 
756 	addr = entry->addr_list.forw;
757 	while (addr && (addr != &entry->addr_list)) {
758 		*dup_addr = *addr;
759 		QUEUE_INSERT_TAIL(&dup->addr_list, dup_addr);
760 		addr = addr->forw;
761 		dup_addr++;
762 	}
763 
764 	return (dup);
765 }
766 
767 static char *
768 smb_strname(struct name_entry *name, char *buf, int bufsize)
769 {
770 	char *p;
771 
772 	(void) snprintf(buf, bufsize, "%15.15s", name->name);
773 	p = strchr(buf, ' ');
774 	if (p)
775 		(void) snprintf(p, 5, "<%02X>", name->name[15]);
776 
777 	return (buf);
778 }
779 
780 static void
781 hash_callback(HT_ITEM *item)
782 {
783 	struct name_entry *entry;
784 
785 	if (item && item->hi_data) {
786 		entry = (struct name_entry *)item->hi_data;
787 		smb_netbios_name_freeaddrs(entry);
788 		free(entry);
789 	}
790 }
791 
792 
793 /*ARGSUSED*/
794 static int
795 smb_netbios_match(const char *key1, const char *key2, size_t n)
796 {
797 	int res;
798 
799 	res = bcmp(key1, key2, NETBIOS_NAME_SZ);
800 	if (res == 0) {
801 		/* Names are the same, compare scopes */
802 		res = strcmp(key1 + NETBIOS_NAME_SZ, key2 + NETBIOS_NAME_SZ);
803 	}
804 
805 	return (res);
806 }
807 
808 static void
809 smb_netbios_cache_key(char *key, unsigned char *name, unsigned char *scope)
810 {
811 	bzero(key, NETBIOS_HKEY_SZ);
812 	(void) memcpy(key, name, NETBIOS_NAME_SZ);
813 	(void) memcpy(key + NETBIOS_NAME_SZ, scope,
814 	    strlen((const char *)scope));
815 }
816