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