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