2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <synch.h>
2N/A#include <stdio.h>
2N/A#include <syslog.h>
2N/A#include <stdlib.h>
2N/A#include <arpa/inet.h>
2N/A#include <netdb.h>
2N/A
2N/A#include <smbsrv/libsmbns.h>
2N/A#include <smbns_netbios.h>
2N/A#include <smbns_hash.h>
2N/A
2N/A#define NETBIOS_HTAB_SZ 128
2N/A#define NETBIOS_HKEY_SZ (NETBIOS_NAME_SZ + NETBIOS_DOMAIN_NAME_MAX)
2N/A
2N/A#define NETBIOS_SAME_IP(addr1, addr2) \
2N/A ((addr1)->sin.sin_addr.s_addr == (addr2)->sin.sin_addr.s_addr)
2N/A
2N/Atypedef char nb_key_t[NETBIOS_HKEY_SZ];
2N/Astatic HT_HANDLE *smb_netbios_cache = 0;
2N/Astatic rwlock_t nb_cache_lock;
2N/A
2N/Astatic void smb_strname(struct name_entry *name, char *buf, int bufsize);
2N/Astatic void hash_callback(HT_ITEM *item);
2N/Astatic int smb_netbios_match(const char *key1, const char *key2, size_t n);
2N/Astatic void smb_netbios_cache_key(char *key, unsigned char *name,
2N/A unsigned char *scope);
2N/A
2N/Aint
2N/Asmb_netbios_cache_init(void)
2N/A{
2N/A (void) rw_wrlock(&nb_cache_lock);
2N/A if (smb_netbios_cache == NULL) {
2N/A smb_netbios_cache = ht_create_table(NETBIOS_HTAB_SZ,
2N/A NETBIOS_HKEY_SZ, HTHF_FIXED_KEY);
2N/A if (smb_netbios_cache == NULL) {
2N/A syslog(LOG_ERR, "nbns: cannot create name cache");
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (-1);
2N/A }
2N/A (void) ht_register_callback(smb_netbios_cache, hash_callback);
2N/A ht_set_cmpfn(smb_netbios_cache, smb_netbios_match);
2N/A }
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_cache_fini(void)
2N/A{
2N/A (void) rw_wrlock(&nb_cache_lock);
2N/A ht_destroy_table(smb_netbios_cache);
2N/A smb_netbios_cache = NULL;
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_cache_clean(void)
2N/A{
2N/A (void) rw_wrlock(&nb_cache_lock);
2N/A (void) ht_clean_table(smb_netbios_cache);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A}
2N/A
2N/Aint
2N/Asmb_netbios_cache_getfirst(nbcache_iter_t *iter)
2N/A{
2N/A HT_ITEM *item;
2N/A struct name_entry *entry;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A item = ht_findfirst(smb_netbios_cache, &iter->nbc_hti);
2N/A if (item == NULL || item->hi_data == NULL) {
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A entry = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&entry->mtx);
2N/A iter->nbc_entry = smb_netbios_name_dup(entry, 1);
2N/A (void) mutex_unlock(&entry->mtx);
2N/A
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A
2N/A return ((iter->nbc_entry) ? 0 : -1);
2N/A}
2N/A
2N/Aint
2N/Asmb_netbios_cache_getnext(nbcache_iter_t *iter)
2N/A{
2N/A HT_ITEM *item;
2N/A struct name_entry *entry;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A item = ht_findnext(&iter->nbc_hti);
2N/A if (item == NULL || item->hi_data == NULL) {
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A entry = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&entry->mtx);
2N/A iter->nbc_entry = smb_netbios_name_dup(entry, 1);
2N/A (void) mutex_unlock(&entry->mtx);
2N/A
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A
2N/A return ((iter->nbc_entry) ? 0 : -1);
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_cache_lookup
2N/A *
2N/A * Searches the name cache for the given entry, if found
2N/A * the entry will be locked before returning to caller
2N/A * so caller MUST unlock the entry after it's done with it.
2N/A */
2N/Astruct name_entry *
2N/Asmb_netbios_cache_lookup(struct name_entry *name)
2N/A{
2N/A HT_ITEM *item;
2N/A nb_key_t key;
2N/A struct name_entry *entry = NULL;
2N/A unsigned char hostname[MAXHOSTNAMELEN];
2N/A
2N/A if (NETBIOS_NAME_IS_STAR(name->name)) {
2N/A /* Return our address */
2N/A if (smb_getnetbiosname((char *)hostname, sizeof (hostname))
2N/A != 0)
2N/A return (NULL);
2N/A
2N/A smb_encode_netbios_name(hostname, 0x00, NULL, name);
2N/A }
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A
2N/A smb_netbios_cache_key(key, name->name, name->scope);
2N/A item = ht_find_item(smb_netbios_cache, key);
2N/A if (item) {
2N/A entry = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&entry->mtx);
2N/A if ((entry->attributes & NAME_ATTR_CONFLICT) != 0) {
2N/A (void) mutex_unlock(&entry->mtx);
2N/A entry = NULL;
2N/A }
2N/A }
2N/A
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (entry);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_cache_unlock_entry(struct name_entry *name)
2N/A{
2N/A if (name)
2N/A (void) mutex_unlock(&name->mtx);
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_cache_lookup_addr
2N/A *
2N/A * lookup the given 'name' in the cache and then checks
2N/A * if the address also matches with the found entry.
2N/A * 'name' is supposed to contain only one address.
2N/A *
2N/A * The found entry will be locked before returning to caller
2N/A * so caller MUST unlock the entry after it's done with it.
2N/A */
2N/Astruct name_entry *
2N/Asmb_netbios_cache_lookup_addr(struct name_entry *name)
2N/A{
2N/A struct name_entry *entry = 0;
2N/A addr_entry_t *addr;
2N/A addr_entry_t *name_addr;
2N/A HT_ITEM *item;
2N/A nb_key_t key;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A smb_netbios_cache_key(key, name->name, name->scope);
2N/A item = ht_find_item(smb_netbios_cache, key);
2N/A
2N/A if (item && item->hi_data) {
2N/A name_addr = &name->addr_list;
2N/A entry = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&entry->mtx);
2N/A addr = &entry->addr_list;
2N/A do {
2N/A if (NETBIOS_SAME_IP(addr, name_addr)) {
2N/A /* note that entry lock isn't released here */
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (entry);
2N/A }
2N/A addr = addr->forw;
2N/A } while (addr != &entry->addr_list);
2N/A (void) mutex_unlock(&entry->mtx);
2N/A }
2N/A
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmb_netbios_cache_insert(struct name_entry *name)
2N/A{
2N/A struct name_entry *entry;
2N/A addr_entry_t *addr;
2N/A addr_entry_t *name_addr;
2N/A HT_ITEM *item;
2N/A nb_key_t key;
2N/A int rc;
2N/A
2N/A /* No point in adding a name with IP address 255.255.255.255 */
2N/A if (name->addr_list.sin.sin_addr.s_addr == 0xffffffff)
2N/A return (0);
2N/A
2N/A (void) rw_wrlock(&nb_cache_lock);
2N/A smb_netbios_cache_key(key, name->name, name->scope);
2N/A item = ht_find_item(smb_netbios_cache, key);
2N/A
2N/A if (item && item->hi_data) {
2N/A /* Name already exists */
2N/A entry = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&entry->mtx);
2N/A
2N/A name_addr = &name->addr_list;
2N/A addr = &entry->addr_list;
2N/A if (NETBIOS_SAME_IP(addr, name_addr) &&
2N/A (addr->sin.sin_port == name_addr->sin.sin_port)) {
2N/A entry->attributes |=
2N/A name_addr->attributes & NAME_ATTR_LOCAL;
2N/A (void) mutex_unlock(&entry->mtx);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (0);
2N/A }
2N/A
2N/A /* Was not primary: looks for others */
2N/A for (addr = entry->addr_list.forw;
2N/A addr != &entry->addr_list; addr = addr->forw) {
2N/A if (NETBIOS_SAME_IP(addr, name_addr) &&
2N/A (addr->sin.sin_port == name_addr->sin.sin_port)) {
2N/A (void) mutex_unlock(&entry->mtx);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A if ((addr = calloc(1, sizeof (addr_entry_t))) != NULL) {
2N/A *addr = name->addr_list;
2N/A name->addr_list.forw = name->addr_list.back =
2N/A &name->addr_list;
2N/A entry->attributes |= addr->attributes;
2N/A QUEUE_INSERT_TAIL(&entry->addr_list, addr);
2N/A rc = 0;
2N/A } else {
2N/A rc = -1;
2N/A }
2N/A
2N/A (void) mutex_unlock(&entry->mtx);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (rc);
2N/A }
2N/A
2N/A if ((entry = calloc(1, sizeof (struct name_entry))) == NULL) {
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A *entry = *name;
2N/A entry->addr_list.forw = entry->addr_list.back = &entry->addr_list;
2N/A entry->attributes |= entry->addr_list.attributes;
2N/A (void) mutex_init(&entry->mtx, 0, 0);
2N/A if (ht_replace_item(smb_netbios_cache, key, entry) == 0) {
2N/A free(entry);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (-1);
2N/A }
2N/A
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A return (0);
2N/A}
2N/A
2N/A
2N/Avoid
2N/Asmb_netbios_cache_delete(struct name_entry *name)
2N/A{
2N/A nb_key_t key;
2N/A HT_ITEM *item;
2N/A struct name_entry *entry;
2N/A
2N/A (void) rw_wrlock(&nb_cache_lock);
2N/A smb_netbios_cache_key(key, name->name, name->scope);
2N/A item = ht_find_item(smb_netbios_cache, key);
2N/A if (item && item->hi_data) {
2N/A entry = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&entry->mtx);
2N/A ht_mark_delete(smb_netbios_cache, item);
2N/A (void) mutex_unlock(&entry->mtx);
2N/A }
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_cache_insert_list
2N/A *
2N/A * Insert a name with multiple addresses
2N/A */
2N/Aint
2N/Asmb_netbios_cache_insert_list(struct name_entry *name)
2N/A{
2N/A struct name_entry entry;
2N/A addr_entry_t *addr;
2N/A
2N/A addr = &name->addr_list;
2N/A do {
2N/A smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, name->scope,
2N/A addr->sin.sin_addr.s_addr,
2N/A addr->sin.sin_port,
2N/A name->attributes,
2N/A addr->attributes,
2N/A &entry);
2N/A (void) memcpy(entry.name, name->name, NETBIOS_NAME_SZ);
2N/A entry.addr_list.refresh_ttl = entry.addr_list.ttl =
2N/A addr->refresh_ttl;
2N/A (void) smb_netbios_cache_insert(&entry);
2N/A addr = addr->forw;
2N/A } while (addr != &name->addr_list);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_cache_update_entry(struct name_entry *entry,
2N/A struct name_entry *name)
2N/A{
2N/A addr_entry_t *addr;
2N/A addr_entry_t *name_addr;
2N/A
2N/A addr = &entry->addr_list;
2N/A name_addr = &name->addr_list;
2N/A
2N/A if (IS_UNIQUE(entry->attributes)) {
2N/A do {
2N/A addr->ttl = name_addr->ttl;
2N/A addr = addr->forw;
2N/A } while (addr != &entry->addr_list);
2N/A
2N/A } else {
2N/A do {
2N/A if (NETBIOS_SAME_IP(addr, name_addr) &&
2N/A (addr->sin.sin_port == name_addr->sin.sin_port)) {
2N/A addr->ttl = name_addr->ttl;
2N/A return;
2N/A }
2N/A addr = addr->forw;
2N/A } while (addr != &entry->addr_list);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_cache_status
2N/A *
2N/A * Scan the name cache and gather status for
2N/A * Node Status response for names in the given scope
2N/A */
2N/Aunsigned char *
2N/Asmb_netbios_cache_status(unsigned char *buf, int bufsize, unsigned char *scope)
2N/A{
2N/A HT_ITERATOR hti;
2N/A HT_ITEM *item;
2N/A struct name_entry *name;
2N/A unsigned char *numnames;
2N/A unsigned char *scan;
2N/A unsigned char *scan_end;
2N/A
2N/A scan = buf;
2N/A scan_end = scan + bufsize;
2N/A
2N/A numnames = scan++;
2N/A *numnames = 0;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A item = ht_findfirst(smb_netbios_cache, &hti);
2N/A do {
2N/A if (item == 0)
2N/A break;
2N/A
2N/A if (item->hi_data == 0)
2N/A continue;
2N/A
2N/A if ((scan + NETBIOS_NAME_SZ + 2) >= scan_end)
2N/A /* no room for adding next entry */
2N/A break;
2N/A
2N/A name = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&name->mtx);
2N/A
2N/A if (IS_LOCAL(name->attributes) &&
2N/A (strcasecmp((char *)scope, (char *)name->scope) == 0)) {
2N/A bcopy(name->name, scan, NETBIOS_NAME_SZ);
2N/A scan += NETBIOS_NAME_SZ;
2N/A *scan++ = PUBLIC_BITS(name->attributes) >> 8;
2N/A *scan++ = PUBLIC_BITS(name->attributes);
2N/A (*numnames)++;
2N/A }
2N/A
2N/A (void) mutex_unlock(&name->mtx);
2N/A } while ((item = ht_findnext(&hti)) != 0);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A
2N/A return (scan);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_cache_reset_ttl()
2N/A{
2N/A addr_entry_t *addr;
2N/A struct name_entry *name;
2N/A HT_ITERATOR hti;
2N/A HT_ITEM *item;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A item = ht_findfirst(smb_netbios_cache, &hti);
2N/A do {
2N/A if (item == 0)
2N/A break;
2N/A
2N/A if (item->hi_data == 0)
2N/A continue;
2N/A
2N/A name = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&name->mtx);
2N/A
2N/A addr = &name->addr_list;
2N/A do {
2N/A if (addr->ttl < 1) {
2N/A if (addr->refresh_ttl)
2N/A addr->ttl = addr->refresh_ttl;
2N/A else
2N/A addr->refresh_ttl = addr->ttl =
2N/A TO_SECONDS(DEFAULT_TTL);
2N/A }
2N/A addr = addr->forw;
2N/A } while (addr != &name->addr_list);
2N/A
2N/A (void) mutex_unlock(&name->mtx);
2N/A } while ((item = ht_findnext(&hti)) != 0);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A}
2N/A
2N/A/*
2N/A * Returns TRUE when given name is added to the refresh queue
2N/A * FALSE if not.
2N/A */
2N/Astatic boolean_t
2N/Asmb_netbios_cache_insrefq(name_queue_t *refq, HT_ITEM *item)
2N/A{
2N/A struct name_entry *name;
2N/A struct name_entry *refent;
2N/A
2N/A name = (struct name_entry *)item->hi_data;
2N/A
2N/A if (IS_LOCAL(name->attributes)) {
2N/A if (IS_UNIQUE(name->attributes)) {
2N/A refent = smb_netbios_name_dup(name, 1);
2N/A if (refent) {
2N/A QUEUE_INSERT_TAIL(&refq->head, refent)
2N/A }
2N/A
2N/A /* next name */
2N/A return (B_TRUE);
2N/A }
2N/A } else {
2N/A ht_mark_delete(smb_netbios_cache, item);
2N/A refent = smb_netbios_name_dup(name, 0);
2N/A if (refent) {
2N/A QUEUE_INSERT_TAIL(&refq->head, refent)
2N/A }
2N/A
2N/A /* next name */
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_cache_refresh
2N/A *
2N/A * Scans the name cache and add all local unique names
2N/A * and non-local names the passed refresh queue. Non-
2N/A * local names will also be marked as deleted.
2N/A *
2N/A * NOTE that the caller MUST protect the queue using
2N/A * its mutex
2N/A */
2N/Avoid
2N/Asmb_netbios_cache_refresh(name_queue_t *refq)
2N/A{
2N/A struct name_entry *name;
2N/A addr_entry_t *addr;
2N/A HT_ITERATOR hti;
2N/A HT_ITEM *item;
2N/A
2N/A bzero(&refq->head, sizeof (refq->head));
2N/A refq->head.forw = refq->head.back = &refq->head;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A item = ht_findfirst(smb_netbios_cache, &hti);
2N/A do { /* name loop */
2N/A if (item == 0)
2N/A break;
2N/A
2N/A if (item->hi_data == 0)
2N/A continue;
2N/A
2N/A name = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&name->mtx);
2N/A
2N/A addr = &name->addr_list;
2N/A do { /* address loop */
2N/A if (addr->ttl > 0) {
2N/A addr->ttl--;
2N/A if (addr->ttl == 0) {
2N/A if (smb_netbios_cache_insrefq(refq,
2N/A item))
2N/A break;
2N/A }
2N/A }
2N/A addr = addr->forw;
2N/A } while (addr != &name->addr_list);
2N/A
2N/A (void) mutex_unlock(&name->mtx);
2N/A } while ((item = ht_findnext(&hti)) != 0);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_cache_delete_locals
2N/A *
2N/A * Scans the name cache and add all local names to
2N/A * the passed delete queue.
2N/A *
2N/A * NOTE that the caller MUST protect the queue using
2N/A * its mutex
2N/A */
2N/Avoid
2N/Asmb_netbios_cache_delete_locals(name_queue_t *delq)
2N/A{
2N/A struct name_entry *entry;
2N/A struct name_entry *delent;
2N/A HT_ITERATOR hti;
2N/A HT_ITEM *item;
2N/A
2N/A bzero(&delq->head, sizeof (delq->head));
2N/A delq->head.forw = delq->head.back = &delq->head;
2N/A
2N/A (void) rw_wrlock(&nb_cache_lock);
2N/A item = ht_findfirst(smb_netbios_cache, &hti);
2N/A do {
2N/A if (item == 0)
2N/A break;
2N/A
2N/A if (item->hi_data == 0)
2N/A continue;
2N/A
2N/A entry = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&entry->mtx);
2N/A
2N/A if (IS_LOCAL(entry->attributes)) {
2N/A ht_mark_delete(smb_netbios_cache, item);
2N/A delent = smb_netbios_name_dup(entry, 1);
2N/A if (delent) {
2N/A QUEUE_INSERT_TAIL(&delq->head, delent)
2N/A }
2N/A }
2N/A
2N/A (void) mutex_unlock(&entry->mtx);
2N/A } while ((item = ht_findnext(&hti)) != 0);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_name_freeaddrs(struct name_entry *entry)
2N/A{
2N/A addr_entry_t *addr;
2N/A
2N/A if (entry == 0)
2N/A return;
2N/A
2N/A while ((addr = entry->addr_list.forw) != &entry->addr_list) {
2N/A QUEUE_CLIP(addr);
2N/A free(addr);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_cache_count
2N/A *
2N/A * Returns the number of names in the cache
2N/A */
2N/Aint
2N/Asmb_netbios_cache_count()
2N/A{
2N/A int cnt;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A cnt = ht_get_total_items(smb_netbios_cache);
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A
2N/A return (cnt);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_cache_dump(FILE *fp)
2N/A{
2N/A struct name_entry *name;
2N/A HT_ITERATOR hti;
2N/A HT_ITEM *item;
2N/A
2N/A (void) rw_rdlock(&nb_cache_lock);
2N/A
2N/A if (ht_get_total_items(smb_netbios_cache) != 0) {
2N/A (void) fprintf(fp, "\n%-22s %-16s %-16s %s\n",
2N/A "Name", "Type", "Address", "TTL");
2N/A (void) fprintf(fp, "%s%s\n",
2N/A "-------------------------------",
2N/A "------------------------------");
2N/A }
2N/A
2N/A item = ht_findfirst(smb_netbios_cache, &hti);
2N/A while (item) {
2N/A if (item->hi_data) {
2N/A name = (struct name_entry *)item->hi_data;
2N/A (void) mutex_lock(&name->mtx);
2N/A smb_netbios_name_dump(fp, name);
2N/A (void) mutex_unlock(&name->mtx);
2N/A }
2N/A item = ht_findnext(&hti);
2N/A }
2N/A (void) rw_unlock(&nb_cache_lock);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_name_dump(FILE *fp, struct name_entry *entry)
2N/A{
2N/A char buf[MAXHOSTNAMELEN];
2N/A addr_entry_t *addr;
2N/A char *type;
2N/A int count = 0;
2N/A
2N/A smb_strname(entry, buf, sizeof (buf));
2N/A type = (IS_UNIQUE(entry->attributes)) ? "UNIQUE" : "GROUP";
2N/A
2N/A (void) fprintf(fp, "%s %-6s (0x%04x) ", buf, type, entry->attributes);
2N/A
2N/A addr = &entry->addr_list;
2N/A do {
2N/A if (count == 0)
2N/A (void) fprintf(fp, "%-16s %d\n",
2N/A inet_ntoa(addr->sin.sin_addr), addr->ttl);
2N/A else
2N/A (void) fprintf(fp, "%-28s (0x%04x) %-16s %d\n",
2N/A " ", addr->attributes,
2N/A inet_ntoa(addr->sin.sin_addr), addr->ttl);
2N/A ++count;
2N/A addr = addr->forw;
2N/A } while (addr != &entry->addr_list);
2N/A}
2N/A
2N/Avoid
2N/Asmb_netbios_name_logf(struct name_entry *entry)
2N/A{
2N/A char namebuf[MAXHOSTNAMELEN];
2N/A addr_entry_t *addr;
2N/A
2N/A smb_strname(entry, namebuf, sizeof (namebuf));
2N/A syslog(LOG_DEBUG, "%s flags=0x%x\n", namebuf, entry->attributes);
2N/A addr = &entry->addr_list;
2N/A do {
2N/A syslog(LOG_DEBUG, " %s ttl=%d flags=0x%x port=%d",
2N/A inet_ntoa(addr->sin.sin_addr),
2N/A addr->ttl, addr->attributes,
2N/A addr->sin.sin_port);
2N/A addr = addr->forw;
2N/A } while (addr && (addr != &entry->addr_list));
2N/A}
2N/A
2N/A/*
2N/A * smb_netbios_name_dup
2N/A *
2N/A * Duplicate the given name entry. If 'alladdr' is 0 only
2N/A * copy the primary address otherwise duplicate all the
2N/A * addresses. NOTE that the duplicate structure is not
2N/A * like a regular cache entry i.e. it's a contiguous block
2N/A * of memory and each addr structure doesn't have it's own
2N/A * allocated memory. So, the returned structure can be freed
2N/A * by one free call.
2N/A */
2N/Astruct name_entry *
2N/Asmb_netbios_name_dup(struct name_entry *entry, int alladdr)
2N/A{
2N/A addr_entry_t *addr;
2N/A addr_entry_t *dup_addr;
2N/A struct name_entry *dup;
2N/A int addr_cnt = 0;
2N/A int size = 0;
2N/A
2N/A if (alladdr) {
2N/A addr = entry->addr_list.forw;
2N/A while (addr && (addr != &entry->addr_list)) {
2N/A addr_cnt++;
2N/A addr = addr->forw;
2N/A }
2N/A }
2N/A
2N/A size = sizeof (struct name_entry) +
2N/A (addr_cnt * sizeof (addr_entry_t));
2N/A dup = (struct name_entry *)calloc(1, size);
2N/A if (dup == 0)
2N/A return (0);
2N/A
2N/A bzero(dup, size);
2N/A
2N/A dup->forw = dup->back = dup;
2N/A dup->attributes = entry->attributes;
2N/A (void) memcpy(dup->name, entry->name, NETBIOS_NAME_SZ);
2N/A (void) strlcpy((char *)dup->scope, (char *)entry->scope,
2N/A NETBIOS_DOMAIN_NAME_MAX);
2N/A dup->addr_list = entry->addr_list;
2N/A dup->addr_list.forw = dup->addr_list.back = &dup->addr_list;
2N/A
2N/A if (alladdr == 0)
2N/A return (dup);
2N/A
2N/A /* LINTED - E_BAD_PTR_CAST_ALIGN */
2N/A dup_addr = (addr_entry_t *)((unsigned char *)dup +
2N/A sizeof (struct name_entry));
2N/A
2N/A addr = entry->addr_list.forw;
2N/A while (addr && (addr != &entry->addr_list)) {
2N/A *dup_addr = *addr;
2N/A QUEUE_INSERT_TAIL(&dup->addr_list, dup_addr);
2N/A addr = addr->forw;
2N/A dup_addr++;
2N/A }
2N/A
2N/A return (dup);
2N/A}
2N/A
2N/Astatic void
2N/Asmb_strname(struct name_entry *entry, char *buf, int bufsize)
2N/A{
2N/A char tmp[MAXHOSTNAMELEN];
2N/A char *p;
2N/A
2N/A (void) snprintf(tmp, MAXHOSTNAMELEN, "%15.15s", entry->name);
2N/A if ((p = strchr(tmp, ' ')) != NULL)
2N/A *p = '\0';
2N/A
2N/A if (entry->scope[0] != '\0') {
2N/A (void) strlcat(tmp, ".", MAXHOSTNAMELEN);
2N/A (void) strlcat(tmp, (char *)entry->scope, MAXHOSTNAMELEN);
2N/A }
2N/A
2N/A (void) snprintf(buf, bufsize, "%-16s <%02X>", tmp, entry->name[15]);
2N/A}
2N/A
2N/Astatic void
2N/Ahash_callback(HT_ITEM *item)
2N/A{
2N/A struct name_entry *entry;
2N/A
2N/A if (item && item->hi_data) {
2N/A entry = (struct name_entry *)item->hi_data;
2N/A smb_netbios_name_freeaddrs(entry);
2N/A free(entry);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Asmb_netbios_match(const char *key1, const char *key2, size_t n)
2N/A{
2N/A int res;
2N/A
2N/A res = bcmp(key1, key2, NETBIOS_NAME_SZ);
2N/A if (res == 0) {
2N/A /* Names are the same, compare scopes */
2N/A res = strcmp(key1 + NETBIOS_NAME_SZ, key2 + NETBIOS_NAME_SZ);
2N/A }
2N/A
2N/A return (res);
2N/A}
2N/A
2N/Astatic void
2N/Asmb_netbios_cache_key(char *key, unsigned char *name, unsigned char *scope)
2N/A{
2N/A bzero(key, NETBIOS_HKEY_SZ);
2N/A (void) memcpy(key, name, NETBIOS_NAME_SZ);
2N/A (void) memcpy(key + NETBIOS_NAME_SZ, scope,
2N/A strlen((const char *)scope));
2N/A}