/*
 * af_netb.c - Contains functions that supply socket system calls for NetBEUI
 *             protocol stack which their names has a 'netbeui_' prefix, and
 *             also some utility functions that their names has a 'nbso_'
 *             prefix.
 *
 * Notes:
 *	- VRP in comments is the acronym of "Value Result Parameter"
 *
 * Copyright (c) 1997 by Procom Technology,Inc.
 *
 * This program can be redistributed or modified under the terms of the 
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 *
 */
 

#define __NO_VERSION__
#include <linux/module.h>
#include <linux/net.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/uio.h>
#include <linux/skbuff.h>
#include <linux/netbeui.h>


static inline struct netbeui_sock *
nbso_alloc_sock (int priority);

static inline void
nbso_free_sock (struct netbeui_sock *sk);

static int
nbso_setsockopt (struct socket *sock, int optname, void *optval, int optlen);

static int
nbso_getsockopt (struct socket *sock, int optname, void *optval, int *optlen);


extern struct proto_ops   nbso_name_proto;
extern struct proto_ops   nbso_dgram_proto;
extern struct proto_ops   nbso_session_proto;



/*
 * Socket System Calls 
 */

/*
 * Function: netbeui_create
 *	Creates a NetBEUI socket.
 *
 * Parameters:
 *	sock     : pointer to 'struct socket' that created by system before
 *	           call this function.
 *	protocol : an integer that have protocol specific meaning and we
 *	           do not use it.
 *
 * Returns: int
 *	0        : if socket has been created successfully.
 *	negative : if a fault occurs.
 *                  (-ESOCKTNOSUPPORT) : Specified socket type NOT supported.
 *                  (-ENOMEM)          : Out of memory condition.
 */

static int
netbeui_create (struct socket *sock, int protocol)
{
	struct proto_ops   *prot;
	struct netbeui_sock   *sk;

	switch (sock->type) {
		case SOCK_STREAM:
			prot = &nbso_session_proto;
			break;

		case SOCK_DGRAM:
			prot = &nbso_dgram_proto;
			break;

		case SOCK_RAW:
			prot = &nbso_name_proto;
			break;

		case SOCK_SEQPACKET:
		default:
			return (-ESOCKTNOSUPPORT);
	}

	sk = nbso_alloc_sock(GFP_KERNEL);
	if (!sk)
		return (-ENOMEM);

	sock->data = sk;
	
	sk->protocol = protocol;
	sk->state = NBSO_INIT;
	sk->socket = sock;
	sk->sto.tv_sec = NBSO_DFLT_STO_SEC;
	sk->sto.tv_usec = NBSO_DFLT_STO_MICSEC;
	sk->rto.tv_sec = NBSO_DFLT_RTO_SEC;
	sk->rto.tv_usec = NBSO_DFLT_RTO_MICSEC;
	sk->owner = current->pid;
	do_gettimeofday(&sk->stamp);
	sk->prot = prot;
	sk->waitq = sock->wait;
	*sk->waitq = NULL;
	
#ifdef MODULE
	MOD_INC_USE_COUNT;	/* Increment number of NetBEUI module users */
#endif MODULE

	return 0;
}


/*
 * Function: netbeui_dup
 *	Copies a NetBEUI socket.
 *
 * Parameters:
 *	newsock : pointer to 'struct socket' that created by system before
 *	          call this function & is destination of copy operation.
 *	oldsock : pointer to original socket.
 *
 * Returns: int
 *	0        : if 'oldsock' has been copied to 'newsock' successfully.
 *	negative : if a fault occurs.
 */

static int 
netbeui_dup (struct socket *newsock, struct socket *oldsock)
{
	return (netbeui_create(newsock,
		((struct netbeui_sock *)(oldsock->data))->protocol));
}


/*
 * Function: netbeui_release
 *	Releases a NetBEUI socket.
 *
 * Parameters:
 *	sock : pointer to socket that must be released.
 *	peer : pointer to peer socket. This parameter defined only for
 *	       UNIX domain sockets (AF_UNIX) and we do not use it.
 *
 * Returns: int
 *	0 : in all cases. (this function always succeed)
 */

