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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
30 #include <arpa/inet.h>
33 #include <smbsrv/libsmbns.h>
34 #include <smbns_netbios.h>
36 #define NETBIOS_HTAB_SZ 128
37 #define NETBIOS_HKEY_SZ (NETBIOS_NAME_SZ + NETBIOS_DOMAIN_NAME_MAX)
39 #define NETBIOS_SAME_IP(addr1, addr2) \
40 ((addr1)->sin.sin_addr.s_addr == (addr2)->sin.sin_addr.s_addr)
42 typedef char nb_key_t
[NETBIOS_HKEY_SZ
];
43 static HT_HANDLE
*smb_netbios_cache
= 0;
44 static rwlock_t nb_cache_lock
;
46 static void smb_strname(struct name_entry
*name
, char *buf
, int bufsize
);
47 static void hash_callback(HT_ITEM
*item
);
48 static int smb_netbios_match(const char *key1
, const char *key2
, size_t n
);
49 static void smb_netbios_cache_key(char *key
, unsigned char *name
,
50 unsigned char *scope
);
53 smb_netbios_cache_init(void)
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
);
64 (void) ht_register_callback(smb_netbios_cache
, hash_callback
);
65 ht_set_cmpfn(smb_netbios_cache
, smb_netbios_match
);
67 (void) rw_unlock(&nb_cache_lock
);
73 smb_netbios_cache_fini(void)
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
);
82 smb_netbios_cache_clean(void)
84 (void) rw_wrlock(&nb_cache_lock
);
85 (void) ht_clean_table(smb_netbios_cache
);
86 (void) rw_unlock(&nb_cache_lock
);
90 smb_netbios_cache_getfirst(nbcache_iter_t
*iter
)
93 struct name_entry
*entry
;
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
);
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
);
107 (void) rw_unlock(&nb_cache_lock
);
109 return ((iter
->nbc_entry
) ? 0 : -1);
113 smb_netbios_cache_getnext(nbcache_iter_t
*iter
)
116 struct name_entry
*entry
;
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
);
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
);
130 (void) rw_unlock(&nb_cache_lock
);
132 return ((iter
->nbc_entry
) ? 0 : -1);
136 * smb_netbios_cache_lookup
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.
143 smb_netbios_cache_lookup(struct name_entry
*name
)
147 struct name_entry
*entry
= NULL
;
148 unsigned char hostname
[MAXHOSTNAMELEN
];
150 if (NETBIOS_NAME_IS_STAR(name
->name
)) {
151 /* Return our address */
152 if (smb_getnetbiosname((char *)hostname
, sizeof (hostname
))
156 smb_encode_netbios_name(hostname
, 0x00, NULL
, name
);
159 (void) rw_rdlock(&nb_cache_lock
);
161 smb_netbios_cache_key(key
, name
->name
, name
->scope
);
162 item
= ht_find_item(smb_netbios_cache
, key
);
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
);
172 (void) rw_unlock(&nb_cache_lock
);
177 smb_netbios_cache_unlock_entry(struct name_entry
*name
)
180 (void) mutex_unlock(&name
->mtx
);
184 * smb_netbios_cache_lookup_addr
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.
190 * The found entry will be locked before returning to caller
191 * so caller MUST unlock the entry after it's done with it.
194 smb_netbios_cache_lookup_addr(struct name_entry
*name
)
196 struct name_entry
*entry
= 0;
198 addr_entry_t
*name_addr
;
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
);
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
;
212 if (NETBIOS_SAME_IP(addr
, name_addr
)) {
213 /* note that entry lock isn't released here */
214 (void) rw_unlock(&nb_cache_lock
);
218 } while (addr
!= &entry
->addr_list
);
219 (void) mutex_unlock(&entry
->mtx
);
222 (void) rw_unlock(&nb_cache_lock
);
227 smb_netbios_cache_insert(struct name_entry
*name
)
229 struct name_entry
*entry
;
231 addr_entry_t
*name_addr
;
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)
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
);
244 if (item
&& item
->hi_data
) {
245 /* Name already exists */
246 entry
= (struct name_entry
*)item
->hi_data
;
247 (void) mutex_lock(&entry
->mtx
);
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
)) {
254 name_addr
->attributes
& NAME_ATTR_LOCAL
;
255 (void) mutex_unlock(&entry
->mtx
);
256 (void) rw_unlock(&nb_cache_lock
);
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
);
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
);
280 (void) mutex_unlock(&entry
->mtx
);
281 (void) rw_unlock(&nb_cache_lock
);
285 if ((entry
= malloc(sizeof (struct name_entry
))) == NULL
) {
286 (void) rw_unlock(&nb_cache_lock
);
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) {
296 (void) rw_unlock(&nb_cache_lock
);
300 (void) rw_unlock(&nb_cache_lock
);
306 smb_netbios_cache_delete(struct name_entry
*name
)
310 struct name_entry
*entry
;
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
);
321 (void) rw_unlock(&nb_cache_lock
);
325 * smb_netbios_cache_insert_list
327 * Insert a name with multiple addresses
330 smb_netbios_cache_insert_list(struct name_entry
*name
)
332 struct name_entry entry
;
335 addr
= &name
->addr_list
;
337 smb_init_name_struct(NETBIOS_EMPTY_NAME
, 0, name
->scope
,
338 addr
->sin
.sin_addr
.s_addr
,
343 (void) memcpy(entry
.name
, name
->name
, NETBIOS_NAME_SZ
);
344 entry
.addr_list
.refresh_ttl
= entry
.addr_list
.ttl
=
346 (void) smb_netbios_cache_insert(&entry
);
348 } while (addr
!= &name
->addr_list
);
354 smb_netbios_cache_update_entry(struct name_entry
*entry
,
355 struct name_entry
*name
)
358 addr_entry_t
*name_addr
;
360 addr
= &entry
->addr_list
;
361 name_addr
= &name
->addr_list
;
363 if (IS_UNIQUE(entry
->attributes
)) {
365 addr
->ttl
= name_addr
->ttl
;
367 } while (addr
!= &entry
->addr_list
);
371 if (NETBIOS_SAME_IP(addr
, name_addr
) &&
372 (addr
->sin
.sin_port
== name_addr
->sin
.sin_port
)) {
373 addr
->ttl
= name_addr
->ttl
;
377 } while (addr
!= &entry
->addr_list
);
382 * smb_netbios_cache_status
384 * Scan the name cache and gather status for
385 * Node Status response for names in the given scope
388 smb_netbios_cache_status(unsigned char *buf
, int bufsize
, unsigned char *scope
)
392 struct name_entry
*name
;
393 unsigned char *numnames
;
395 unsigned char *scan_end
;
398 scan_end
= scan
+ bufsize
;
403 (void) rw_rdlock(&nb_cache_lock
);
404 item
= ht_findfirst(smb_netbios_cache
, &hti
);
409 if (item
->hi_data
== 0)
412 if ((scan
+ NETBIOS_NAME_SZ
+ 2) >= scan_end
)
413 /* no room for adding next entry */
416 name
= (struct name_entry
*)item
->hi_data
;
417 (void) mutex_lock(&name
->mtx
);
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;
428 (void) mutex_unlock(&name
->mtx
);
429 } while ((item
= ht_findnext(&hti
)) != 0);
430 (void) rw_unlock(&nb_cache_lock
);
436 smb_netbios_cache_reset_ttl()
439 struct name_entry
*name
;
443 (void) rw_rdlock(&nb_cache_lock
);
444 item
= ht_findfirst(smb_netbios_cache
, &hti
);
449 if (item
->hi_data
== 0)
452 name
= (struct name_entry
*)item
->hi_data
;
453 (void) mutex_lock(&name
->mtx
);
455 addr
= &name
->addr_list
;
458 if (addr
->refresh_ttl
)
459 addr
->ttl
= addr
->refresh_ttl
;
461 addr
->refresh_ttl
= addr
->ttl
=
462 TO_SECONDS(DEFAULT_TTL
);
465 } while (addr
!= &name
->addr_list
);
467 (void) mutex_unlock(&name
->mtx
);
468 } while ((item
= ht_findnext(&hti
)) != 0);
469 (void) rw_unlock(&nb_cache_lock
);
473 * Returns TRUE when given name is added to the refresh queue
477 smb_netbios_cache_insrefq(name_queue_t
*refq
, HT_ITEM
*item
)
479 struct name_entry
*name
;
480 struct name_entry
*refent
;
482 name
= (struct name_entry
*)item
->hi_data
;
484 if (IS_LOCAL(name
->attributes
)) {
485 if (IS_UNIQUE(name
->attributes
)) {
486 refent
= smb_netbios_name_dup(name
, 1);
488 QUEUE_INSERT_TAIL(&refq
->head
, refent
)
494 ht_mark_delete(smb_netbios_cache
, item
);
495 refent
= smb_netbios_name_dup(name
, 0);
497 QUEUE_INSERT_TAIL(&refq
->head
, refent
)
507 * smb_netbios_cache_refresh
509 * Scans the name cache and add all local unique names
510 * and non-local names the passed refresh queue. Non-
511 * local names will also be marked as deleted.
513 * NOTE that the caller MUST protect the queue using
517 smb_netbios_cache_refresh(name_queue_t
*refq
)
519 struct name_entry
*name
;
524 bzero(&refq
->head
, sizeof (refq
->head
));
525 refq
->head
.forw
= refq
->head
.back
= &refq
->head
;
527 (void) rw_rdlock(&nb_cache_lock
);
528 item
= ht_findfirst(smb_netbios_cache
, &hti
);
533 if (item
->hi_data
== 0)
536 name
= (struct name_entry
*)item
->hi_data
;
537 (void) mutex_lock(&name
->mtx
);
539 addr
= &name
->addr_list
;
540 do { /* address loop */
543 if (addr
->ttl
== 0) {
544 if (smb_netbios_cache_insrefq(refq
,
550 } while (addr
!= &name
->addr_list
);
552 (void) mutex_unlock(&name
->mtx
);
553 } while ((item
= ht_findnext(&hti
)) != 0);
554 (void) rw_unlock(&nb_cache_lock
);
558 * smb_netbios_cache_delete_locals
560 * Scans the name cache and add all local names to
561 * the passed delete queue.
563 * NOTE that the caller MUST protect the queue using
567 smb_netbios_cache_delete_locals(name_queue_t
*delq
)
569 struct name_entry
*entry
;
570 struct name_entry
*delent
;
574 bzero(&delq
->head
, sizeof (delq
->head
));
575 delq
->head
.forw
= delq
->head
.back
= &delq
->head
;
577 (void) rw_wrlock(&nb_cache_lock
);
578 item
= ht_findfirst(smb_netbios_cache
, &hti
);
583 if (item
->hi_data
== 0)
586 entry
= (struct name_entry
*)item
->hi_data
;
587 (void) mutex_lock(&entry
->mtx
);
589 if (IS_LOCAL(entry
->attributes
)) {
590 ht_mark_delete(smb_netbios_cache
, item
);
591 delent
= smb_netbios_name_dup(entry
, 1);
593 QUEUE_INSERT_TAIL(&delq
->head
, delent
)
596 (void) mutex_unlock(&entry
->mtx
);
597 } while ((item
= ht_findnext(&hti
)) != 0);
598 (void) rw_unlock(&nb_cache_lock
);
602 smb_netbios_name_freeaddrs(struct name_entry
*entry
)
609 while ((addr
= entry
->addr_list
.forw
) != &entry
->addr_list
) {
616 * smb_netbios_cache_count
618 * Returns the number of names in the cache
621 smb_netbios_cache_count()
625 (void) rw_rdlock(&nb_cache_lock
);
626 cnt
= ht_get_total_items(smb_netbios_cache
);
627 (void) rw_unlock(&nb_cache_lock
);
633 smb_netbios_cache_dump(FILE *fp
)
635 struct name_entry
*name
;
639 (void) rw_rdlock(&nb_cache_lock
);
641 if (ht_get_total_items(smb_netbios_cache
) != 0) {
642 (void) fprintf(fp
, "\n%-22s %-16s %-16s %s\n",
643 "Name", "Type", "Address", "TTL");
644 (void) fprintf(fp
, "%s%s\n",
645 "-------------------------------",
646 "------------------------------");
649 item
= ht_findfirst(smb_netbios_cache
, &hti
);
652 name
= (struct name_entry
*)item
->hi_data
;
653 (void) mutex_lock(&name
->mtx
);
654 smb_netbios_name_dump(fp
, name
);
655 (void) mutex_unlock(&name
->mtx
);
657 item
= ht_findnext(&hti
);
659 (void) rw_unlock(&nb_cache_lock
);
663 smb_netbios_name_dump(FILE *fp
, struct name_entry
*entry
)
665 char buf
[MAXHOSTNAMELEN
];
670 smb_strname(entry
, buf
, sizeof (buf
));
671 type
= (IS_UNIQUE(entry
->attributes
)) ? "UNIQUE" : "GROUP";
673 (void) fprintf(fp
, "%s %-6s (0x%04x) ", buf
, type
, entry
->attributes
);
675 addr
= &entry
->addr_list
;
678 (void) fprintf(fp
, "%-16s %d\n",
679 inet_ntoa(addr
->sin
.sin_addr
), addr
->ttl
);
681 (void) fprintf(fp
, "%-28s (0x%04x) %-16s %d\n",
682 " ", addr
->attributes
,
683 inet_ntoa(addr
->sin
.sin_addr
), addr
->ttl
);
686 } while (addr
!= &entry
->addr_list
);
690 smb_netbios_name_logf(struct name_entry
*entry
)
692 char namebuf
[MAXHOSTNAMELEN
];
695 smb_strname(entry
, namebuf
, sizeof (namebuf
));
696 syslog(LOG_DEBUG
, "%s flags=0x%x\n", namebuf
, entry
->attributes
);
697 addr
= &entry
->addr_list
;
699 syslog(LOG_DEBUG
, " %s ttl=%d flags=0x%x port=%d",
700 inet_ntoa(addr
->sin
.sin_addr
),
701 addr
->ttl
, addr
->attributes
,
704 } while (addr
&& (addr
!= &entry
->addr_list
));
708 * smb_netbios_name_dup
710 * Duplicate the given name entry. If 'alladdr' is 0 only
711 * copy the primary address otherwise duplicate all the
712 * addresses. NOTE that the duplicate structure is not
713 * like a regular cache entry i.e. it's a contiguous block
714 * of memory and each addr structure doesn't have it's own
715 * allocated memory. So, the returned structure can be freed
719 smb_netbios_name_dup(struct name_entry
*entry
, int alladdr
)
722 addr_entry_t
*dup_addr
;
723 struct name_entry
*dup
;
728 addr
= entry
->addr_list
.forw
;
729 while (addr
&& (addr
!= &entry
->addr_list
)) {
735 size
= sizeof (struct name_entry
) +
736 (addr_cnt
* sizeof (addr_entry_t
));
737 dup
= (struct name_entry
*)malloc(size
);
743 dup
->forw
= dup
->back
= dup
;
744 dup
->attributes
= entry
->attributes
;
745 (void) memcpy(dup
->name
, entry
->name
, NETBIOS_NAME_SZ
);
746 (void) strlcpy((char *)dup
->scope
, (char *)entry
->scope
,
747 NETBIOS_DOMAIN_NAME_MAX
);
748 dup
->addr_list
= entry
->addr_list
;
749 dup
->addr_list
.forw
= dup
->addr_list
.back
= &dup
->addr_list
;
754 /* LINTED - E_BAD_PTR_CAST_ALIGN */
755 dup_addr
= (addr_entry_t
*)((unsigned char *)dup
+
756 sizeof (struct name_entry
));
758 addr
= entry
->addr_list
.forw
;
759 while (addr
&& (addr
!= &entry
->addr_list
)) {
761 QUEUE_INSERT_TAIL(&dup
->addr_list
, dup_addr
);
770 smb_strname(struct name_entry
*entry
, char *buf
, int bufsize
)
772 char tmp
[MAXHOSTNAMELEN
];
775 (void) snprintf(tmp
, MAXHOSTNAMELEN
, "%15.15s", entry
->name
);
776 if ((p
= strchr(tmp
, ' ')) != NULL
)
779 if (entry
->scope
[0] != '\0') {
780 (void) strlcat(tmp
, ".", MAXHOSTNAMELEN
);
781 (void) strlcat(tmp
, (char *)entry
->scope
, MAXHOSTNAMELEN
);
784 (void) snprintf(buf
, bufsize
, "%-16s <%02X>", tmp
, entry
->name
[15]);
788 hash_callback(HT_ITEM
*item
)
790 struct name_entry
*entry
;
792 if (item
&& item
->hi_data
) {
793 entry
= (struct name_entry
*)item
->hi_data
;
794 smb_netbios_name_freeaddrs(entry
);
802 smb_netbios_match(const char *key1
, const char *key2
, size_t n
)
806 res
= bcmp(key1
, key2
, NETBIOS_NAME_SZ
);
808 /* Names are the same, compare scopes */
809 res
= strcmp(key1
+ NETBIOS_NAME_SZ
, key2
+ NETBIOS_NAME_SZ
);
816 smb_netbios_cache_key(char *key
, unsigned char *name
, unsigned char *scope
)
818 bzero(key
, NETBIOS_HKEY_SZ
);
819 (void) memcpy(key
, name
, NETBIOS_NAME_SZ
);
820 (void) memcpy(key
+ NETBIOS_NAME_SZ
, scope
,
821 strlen((const char *)scope
));