/*
* Copyright (c) 2000-2001 Boris Popov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Boris Popov.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/autoconf.h>
#include <sys/sysmacros.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_tran.h>
#include <netsmb/smb_trantcp.h>
/*
* SMB messages are up to 64K.
* Let's leave room for two.
*/
/*
* Get mblks into *mpp until the data length is at least mlen.
* Note that *mpp may already contain a fragment.
*
* If we ever have to wait more than 15 sec. to read a message,
* return ETIME. (Caller will declare the VD dead.)
*/
static int
{
int error = 0;
/* We should be the only reader. */
/* nbp->nbp_tiptr checked by caller */
/*
* Get the first message (fragment) if
* we don't already have a left-over.
*/
/*
* I think we still want this to return ETIME
* if nothing arrives for SMB_NBTIMO (15) sec.
* so we can report "server not responding".
* We _could_ just block here now that our
* IOD is just a reader.
*/
#if 1
/* Wait with timeout... */
events = 0;
if (error)
break;
/* file mode for recv is: */
#else
fmode = 0; /* normal (blocking) */
#endif
/* Get some more... */
continue;
if (error)
break;
/*
* Normally get M_DATA messages here,
* but have to check for other types.
*/
case M_DATA:
break;
case M_PROTO:
case M_PCPROTO:
/*LINTED*/
case T_DATA_IND:
/* remove 1st mblk, keep the rest. */
break;
case T_DISCON_IND:
/* Peer disconnected. */
NBDEBUG("T_DISCON_IND: reason=%d",
goto discon;
case T_ORDREL_IND:
/* Peer disconnecting. */
NBDEBUG("T_ORDREL_IND");
goto discon;
case T_OK_ACK:
case T_DISCON_REQ:
goto discon;
default:
goto discon;
}
default:
goto discon;
}
break; /* M_PROTO, M_PCPROTO */
default:
NBDEBUG("unexpected msg type=%d",
/*FALLTHROUGH*/
/*
* The connection is no longer usable.
* Drop this message and disconnect.
*
* Note: nb_disconnect only does t_snddis
* on the first call, but does important
* cleanup and state change on any call.
*/
(void) nb_disconnect(nbp);
return (ENOTCONN);
}
/*
* If we have a data message, append it to
* the previous chunk(s) and update dlen
*/
if (!tm)
continue;
} else {
/* Append */
;
}
}
return (error);
}
/*
* Send a T_DISCON_REQ (disconnect)
*/
static int
{
return (EBADF);
mlen = sizeof (struct T_discon_req);
return (error);
/*LINTED*/
/*
* either handled by our receiver thread, or just
* discarded if we're closing this endpoint.
*/
return (error);
}
/*
* Stuff the NetBIOS header into space already prepended.
*/
static void
{
uint32_t *p;
len &= 0x1FFFF;
/*LINTED*/
}
/*
* Wait for up to 15 sec. for the next packet.
* Often return ETIME and do nothing else.
* When a packet header is available, check
* the header and get the length, but don't
* consume it. No side effects here except
* for the pullupmsg call.
*/
static int
{
int error;
/*
* Get the first message (fragment) if
* we don't already have a left-over.
*/
if (error)
return (error);
return (ENOSR);
/*
* Check the NetBIOS header.
* (NOT consumed here)
*/
/*LINTED*/
return (EPIPE);
}
switch (*rpcodep) {
case NB_SSN_MESSAGE:
case NB_SSN_REQUEST:
case NB_SSN_POSRESP:
case NB_SSN_NEGRESP:
case NB_SSN_RTGRESP:
case NB_SSN_KEEPALIVE:
break;
default:
return (EPIPE);
}
len &= 0x1ffff;
if (len > NB_MAXPKTLEN) {
return (EFBIG);
}
return (0);
}
/*
* Receive a NetBIOS message. This may block to wait for the entire
* message to arrive. The caller knows there is (or should be) a
* message to be read. When we receive and drop a keepalive or
* zero-length message, return EAGAIN so the caller knows that
* something was received. This avoids false triggering of the
* "server not responding" state machine.
*
* Calls to this are serialized at a higher level.
*/
static int
{
int error;
/* We should be the only reader. */
return (EBADF);
if (mpp) {
if (*mpp) {
NBDEBUG("*mpp not 0 - leak?");
}
}
/*
* Get the NetBIOS header (not consumed yet)
*/
if (error) {
return (error);
}
NBDEBUG("Have pkt, type=0x%x len=0x%x\n",
/*
* Block here waiting for the whole packet to arrive.
* If we get a timeout, return without side effects.
* The data length we wait for here includes both the
* NetBIOS header and the payload.
*/
if (error) {
return (error);
}
/*
* We now have an entire NetBIOS message.
* Trim off the NetBIOS header and consume it.
* Note: _peekhdr has done pullupmsg for us,
* so we know it's safe to advance b_rptr.
*/
/*
* There may be more data after the message
* we're about to return, in which case we
* split it and leave the remainder.
*/
/*
* No session is established.
* Return whatever packet we got.
*/
goto out;
}
/*
* A session is established; the only packets
* we should see are session message and
* keep-alive packets. Drop anything else.
*/
switch (rpcode) {
case NB_SSN_KEEPALIVE:
/*
* It's a keepalive. Discard any data in it
* (there's not supposed to be any, but that
* doesn't mean some server won't send some)
*/
if (len)
break;
case NB_SSN_MESSAGE:
/*
* Session message. Does it have any data?
*/
if (len == 0) {
/*
* No data - treat as keepalive (drop).
*/
break;
}
/*
* Yes, has data. Return it.
*/
error = 0;
break;
default:
/*
* Drop anything else.
*/
break;
}
out:
if (error) {
if (m0)
return (error);
}
if (mpp)
else
return (0);
}
/*
* SMB transport interface
*
* This is called only by the thread creating this endpoint,
* so we're single-threaded here.
*/
/*ARGSUSED*/
static int
{
return (0);
}
/*
* destroy a transport endpoint
*
* This is called only by the thread with the last reference
* to this endpoint, so we're single-threaded here.
*/
static int
{
return (ENOTCONN);
/*
* Don't really need to disconnect here,
* because the close following will do it.
* But it's harmless.
*/
(void) nb_disconnect(nbp);
return (0);
}
/*
* Loan a transport file pointer (from user space) to this
* IOD endpoint. There should be no other thread using this
* endpoint when we do this, but lock for consistency.
*/
static int
{
int err;
if (err != 0)
return (err);
return (0);
}
/*
* Take back the transport file pointer we previously loaned.
* It's possible there may be another thread in here, so let
* others get out of the way before we pull the rug out.
*
* Some notes about the locking here: The higher-level IOD code
* serializes activity such that at most one reader and writer
* thread can be active in this code (and possibly both).
* Keeping nbp_lock held during the activities of these two
* threads would lead to the possibility of nbp_lock being
* held by a blocked thread, so this instead sets one of the
* flags (NBF_SENDLOCK | NBF_RECVLOCK) when a sender or a
* receiver is active (respectively). Lastly, tear-down is
* the only tricky bit (here) where we must wait for any of
* these activities to get out of current calls so they will
* notice that we've turned off the NBF_CONNECTED flag.
*/
static void
{
}
}
}
}
static int
{
int error = 0;
/*
* Un-loan the existing one, if any.
*/
(void) nb_disconnect(nbp);
/*
* Loan the new one passed in.
*/
}
return (error);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
/*ARGSUSED*/
static int
{
return (ENOTCONN);
return (nb_disconnect(nbp));
}
static int
{
int err = 0;
}
return (err);
}
/*
* Add the NetBIOS session header and send.
*
* Calls to this are serialized at a higher level.
*/
static int
{
int error;
/* We should be the only sender. */
goto errout;
}
/*
* Get the message length, which
* does NOT include the NetBIOS header
*/
/*
* Normally, mb_init() will have left space
* for us to prepend the NetBIOS header in
* the data block of the first mblk.
* However, we have to check in case other
* code did not leave this space, or if the
* message is from dupmsg (db_ref > 1)
*
* If don't find room in the first data block,
* we have to allocb a new message and link it
* on the front of the chain. We try not to
* do this becuase it's less efficient. Also,
* some network drivers will apparently send
* each mblk in the chain as separate frames.
* (That's arguably a driver bug.)
*
* Not bothering with allocb_cred_wait below
* because the message we're prepending to
* should already have a db_credp.
*/
/* We can use the first dblk. */
m->b_rptr -= 4;
} else {
/* Link a new mblk on the head. */
/* M_PREPEND */
goto errout;
m = m0;
}
return (error);
if (m != NULL)
m_freem(m);
return (error);
}
/*
* Always consume the message.
* (On error too!)
*/
static int
{
int err;
goto out;
}
NBDEBUG("multiple smb_nbst_send!\n");
err = EWOULDBLOCK;
goto out;
}
m = NULL; /* nbssn_send always consumes this */
}
out:
if (m != NULL)
m_freem(m);
return (err);
}
static int
{
goto out;
}
NBDEBUG("multiple smb_nbst_recv!\n");
err = EWOULDBLOCK;
goto out;
}
}
out:
return (err);
}
/*
* Wait for up to "ticks" clock ticks for input on vcp.
* Returns zero if input is available, otherwise ETIME
* indicating time expired, or other error codes.
*/
/*ARGSUSED*/
static int
{
return (ENOTSUP);
}
static int
{
switch (param) {
case SMBTP_SNDSZ:
break;
case SMBTP_RCVSZ:
break;
case SMBTP_TIMEOUT:
break;
#ifdef SMBTP_SELECTID
case SMBTP_SELECTID:
break;
#endif
#ifdef SMBTP_UPCALL
case SMBTP_UPCALL:
break;
#endif
default:
return (EINVAL);
}
return (0);
}
/*ARGSUSED*/
static int
{
return (EINVAL);
}
/*
* Check for fatal errors
*/
/*ARGSUSED*/
static int
{
switch (error) {
case ENOTCONN:
case ENETRESET:
case ECONNABORTED:
case EPIPE:
return (1);
}
return (0);
}
};