static int 
netbeui_release (struct socket *sock, struct socket *peer)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

//	sock->state = SS_DISCONNECTING;	/* Was performed by system before call us */

	sk->prot->release(sock, peer);

	sock->data = NULL;
	sock->state = SS_UNCONNECTED;
	nbso_free_sock(sk);

#ifdef MODULE
	MOD_DEC_USE_COUNT;	/* Decrement number of NetBEUI module users */
#endif MODULE

	return 0;
}


/*
 * Function: netbeui_bind
 *	Binds a NetBIOS name to a NetBEUI socket.
 *
 * Parameters:
 *	sock     : pointer to socket that must bind a name to it.
 *	uaddr    : pointer to 'struct sockaddr_netbeui' that contains
 *	           information about sightly name.
 *	addr_len : length of 'struct sockaddr_netbeui'.
 *
 * Returns: int
 *	0        : if name is binded to socket successfully.
 *	negative : if a fault occurs.
 *                  (-EAFNOSUPPORT) : if Address Family of the given name
 *                                    is not AF_NETBEUI.
 */

static int 
netbeui_bind (struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	if (uaddr->sa_family != AF_NETBEUI)
		return (-EAFNOSUPPORT);

	return sk->prot->bind(sock, uaddr, addr_len);
}


/*
 * Function: netbeui_connect
 *	Connects (SOCK_STREAM) or Attaches (SOCK_DGRAM) a socket to a
 *	specified peer.
 *
 * Parameters:
 *	sock     : pointer to socket that must connect to peer.
 *	uaddr    : pointer to 'struct sockaddr_netbeui' that contains
 *	           information about peer.
 *	addr_len : length of 'struct sockaddr_netbeui'.
 *	sflags   : bitwise integer that contains socket flags.
 *
 * Returns: int
 *	0        : if socket connects/attaches to the specified peer
 *	           successfully.
 *	negative : if a fault occurs.
 *	           (-EINPROGRESS) : means that connection/attachment operation
 *	                            is in progress now.
 */

static int 
netbeui_connect (struct socket *sock, struct sockaddr *uaddr, int addr_len,
								 int sflags)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	if (sock->state == SS_CONNECTING)
		return (-EINPROGRESS);

	return sk->prot->connect(sock, uaddr, addr_len, sflags);
}


/*
 * Function: netbeui_socketpair
 *	This function was defined only for AF_UNIX sockets.
 *
 * Parameters:
 *
 * Returns: int
 *	   (-EOPNOTSUPP) : This operation NOT supported by us.
 */

static int 
netbeui_socketpair (struct socket *sock1, struct socket *sock2)
{
	return (-EOPNOTSUPP);
}


/*
 * Function: netbeui_accept
 *	Accepts received requests for connection with this socket from anothers.
 *	Note: this function was defined only for SOCK_STREAM sockets.
 *
 * Parameters:
 *	sock    : pointer to socket that wants to accept the incomming
 *	          connection requests.
 *	newsock : (Semi VRP!) pointer to a new socket with attributes like
 *	          original that connection between it & peer will be established.
 *	          this 'struct socket' created by system before call us, and
 *	          we only must complete its fields.
 *	sflags  : bitwise integer that contains socket flags.
 *
 * Returns: int
 *	0        : if connection is successfully established between newsock
 *	           and peer.
 *	negative : if a fault occurs.
 */

static int 
netbeui_accept (struct socket *sock, struct socket *newsock, int sflags)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	return sk->prot->accept(sock, newsock, sflags);
}


/*
 * Function: netbeui_getname
 *	Gets socket name or peer name that connected/attached to it.
 *
 * Parameters:
 *	sock      : pointer to socket that we need to name of it or its peer.
 *	uaddr     : (VRP) pointer to 'struct sockaddr_netbeui' that be filled
 *	            with requested information.
 *	uaddr_len : (VRP) pointer to an integer that returns length of
 *	            'struct sockaddr_netbeui'.
 *	peer      : an integer that indicates type of request.
 *
 * Returns: int
 *	0        : if requested name is retrieved successfully.
 *	negative : if a fault occurs.
 */

