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
42typedef char nb_key_t[NETBIOS_HKEY_SZ];
43static HT_HANDLE *smb_netbios_cache = 0;
44static rwlock_t nb_cache_lock;
45
46static void smb_strname(struct name_entry *name, char *buf, int bufsize);
47static void hash_callback(HT_ITEM *item);
48static int smb_netbios_match(const char *key1, const char *key2, size_t n);
49static void smb_netbios_cache_key(char *key, unsigned char *name,
50					unsigned char *scope);
51
52int
53smb_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
72void
73smb_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
81void
82smb_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
89int
90smb_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
112int
113smb_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 */
142struct name_entry *
143smb_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
176void
177smb_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 */
193struct name_entry *
194smb_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
226int
227smb_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
305void
306smb_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 */
329int
330smb_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
353void
354smb_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 */
387unsigned char *
388smb_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
435void
436smb_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 */
476static boolean_t
477smb_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 */
518void
519smb_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 */
568void
569smb_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
604void
605smb_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 */
623int
624smb_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
635void
636smb_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
665void
666smb_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
692void
693smb_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 */
721struct name_entry *
722smb_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
772static void
773smb_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
790static void
791hash_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*/
804static int
805smb_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
818static void
819smb_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