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