static int 
netbeui_getname (struct socket *sock, struct sockaddr *uaddr, int *uaddr_len,
								 int peer)
{
	int   rc;
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;
	struct sockaddr_netbeui   *addr = (struct sockaddr_netbeui *) uaddr;

	rc = sk->prot->getname(sock, uaddr, uaddr_len, peer);
	if (rc)
		return rc;

	addr->snb_family = AF_NETBEUI;
	*uaddr_len = sizeof(struct sockaddr_netbeui);
	addr->snb_addr.reserved = 0;

	return 0;
}


/*
 * Function: netbeui_select
 *	Determines socket operational (particularly I/O) condition.
 *
 * Parameters:
 *	sock     : pointer to socket that check it.
 *	sel_type : an integer that determines type of checking.
 *	wait     : pointer to a particular structure that contains some
 *	           wait queues. The system itself checks members of these 
 *	           wait queues for their time outs. we only sleep on this
 *	           structure if there is not exist a categoric answer, so far.
 *
 * Returns: int
 *	0 : means that however must wait.
 *	1 : means that answer is positive or an error occured.
 */

static int 
netbeui_select (struct socket *sock , int sel_type, select_table *wait)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	return sk->prot->select(sock, sel_type, wait);
}


/*
 * Function: netbeui_ioctl
 *	Performs some particular operations on socket, that can not do with
 *	regular system calls.
 *
 * Parameters:
 *	sock : pointer to socket that action must perform on it.
 *	cmd  : an integer that indicates type of operation.
 *	arg  : this parameter often is a pointer to 'cmd' relative data structure
 *	       that be used by it as an argument.
 *
 * Returns: int
 *	0        : if cmd is performed successfully.
 *	negative : if a fault occurs. error codes that bubble to user are
 *	           dependent to cmd.
 */

static int 
netbeui_ioctl (struct socket *sock, unsigned int cmd, unsigned long arg)
{
	int   rc;
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	/* Pass ioctl command to appropriate sub-protocol */
	if (sk->prot->ioctl(sock, cmd, arg) == 0)
		return 0;

	switch(cmd) {
		case FIOSETOWN:
		case SIOCSPGRP: {
			int   pid;

			rc = verify_area(VERIFY_READ, (int *) arg, sizeof(long));
			if (rc)
				return rc;

			pid = get_user((int *) arg);

			if ((current->pid != pid) &&
			    (current->pgrp != -pid) &&
			    !suser())
				return (-EACCES);

			sk->owner = pid;
			return 0;
		}

		case FIOGETOWN:
		case SIOCGPGRP:
			rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
			if (rc)
				return rc;

			put_fs_long(sk->owner, (int *) arg);
			return 0;			

		case SIOCGSTAMP:
			if (sk->stamp.tv_sec == 0)
				return (-ENOENT);

			rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct timeval));
			if (rc)
				return rc;

			memcpy_tofs((void *) arg, &sk->stamp, sizeof(struct timeval));
			return 0;

		case SIOCGSTATE:
			rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
			if (rc)
				return rc;

			put_fs_long(sk->state, (int *) arg);
			return 0;			

		default:
			/* Pass ioctl command to next layer */
			return (nbcs_ioctl(cmd, (void *) arg));
	}
}


/*
 * Function: netbeui_listen
 *	Listens for incomming connection requests, and places them in a queue
 *	that MAXimum of its length is 'backlog'.
 *
 * Parameters:
 *	sock    : pointer to socket that must listens for incomming requests.
 *	backlog : an integer that indicates length of queue which holds
 *	          incomming requests that not accepted yet.
 *
 * Returns: int
 *	0        : if operation is performed successfully.
 *	negative : if a fault occurs.
 */

static int 
netbeui_listen (struct socket *sock, int backlog)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	return sk->prot->listen(sock, backlog);
}


