/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* NetBIOS name resolution node types.
*
* A B-node (broadcast node) uses broadcasts for name registration
* and resolution. Routers typically do not forward broadcasts and
* only computers on the local subnet will respond.
*
* A P-node (peer-to-peer node) uses a NetBIOS name server (WINS)
* to resolve NetBIOS names, which allows it to work across routers.
* In order to function in a P-node environment, all computers must
* be configured to use the NetBIOS name server because P-nodes do
* not broadcast on the network.
*
* A mixed node (M-node) behaves as a B-node by default. If it cannot
* resolve the name via broadcast then it tries a NetBIOS name server
* lookup (P-node).
*
* A hybrid node (H-node) behaves as a P-node by default. If it cannot
* resolve the name using a NetBIOS name server then it resorts to
* broadcasts (B-node).
*
* NetBIOS Name Service Protocols
*
* A REQUEST packet is always sent to the well known UDP port 137.
* The destination address is normally either the IP broadcast address or
* the address of the NAME - the address of the NAME server it set up at
* initialization time. In rare cases, a request packet will be sent to
* an end node, e.g. a NAME QUERY REQUEST sent to "challenge" a node.
*
* A RESPONSE packet is always sent to the source UDP port and source IP
* address of the request packet.
*
* A DEMAND packet must always be sent to the well known UDP port 137.
* There is no restriction on the target IP address.
*
* A transaction ID is a value composed from the requestor's IP address and
* a unique 16 bit value generated by the originator of the transaction.
*/
#include <unistd.h>
#include <syslog.h>
#include <stdlib.h>
#include <synch.h>
#include <errno.h>
#include <netdb.h>
#include <smbsrv/libsmbns.h>
#include <smbns_netbios.h>
/*
* RFC 1002 4.2.1.1. HEADER
*/
/*
* RFC 1002 4.2.1.3. RESOURCE RECORD
*/
/*
*
* RESOURCE RECORD RR_CLASS field definitions
*/
/*
* NB_FLAGS field of the RESOURCE RECORD RDATA field for RR_TYPE of NB.
*/
typedef struct nbt_name_reply {
static int name_sock = 0;
static int bcast_num = 0;
static int nbns_num = 0;
struct name_packet *, uint32_t);
struct name_packet *packet);
/*
* Allocate a transaction id.
*/
static uint16_t
smb_netbios_name_trn_id(void)
{
(void) mutex_lock(&trn_id_mtx);
do {
++trn_id;
(void) mutex_unlock(&trn_id_mtx);
return (trn_id);
}
static int
{
int rc;
/*
* The response packet has in it the address of the presumed owner
* of the name. Challenge that owner. If owner either does not
* respond or indicates that he no longer owns the name, claim the
* name. Otherwise, the name cannot be claimed.
*/
return (-1);
&packet, UCAST_REQ_RETRY_TIMEOUT)) != 0)
return (rc);
}
}
/* No reply */
return (0);
}
static nbt_name_reply_t *
{
(void) mutex_lock(&rq_mtx);
(void) mutex_unlock(&rq_mtx);
for (;;) {
(void) gettimeofday(&wt, 0);
(void) mutex_lock(&rq_mtx);
(void) mutex_unlock(&rq_mtx);
if (reply->reply_ready) {
(void) mutex_lock(&rq_mtx);
(void) mutex_unlock(&rq_mtx);
continue;
}
return (reply);
}
(void) gettimeofday(&wt, 0);
(void) mutex_lock(&rq_mtx);
(void) mutex_unlock(&rq_mtx);
break;
}
}
}
return (0);
}
static void
{
(void) mutex_lock(&rq_mtx);
(void) cond_signal(&rq_cv);
(void) mutex_unlock(&rq_mtx);
return;
}
}
(void) mutex_unlock(&rq_mtx);
/* Presumably nobody is waiting any more... */
if (answer)
}
static int
{
int rc = 0;
return (0); /* No reply: retry */
}
/* response */
switch (PACKET_TYPE(info)) {
case NAME_QUERY_RESPONSE:
if (POSITIVE_RESPONSE(info)) {
do {
/*
* Make sure that remote name is not
* flagged local
*/
else
rc = 1;
} else {
rc = -1;
}
break;
if (NEGATIVE_RESPONSE(info)) {
if (answer == 0) {
break;
}
if (entry) {
/*
* a name in the state "conflict
* detected" does not "logically" exist
* on that node. No further session
* will be accepted on that name.
* No datagrams can be sent against
* that name.
* Such an entry will not be used for
* purposes of processing incoming
* request packets.
* The only valid user NetBIOS operation
* against such a name is DELETE NAME.
*/
"nbns: name conflict: %15.15s",
}
}
break;
}
/*
* name can be added:
* adjust refresh timeout value,
* TTL, for this name
*/
do {
if ((addr->refresh_ttl == 0) ||
}
rc = 1;
break;
case NAME_RELEASE_RESPONSE:
rc = 1;
break;
/*
* The response packet has in it the
* address of the presumed owner of the
* name. Challenge that owner. If
* owner either does not respond or
* indicates that he no longer owns the
* name, claim the name. Otherwise,
* the name cannot be claimed.
*/
break;
default:
rc = 0;
break;
}
if (answer)
return (rc); /* retry */
}
/*
* smb_name_buf_from_packet
*
* Description:
* Convert a NetBIOS Name Server Packet Block (npb)
* into the bits and bytes destined for the wire.
* The "buf" is used as a heap.
*
* Inputs:
* char * buf -> Buffer, from the wire
* unsigned n_buf -> Length of 'buf'
* name_packet *npb -> Packet block, decode into
* unsigned n_npb -> Max bytes in 'npb'
*
* Returns:
* >0 -> Encode successful, value is length of packet in "buf"
* -1 -> Hard error, can not possibly encode
* -2 -> Need more memory in buf -- it's too small
*/
static int
struct name_packet *npb)
{
unsigned int tmp;
int i, step;
if (n_buf < NAME_HEADER_SIZE)
return (-1); /* no header, impossible */
dnptrs[1] = 0;
heap += 2;
heap += 2;
heap += 2;
heap += 2;
heap += 2;
heap += 2;
return (-2);
comp_name_buf, sizeof (comp_name_buf));
heap += 2;
heap += 2;
}
int n;
/* truly ugly, but saves code copying */
if (step == 1) {
} else if (step == 2) {
} else { /* step == 3 */
}
for (i = 0; i < n; i++) {
return (-2);
comp_name_buf, sizeof (comp_name_buf));
heap += 2;
heap += 2;
heap += 4;
heap += 2;
return (-2);
heap += 2;
sizeof (uint32_t));
heap += 4;
} else {
}
}
}
}
}
/*
* strnchr
*
* Lookup for character 'c' in first 'n' chars of string 's'.
* Returns pointer to the found char, otherwise returns 0.
*/
static char *
strnchr(const char *s, char c, int n)
{
char *ps = (char *)s;
char *es = (char *)s + n;
if (*ps == c)
return (ps);
++ps;
}
return (ps);
return (0);
}
static boolean_t
{
}
/*
* smb_netbios_getname
*
* Get the Netbios name part of the given record.
* Does some boundary checks.
*
* Returns the name length on success, otherwise
* returns 0.
*/
static int
{
char *name_end;
int name_len;
/* no room for a NB name */
return (0);
}
if (name_end == 0) {
/* not a valid NB name */
return (0);
}
return (name_len);
}
/*
* smb_name_buf_to_packet
*
* Convert the bits and bytes that came from the wire into a NetBIOS
* Name Server Packet Block (npb). The "block" is used as a heap.
*
* Returns a pointer to a name packet on success. Otherwise, returns
* a NULL pointer.
*/
static struct name_packet *
{
unsigned char *heap;
int name_len;
if (n_buf < NAME_HEADER_SIZE) {
/* truncated header */
return (NULL);
}
ns = sizeof (struct name_entry);
n = n_buf + sizeof (struct name_packet) +
return (NULL);
/* scan is in position for question entries */
/*
* Measure the space needed for the tables
*/
if (qdcount > 0) {
/* LINTED - E_BAD_PTR_CAST_ALIGN */
for (i = 0; i < qdcount; i++) {
/* LINTED - E_BAD_PTR_CAST_ALIGN */
heap += sizeof (struct name_entry);
}
}
/* LINTED - E_BAD_PTR_CAST_ALIGN */
if (ancount > 0) {
/* LINTED - E_BAD_PTR_CAST_ALIGN */
}
if (nscount > 0) {
/* LINTED - E_BAD_PTR_CAST_ALIGN */
}
if (arcount > 0) {
/* LINTED - E_BAD_PTR_CAST_ALIGN */
}
/*
* Populate each resource_record's .name field.
* Done as a second pass so that all resource records
* (answer, authority, additional) are consecutive via nrr[i].
*/
/* LINTED - E_BAD_PTR_CAST_ALIGN */
heap += sizeof (struct name_entry);
}
(char *)scan_end);
if (name_len <= 0) {
return (NULL);
}
smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0,
if (rc < 0) {
/* Couldn't decode the question name */
return (NULL);
}
/* no room for Question Type(2) and Class(2) fields */
return (NULL);
}
}
/*
* Cheat. Remaining sections are of the same resource_record
* format. Table space is consecutive.
*/
if (scan[0] == 0xc0) {
/* Namebuf is reused... */
rc = 2;
} else {
(char *)scan_end);
if (name_len <= 0) {
return (NULL);
}
}
/*
* no room for RR_TYPE (2), RR_CLASS (2), TTL (4) and
* RDLENGTH (2) fields.
*/
return (NULL);
}
smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0,
return (NULL);
}
/* no room for RDATA */
return (NULL);
}
nn = n;
while (nn) {
if (nn == 6)
else {
sizeof (addr_entry_t));
if (next == 0) {
/* not enough memory */
return (NULL);
}
next);
}
(void) memcpy(
nn -= 6;
scan += 6;
}
} else {
scan += n;
}
heap += n;
}
}
return (npb);
}
/*
* smb_send_name_service_packet
*
* Description:
*
* Send out a name service packet to proper destination.
*
* Inputs:
* struct netbios_name *dest -> NETBIOS name of destination
* struct name_packet *packet -> Packet to send
*
* Returns:
* success -> >0
* failure -> <=0
*/
static int
{
int len;
return (-1);
}
}
/*
* smb_netbios_send_rcv
*
* This function sends the given NetBIOS packet to the given
* address and get back the response. If send operation is not
* successful, it's repeated 'retries' times.
*
* Returns:
* 0 Unsuccessful send operation; no reply
* 1 Got reply
*/
static int
{
int rc;
return (0);
return (1);
if (rc != 0)
return (0);
}
}
return (0);
}
/*
* RFC 1002 4.2.2. NAME REGISTRATION REQUEST
*/
static int
struct resource_record *additional)
{
int gotreply = 0;
unsigned char type;
type);
return (-1);
}
if (bcast_num == 0)
return (0);
} else {
if (nbns_num == 0)
return (0);
}
for (i = 0; i < addr_num; i++) {
/*
* Only register with the Primary WINS server,
* unless we got no reply.
*/
break;
if (rc == 1)
gotreply = 1;
}
return (gotreply);
}
/*
* RFC 1002 4.2.4. NAME REFRESH REQUEST
*/
/*ARGSUSED*/
static int
{
int rc = 0;
int gotreply = 0;
unsigned char type;
return (-1);
}
switch (bcast) {
case BROADCAST :
if (bcast_num == 0)
return (-1);
break;
case UNICAST :
if (nbns_num == 0)
return (-1);
break;
default:
/*
* the value of addr_num is irrelvant here, because
* the code is going to do special_process so it doesn't
* need the addr_num. We set a value here just to avoid
* compiler warning.
*/
addr_num = 0;
q_addrs = 1;
break;
}
if (q_addrs)
goto special_process;
for (i = 0; i < addr_num; i++) {
if (rc == 1)
gotreply = 1;
}
return (gotreply);
addr = destination;
do {
if (rc == 1)
gotreply = 1;
} while (addr != destination);
return (gotreply);
}
/*
* RFC 1002 4.2.5. POSITIVE NAME REGISTRATION RESPONSE
* RFC 1002 4.2.6. NEGATIVE NAME REGISTRATION RESPONSE
*/
static int
{
(rcode & NAME_RCODE_MASK);
}
/*
* RFC 1002 4.2.9. NAME RELEASE REQUEST & DEMAND
*/
static int
{
int gotreply = 0;
int i, rc;
int addr_num;
if (bcast_num == 0)
return (-1);
} else {
if (nbns_num == 0)
return (-1);
}
for (i = 0; i < addr_num; i++) {
if (rc == 1)
gotreply = 1;
}
return (gotreply);
}
/*
* RFC 1002 4.2.10. POSITIVE NAME RELEASE RESPONSE
* RFC 1002 4.2.11. NEGATIVE NAME RELEASE RESPONSE
*/
static int
/* LINTED - E_STATIC_UNUSED */
{
}
/*
* RFC 1002 4.2.12. NAME QUERY REQUEST
*/
static int
{
int rc = 0;
int i, addr_num;
if (bcast_num == 0)
return (-1);
} else {
if (nbns_num == 0)
return (-1);
}
for (i = 0; i < addr_num; i++) {
break;
if (smb_send_name_service_packet(&destination[i],
&packet) >= 0) {
&destination[i],
break;
}
}
}
return (rc);
}
/*
* RFC 1002 4.2.13. POSITIVE NAME QUERY RESPONSE
* RFC 1002 4.2.14. NEGATIVE NAME QUERY RESPONSE
*/
static int
{
if (rcode) {
} else {
do {
}
}
/*
* RFC 1002 4.2.18. NODE STATUS RESPONSE
*/
static int
struct name_packet *original_packet)
{
unsigned char *scan;
unsigned char *scan_end;
if (smb_nic_is_same_subnet(&ipaddr))
else
net_ipaddr = 0;
while (!scan_done) {
break;
}
if (net_ipaddr != 0) {
int s;
/* LINTED - E_BAD_PTR_CAST_ALIGN */
} else {
}
(void) close(s);
} else {
}
scan += 6;
break;
}
scan += 26;
break;
}
break;
}
break;
}
break;
}
break;
}
break;
}
break;
}
break;
}
break;
}
}
}
static int
{
int rc = 0;
do {
/* build name service packet */
/*
* question.name->attributes |= NAME_NB_FLAGS_ONT_B;
* This is commented because NAME_NB_FLAGS_ONT_B is 0
*/
additional.ttl = 0;
sizeof (uint32_t));
&additional);
return (rc);
}
static int
{
}
static int
{
/* build packet */
additional.ttl = 0;
additional.rdlength = 0;
do {
&question, &additional));
}
static int
{
int rc = 0;
/* build packet */
do {
additional.ttl = 0;
sizeof (uint32_t));
&additional);
return (rc);
}
static int
{
int rc = 0;
/* build packet */
do {
additional.ttl = 0;
sizeof (uint32_t));
&additional, 1);
return (rc);
}
static int
{
/*
* Host initiated processing for a P node
*/
}
static int
{
/* build packet */
additional.ttl = 0;
additional.rdlength = 0;
do {
&question, &additional);
return (1);
}
static int
{
if (smb_name_Bnode_add_name(name) > 0) {
if (nbns_num == 0)
return (1); /* No name server configured */
return (smb_name_Pnode_add_name(name));
}
return (-1);
}
static int
{
if (nbns_num > 0) {
return (1);
}
return (smb_name_Bnode_add_name(name));
}
static int
{
return (1);
if (nbns_num == 0)
return (1); /* No name server configured */
return (smb_name_Pnode_find_name(name));
}
static int
{
if (nbns_num > 0)
return (1);
return (smb_name_Bnode_find_name(name));
}
static int
{
(void) smb_name_Bnode_delete_name(name);
if (nbns_num == 0)
return (-1); /* No name server configured */
if (smb_name_Pnode_delete_name(name) > 0)
return (1);
return (-1);
}
static int
{
if (nbns_num > 0)
if (smb_name_Pnode_delete_name(name) > 0)
return (1);
return (smb_name_Bnode_delete_name(name));
}
static void
{
case NAME_OPCODE_REFRESH:
/* Guard against malformed packets */
if ((question == 0) || (additional == 0))
break;
break;
}
else
break;
case NAME_OPCODE_QUERY:
/*
* This opcode covers both NAME_QUERY_REQUEST and
* NODE_STATUS_REQUEST. They can be distinguished
* based on the type of question entry.
*/
/* All query requests have to have question entry */
if (question == 0)
break;
(void) smb_send_name_query_response(addr,
}
}
else
/*
* Name of "*" may be used to force node to
* divulge status for administrative purposes
*/
entry = 0;
if (entry)
/*
* send only those names that are
* in the same scope as the scope
* field in the request packet
*/
(void) smb_send_node_status_response(addr,
packet);
}
}
break;
default:
break;
}
}
static void
{
/*
* always ignore UDP broadcast packets
*/
return;
}
case NAME_OPCODE_REFRESH:
/* Guard against malformed packets */
if ((question == 0) || (additional == 0))
break;
break;
}
else
(void) smb_netbios_cache_insert(name);
break;
case NAME_OPCODE_QUERY:
/*
* This opcode covers both NAME_QUERY_REQUEST and
* NODE_STATUS_REQUEST. They can be distinguished
* based on the type of question entry.
*/
/* All query requests have to have question entry */
if (question == 0)
break;
/*
* send response to the IP address and port
* number from which the request was received.
*/
(void) smb_send_name_query_response(addr,
} else {
/*
* send response to the requestor
*/
(void) smb_send_name_query_response(addr,
}
}
else
/*
* Name of "*" may be used to force node to
* divulge status for administrative purposes
*/
entry = 0;
/*
* send only those names that are
* in the same scope as the scope
* field in the request packet
*/
if (entry)
(void) smb_send_node_status_response(addr,
packet);
}
}
break;
default:
break;
}
}
static void
{
else
}
static void
{
else
}
/*
* smb_netbios_name_tick
*
* Called once a second to handle name server timeouts.
*/
void
smb_netbios_name_tick(void)
{
(void) smb_name_Pnode_refresh_name(name);
}
} else {
}
}
}
/*
* smb_name_find_name
*
* Lookup name cache for the given name.
* If it's not in the cache it'll send a
* name query request and then lookup the
* cache again. Note that if a name is
* returned it's locked and called MUST
* unlock it by calling smb_name_unlock_name()
*/
struct name_entry *
{
switch (smb_node_type) {
case 'B':
(void) smb_name_Bnode_find_name(name);
break;
case 'P':
(void) smb_name_Pnode_find_name(name);
break;
case 'M':
(void) smb_name_Mnode_find_name(name);
break;
case 'H':
default:
(void) smb_name_Hnode_find_name(name);
break;
}
return (smb_netbios_cache_lookup(name));
}
return (result);
}
void
{
}
int
{
switch (smb_node_type) {
case 'B':
break;
case 'P':
break;
case 'M':
break;
case 'H':
default:
break;
}
if (rc >= 0)
(void) smb_netbios_cache_insert(name);
return (rc);
}
int
{
int rc;
unsigned char type;
return (-1);
}
switch (smb_node_type) {
case 'B':
break;
case 'P':
break;
case 'M':
break;
case 'H':
default:
break;
}
if (rc > 0)
return (0);
return (-1);
}
typedef struct {
char *buf;
int length;
/*
* smb_netbios_worker
*
* name service (on port 138).
*/
void *
{
/* Reply packet */
free(p);
return (NULL);
}
/* Request packet */
switch (smb_node_type) {
case 'B':
break;
case 'P':
break;
case 'M':
break;
case 'H':
default:
break;
}
} else {
}
free(p);
return (NULL);
}
/*
* Configure the node type. If a WINS server has been specified,
* act like an H-node. Otherwise, behave like a B-node.
*/
static void
smb_netbios_node_config(void)
{
};
int i;
nbns_num = 0;
for (i = 0; i < SMB_PI_MAX_WINS; ++i) {
ipstr[0] = '\0';
continue;
nbns_num++;
}
}
static void
{
int rc;
while (rc == 0) {
(void) smb_netbios_name_logf(name);
switch (smb_node_type) {
case SMB_NODETYPE_B:
(void) smb_name_Bnode_add_name(name);
break;
case SMB_NODETYPE_P:
(void) smb_name_Pnode_add_name(name);
break;
case SMB_NODETYPE_M:
(void) smb_name_Mnode_add_name(name);
break;
case SMB_NODETYPE_H:
default:
(void) smb_name_Hnode_add_name(name);
break;
}
}
}
}
/*
* Note that the node configuration must be setup before calling
* smb_init_name_struct().
*/
void
smb_netbios_name_config(void)
{
int rc;
(void) mutex_lock(&nbt_name_config_mtx);
bcast_num = 0;
while (rc == SMB_NIC_SUCCESS) {
continue;
}
bcast_num++;
(void) smb_netbios_cache_insert(&name);
(void) smb_netbios_cache_insert(&name);
}
(void) mutex_unlock(&nbt_name_config_mtx);
}
void
{
(void) mutex_lock(&nbt_name_config_mtx);
(void) smb_name_delete_name(name);
}
(void) mutex_unlock(&nbt_name_config_mtx);
}
void
{
}
/*
* NetBIOS Name Service (port 137)
*/
/*ARGSUSED*/
void *
{
int len;
char *buf;
/*
* Initialize reply_queue
*/
return (NULL);
}
flag = 1;
sizeof (flag));
flag = 1;
sizeof (flag));
return (NULL);
}
while (smb_netbios_running()) {
/* Sleep for 10 seconds and try again */
smb_netbios_sleep(10);
continue;
}
/* Sleep for 10 seconds and try again */
smb_netbios_sleep(10);
continue;
}
goto shutdown;
}
/* Ignore any incoming packets from myself... */
if (smb_nic_is_local(&ipaddr))
goto ignore;
/*
* Launch a netbios worker to process the received packet.
*/
if (worker_param) {
(void) pthread_attr_init(&tattr);
(void) pthread_attr_setdetachstate(&tattr,
(void) pthread_attr_destroy(&tattr);
}
}
if (!smb_netbios_error())
return (NULL);
}