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