/*
 * Function: netbeui_shutdown
 *	Shuts down part of a full-duplex connection.
 *
 * Parameters:
 *	sock : pointer to socket that part of its connection must be closed.
 *	how  : an integer that indicates part of full_duplex connection that
 *	       must be closed.
 *
 * Returns: int
 *	0        : if operation is performed successfully.
 *	negative : if a fault occurs.
 */

static int 
netbeui_shutdown (struct socket *sock, int how)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	return sk->prot->shutdown(sock, how);
}


/*
 * Function: netbeui_setsockopt
 *	Sets some operational options of protocol stack (from socket layer
 *	down to LAN adapter).
 *
 * Parameters:
 *	sock    : a pointer to socket that tuning perform on it.
 *	level   : an integer that indicates level in protocol stack that
 *	          operation performs on.
 *	optname : an integer that indicates option that must tune.
 *	optval  : a pointer to related data structure which used for assign
 *	          value(s) to option.
 *	optlen  : length of data structure that 'optval' points to it.
 *
 * Returns: int
 *	0        : if tuning is performed successfully.
 *	negative : if a fault occurs.
 *	           (-EINVAL) : Some of parameters are invalid.
 */

static int 
netbeui_setsockopt (struct socket *sock, int level, int optname, char *optval,
								int optlen)
{
	int   rc;

  	if (!optval || !optlen) 
  		return (-EINVAL);

  	rc = verify_area(VERIFY_READ, optval, optlen);
  	if (rc)
  		return rc;
  	
	switch (level) {
		case SOL_SOCKET:
			return (nbso_setsockopt(sock, optname, optval, optlen));

		case SOL_NETBEUI:
			return (nbcs_setsockopt(sock, optname, optval, optlen));

		case SOL_LLC:
/* NOT IMPLEMENTED */
//			return (ll___setsockopt(sock, optname, optval, optlen));

		case SOL_LANA:
/* NOT IMPLEMENTED */
//			return (la___setsockopt(sock, optname, optval, optlen));
	}

  	return (-EINVAL);
}


/*
 * Function: netbeui_getsockopt
 *	Gets some operational options of protocol stack (from socket layer
 *	down to LAN adapter).
 *
 * Parameters:
 *	sock    : a pointer to socket that action performs on it.
 *	level   : an integer that indicates level in protocol stack that
 *	          operation performs on.
 *	optname : an integer that indicates option that must be gotten.
 *	optval  : (VRP) a pointer to related data structure which used for
 *	          getting value(s) of option.
 *	optlen  : (VRP) length of data structure that 'optval' points to it.
 *
 * Returns: int
 *	0        : if operation is performed successfully.
 *	negative : if a fault occurs.
 *	           (-EINVAL) : Some of parameters are invalid.
 */

static int 
netbeui_getsockopt (struct socket *sock, int level, int optname, char *optval,
								 int *optlen)
{
	int   rc;

  	if (!optval || !optlen) 
  		return (-EINVAL);

	rc= verify_area(VERIFY_WRITE, optlen, sizeof(int));
	if (rc)
		return rc;

  	rc = verify_area(VERIFY_WRITE, optval, get_user((int *)optlen));
  	if (rc)
  		return rc;

	switch (level) {
		case SOL_SOCKET:
			return (nbso_getsockopt(sock, optname, optval, optlen));

		case SOL_NETBEUI:
			return (nbcs_getsockopt(sock, optname, optval, optlen));

		case SOL_LLC:
/* NOT IMPLEMENTED */
//			return (ll___getsockopt(sock, optname, optval, optlen));

		case SOL_LANA:
/* NOT IMPLEMENTED */
//			return (la___getsockopt(sock, optname, optval, optlen));
	}

  	return (-EINVAL);
}


/*
 * Function: netbeui_fcntl
 *	Performs some miscellaneous operations on sockets.
 *
 * Parameters:
 *	sock : pointer to socket that function must perform on it.
 *	cmd  : an integer that indicates what action must perform.
 *	arg  : this parameter often is a pointer to 'cmd' relative data structure
 *	       that be used by it as an argument.
 *
 * Returns: int
 *	0        : if operation is performed successfully. for GET commands
 *	           return code may be non-zero in successful return! (see
 *	           'fcntl()' man page for more).
 *	negative : if a fault occurs. error codes that bubble to user are
 *	           dependent to cmd.
 */

static int 
netbeui_fcntl (struct socket *sock, unsigned int cmd, unsigned long arg)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	switch(cmd) {
		case F_SETOWN:
			if (!suser() && 
			    (current->pgrp != -arg) &&
			    (current->pid != arg))
				return (-EACCES);

			sk->owner = arg;
			return 0;

		case F_GETOWN:
			return (sk->owner);

		default:
			/* Pass fcntl command to appropriate sub-protocol */
			return sk->prot->fcntl(sock, cmd, arg);
	}
}


/*
 * Function: netbeui_sendmsg
 *	Sends a message through the socket to desired target(s).
 *
 * Parameters:
 *	sock     : a pointer to socket that data sends through it.
 *	msg      : a pointer to 'struct msghdr' that contains message body,
 *	           target name and etc.
 *	len      : length of message all around.
 *	nonblock : an integer that if be set to non-zero value means that
 *	           no waiting (sleeping, blocking & ...) acceptable during
 *	           operation.
 *	sflags   : bitwise integer that contains socket flags.
 *
 * Returns: int
 *	positive : that means how much data was sent.
 *	negative : if a fault occurs.
 */

static int 
netbeui_sendmsg (struct socket *sock, struct msghdr *msg, int len, int nonblock,
								int sflags)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	return sk->prot->sendmsg(sock, msg, len, nonblock, sflags);
}


/*
 * Function: netbeui_recvmsg
 *	Receives a message through the socket from desired source(s).
 *
 * Parameters:
 *	sock     : a pointer to socket that data receives through it.
 *	msg      : (VRP) a pointer to 'struct msghdr' that at return contains
 *	           message body, source name and etc.
 *	size     : MAXimum length of message all around.
 *	nonblock : an integer that if be set to non-zero value means that
 *	           no waiting (sleeping, blocking & ...) acceptable during
 *	           operation.
 *	sflags   : bitwise integer that contains socket flags.
 *	addr_len : (VRP) a pointer to an integer that if it is not NULL, at
 *	           return will be filled with length of 'struct sockaddr_netbeui'.
 *
 * Returns: int
 *	positive : that means how much data was received.
 *	negative : if a fault occurs.
 */

static int 
netbeui_recvmsg (struct socket *sock, struct msghdr *msg, int size, int nonblock,
						int sflags, int *addr_len)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

	return sk->prot->recvmsg(sock, msg, size, nonblock, sflags, addr_len);
}


/*
 * NBSO internal functions 
 */

/*
 * Function: nbso_alloc_sock
 *	Allocates a NetBEUI socket structure and initials it to zeros.
 *
 * Parameters:
 *	priority : an integer that uses with kmalloc() for memory allocation.
 *
 * Returns: struct netbeui_sock *
 *	not NULL : pointer to allocated structure (means successfull).
 *	NULL     : means that kmalloc() fails to allocate memory with requested
 *	           priority.
 */

static inline struct netbeui_sock *
nbso_alloc_sock (int priority)
{
	struct netbeui_sock   *sk;

	sk = (struct netbeui_sock *) kmalloc(sizeof(*sk), priority);
	if (!sk)
		return NULL;

	memset(sk, 0, sizeof(*sk));
	return sk;
}


/*
 * Function: nbso_free_sock
 *	Frees an allocated netbeui socket structure.
 *
 * Parameters:
 *	sk : pointer to netbeui socket structure that must be freed.
 *
 * Returns: none
 */

static inline void
nbso_free_sock (struct netbeui_sock *sk)
{
	kfree(sk);
}


/*
 * Function: nbso_setsockopt
 *	Sets some operational options of protocol stack in socket layer.
 *
 * Parameters:
 *	sock    : a pointer to socket that must tune its options.
 *	optname : an integer that indicates option that must tune.
 *	optval  : a pointer to related data structure which used for assign
 *	          value(s) to option.
 *	optlen  : length of data structure that 'optval' points to it.
 *
 * Returns: int
 *	0        : if tuning is performed successfully.
 *	negative : if a fault occurs.
 *	           (-EOPNOTSUPP)  : Operation not supported by us.
 *	           (-ENOPROTOOPT) : Option name do not recognized by us.
 */

static int
nbso_setsockopt (struct socket *sock, int optname, void *optval, int optlen)
{
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;

  	switch (optname) {

		case SO_USELOOPBACK:
			return (-EOPNOTSUPP);
		
		default:
			/* Pass setsockopt option to appropriate sub-protocol */
			return sk->prot->setsockopt(sock, 0, optname, optval, optlen);
  	}

	/* NOTREACHED! */
	return(-ENOPROTOOPT);
}


/*
 * Function: nbso_getsockopt
 *	Gets some operational options of protocol stack in socket layer.
 *
 * Parameters:
 *	sock    : a pointer to socket that action performs on it.
 *	optname : an integer that indicates option that must be gotten.
 *	optval  : (VRP) a pointer to related data structure which used for
 *	          getting value(s) of option.
 *	optlen  : (VRP) length of data structure that 'optval' points to it.
 *
 * Returns: int
 *	0        : if operation is performed successfully.
 *	negative : if a fault occurs.
 *	           (-EOPNOTSUPP)  : Operation not supported by us.
 *	           (-ENOPROTOOPT) : Option name do not recognized by us.
 */

static int
nbso_getsockopt (struct socket *sock, int optname, void *optval, int *optlen)
{
	int   len;
	struct netbeui_sock   *sk = (struct netbeui_sock *) sock->data;
  	
  	switch (optname) {

		case SO_TYPE:
			len= MIN(get_user((int *)optlen), sizeof(sock->type));
			memcpy_tofs(optval, (void *)&sock->type, len);
			put_user(len, (int *)optlen);
			return 0;			

		case SO_ERROR:
			len= MIN(get_user((int *)optlen), sizeof(sk->error));
			memcpy_tofs(optval, (void *)&sk->error, len);
			put_user(len, (int *)optlen);
			sk->error = 0;
			return 0;			

		case SO_USELOOPBACK:
			return (-EOPNOTSUPP);
		
		default: 
			/* Pass getsockopt option to appropriate sub-protocol */
			return sk->prot->getsockopt(sock, 0, optname, optval, optlen);

  	}

	/* NOTREACHED! */
	return(-ENOPROTOOPT);
}


/*
 * Socket attach/detach 
 */

static struct proto_ops netbeui_proto_ops = {
	AF_NETBEUI,
	netbeui_create,       
	netbeui_dup,         
	netbeui_release,    
	netbeui_bind,      
	netbeui_connect,   
	netbeui_socketpair, 
	netbeui_accept,    
	netbeui_getname,  
	netbeui_select,    
	netbeui_ioctl,      
	netbeui_listen,      
	netbeui_shutdown,   
	netbeui_setsockopt, 
	netbeui_getsockopt,
	netbeui_fcntl,    
	netbeui_sendmsg, 
	netbeui_recvmsg 
};


/*
 * Function: nbso_init
 *	Initializes socket interface for servicing. This function is called
 *	in start of NetBEUI module installation in kernel memory.
 *
 * Parameters: none
 *
 * Returns: int
 *	0        : if operation is performed successfully.
 *	negative : if a fault occurs.
 */

int
nbso_init (void)
{
	int   rc;

	rc = sock_register(netbeui_proto_ops.family, &netbeui_proto_ops);

	return (rc < 0 ? rc : 0);
}


/*
 * Function: nbso_exit
 *	Prepares socket interface for termination of NetBEUI module operation.
 *
 * Parameters: none
 *
 * Returns: int
 *	0        : if operation is performed successfully.
 *	negative : if a fault occurs.
 */

int
nbso_exit (void)
{
	int   rc;

	rc = sock_unregister(netbeui_proto_ops.family);

	return (rc < 0 ? rc : 0);
}
