xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
1 /*
2  * Copyright (c) 2000-2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $
33  */
34 /*
35  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
36  * Use is subject to license terms.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/autoconf.h>
42 #include <sys/sysmacros.h>
43 #include <sys/sunddi.h>
44 #include <sys/kmem.h>
45 #include <sys/proc.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/poll.h>
49 #include <sys/stream.h>
50 #include <sys/strsubr.h>
51 #include <sys/strsun.h>
52 #include <sys/stropts.h>
53 #include <sys/cmn_err.h>
54 #include <sys/tihdr.h>
55 #include <sys/tiuser.h>
56 #include <sys/t_kuser.h>
57 #include <sys/priv.h>
58 
59 #include <net/if.h>
60 #include <net/route.h>
61 
62 #include <netinet/in.h>
63 #include <netinet/tcp.h>
64 
65 #ifdef APPLE
66 #include <sys/smb_apple.h>
67 #else
68 #include <netsmb/smb_osdep.h>
69 #endif
70 
71 #include <netsmb/mchain.h>
72 #include <netsmb/netbios.h>
73 
74 #include <netsmb/smb.h>
75 #include <netsmb/smb_conn.h>
76 #include <netsmb/smb_subr.h>
77 #include <netsmb/smb_tran.h>
78 #include <netsmb/smb_trantcp.h>
79 
80 /*
81  * SMB messages are up to 64K.
82  * Let's leave room for two.
83  */
84 static int smb_tcpsndbuf = 0x20000;
85 static int smb_tcprcvbuf = 0x20000;
86 
87 static int  nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
88 	uint8_t *rpcodep);
89 static int  nb_disconnect(struct nbpcb *nbp);
90 
91 
92 /*
93  * Get mblks into *mpp until the data length is at least mlen.
94  * Note that *mpp may already contain a fragment.
95  *
96  * If we ever have to wait more than 15 sec. to read a message,
97  * return ETIME.  (Caller will declare the VD dead.)
98  */
99 static int
100 nb_getmsg_mlen(struct nbpcb *nbp, mblk_t **mpp, size_t mlen)
101 {
102 	mblk_t *im, *tm;
103 	union T_primitives	*pptr;
104 	size_t dlen;
105 	int events, fmode, timo, waitflg;
106 	int error = 0;
107 
108 	/*
109 	 * Get the first message (fragment) if
110 	 * we don't already have a left-over.
111 	 */
112 	dlen = msgdsize(*mpp); /* *mpp==null is OK */
113 	while (dlen < mlen) {
114 
115 		/*
116 		 * I think we still want this to return ETIME
117 		 * if nothing arrives for SMB_NBTIMO (15) sec.
118 		 * so we can report "server not responding".
119 		 * We _could_ just block here now that our
120 		 * IOD is just a reader.
121 		 */
122 #if 1
123 		/* Wait with timeout... */
124 		events = 0;
125 		waitflg = READWAIT;
126 		timo = SEC_TO_TICK(SMB_NBTIMO);
127 		error = t_kspoll(nbp->nbp_tiptr, timo, waitflg, &events);
128 		if (!error && !events)
129 			error = ETIME;
130 		if (error)
131 			break;
132 		/* file mode for recv is: */
133 		fmode = FNDELAY; /* non-blocking */
134 #else
135 		fmode = 0; /* normal (blocking) */
136 #endif
137 
138 		/* Get some more... */
139 		tm = NULL;
140 		error = tli_recv(nbp->nbp_tiptr, &tm, fmode);
141 		if (error == EAGAIN)
142 			continue;
143 		if (error)
144 			break;
145 
146 		/*
147 		 * Normally get M_DATA messages here,
148 		 * but have to check for other types.
149 		 */
150 		switch (tm->b_datap->db_type) {
151 		case M_DATA:
152 			break;
153 		case M_PROTO:
154 		case M_PCPROTO:
155 			/*LINTED*/
156 			pptr = (union T_primitives *)tm->b_rptr;
157 			switch (pptr->type) {
158 			case T_DATA_IND:
159 				/* remove 1st mblk, keep the rest. */
160 				im = tm->b_cont;
161 				tm->b_cont = NULL;
162 				freeb(tm);
163 				tm = im;
164 				break;
165 			case T_DISCON_IND:
166 				/* Peer disconnected. */
167 				NBDEBUG("T_DISCON_IND: reason=%d",
168 				    pptr->discon_ind.DISCON_reason);
169 				goto discon;
170 			case T_ORDREL_IND:
171 				/* Peer disconnecting. */
172 				NBDEBUG("T_ORDREL_IND");
173 				goto discon;
174 			case T_OK_ACK:
175 				switch (pptr->ok_ack.CORRECT_prim) {
176 				case T_DISCON_REQ:
177 					NBDEBUG("T_OK_ACK/T_DISCON_REQ");
178 					goto discon;
179 				default:
180 					NBDEBUG("T_OK_ACK/prim=%d",
181 					    pptr->ok_ack.CORRECT_prim);
182 					goto discon;
183 				}
184 			default:
185 				NBDEBUG("M_PROTO/type=%d", pptr->type);
186 				goto discon;
187 			}
188 			break; /* M_PROTO, M_PCPROTO */
189 
190 		default:
191 			NBDEBUG("unexpected msg type=%d",
192 			    tm->b_datap->db_type);
193 			/*FALLTHROUGH*/
194 discon:
195 			/*
196 			 * The connection is no longer usable.
197 			 * Drop this message and disconnect.
198 			 *
199 			 * Note: nb_disconnect only does t_snddis
200 			 * on the first call, but does important
201 			 * cleanup and state change on any call.
202 			 */
203 			freemsg(tm);
204 			nb_disconnect(nbp);
205 			return (ENOTCONN);
206 		}
207 
208 		/*
209 		 * If we have a data message, append it to
210 		 * the previous chunk(s) and update dlen
211 		 */
212 		if (!tm)
213 			continue;
214 		if (*mpp == NULL) {
215 			*mpp = tm;
216 		} else {
217 			/* Append */
218 			for (im = *mpp; im->b_cont; im = im->b_cont)
219 				;
220 			im->b_cont = tm;
221 		}
222 		dlen += msgdsize(tm);
223 	}
224 
225 	return (error);
226 }
227 
228 /*
229  * Send a T_DISCON_REQ (disconnect)
230  */
231 static int
232 nb_snddis(TIUSER *tiptr)
233 {
234 	cred_t *cr;
235 	mblk_t *mp;
236 	struct T_discon_req *dreq;
237 	int error, fmode, mlen;
238 
239 	cr = ddi_get_cred();
240 	mlen = sizeof (struct T_discon_req);
241 	if (!(mp = allocb_cred_wait(mlen, STR_NOSIG, &error, cr, NOPID)))
242 		return (error);
243 
244 	mp->b_datap->db_type = M_PROTO;
245 	/*LINTED*/
246 	dreq = (struct T_discon_req *)mp->b_wptr;
247 	dreq->PRIM_type = T_DISCON_REQ;
248 	dreq->SEQ_number = -1;
249 	mp->b_wptr += sizeof (struct T_discon_req);
250 
251 	fmode = tiptr->fp->f_flag;
252 	if ((error = tli_send(tiptr, mp, fmode)) != 0)
253 		return (error);
254 
255 	fmode = 0; /* need to block */
256 	error = get_ok_ack(tiptr, T_DISCON_REQ, fmode);
257 
258 	return (error);
259 }
260 
261 /*
262  * Stuff the NetBIOS header into space already prepended.
263  */
264 static int
265 nb_sethdr(mblk_t *m, uint8_t type, uint32_t len)
266 {
267 	uint32_t *p;
268 
269 	len &= 0x1FFFF;
270 	len |= (type << 24);
271 
272 	/*LINTED*/
273 	p = (uint32_t *)m->b_rptr;
274 	*p = htonl(len);
275 	return (0);
276 }
277 
278 /*
279  * Wait for up to 15 sec. for the next packet.
280  * Often return ETIME and do nothing else.
281  * When a packet header is available, check
282  * the header and get the length, but don't
283  * consume it.  No side effects here except
284  * for the pullupmsg call.
285  */
286 static int
287 nbssn_peekhdr(struct nbpcb *nbp, size_t *lenp,	uint8_t *rpcodep)
288 {
289 	uint32_t len, *hdr;
290 	int error;
291 
292 	/*
293 	 * Get the first message (fragment) if
294 	 * we don't already have a left-over.
295 	 */
296 	error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, sizeof (len));
297 	if (error)
298 		return (error);
299 
300 	if (!pullupmsg(nbp->nbp_frag, sizeof (len)))
301 		return (ENOSR);
302 
303 	/*
304 	 * Check the NetBIOS header.
305 	 * (NOT consumed here)
306 	 */
307 	/*LINTED*/
308 	hdr = (uint32_t *)nbp->nbp_frag->b_rptr;
309 
310 	len = ntohl(*hdr);
311 	if ((len >> 16) & 0xFE) {
312 		NBDEBUG("bad nb header received 0x%x (MBZ flag set)\n", len);
313 		return (EPIPE);
314 	}
315 	*rpcodep = (len >> 24) & 0xFF;
316 	switch (*rpcodep) {
317 	case NB_SSN_MESSAGE:
318 	case NB_SSN_REQUEST:
319 	case NB_SSN_POSRESP:
320 	case NB_SSN_NEGRESP:
321 	case NB_SSN_RTGRESP:
322 	case NB_SSN_KEEPALIVE:
323 		break;
324 	default:
325 		NBDEBUG("bad nb header received 0x%x (bogus type)\n", len);
326 		return (EPIPE);
327 	}
328 	len &= 0x1ffff;
329 	if (len > NB_MAXPKTLEN) {
330 		NBDEBUG("packet too long (%d)\n", len);
331 		return (EFBIG);
332 	}
333 	*lenp = len;
334 	return (0);
335 }
336 
337 /*
338  * Receive a NetBIOS message.  This may block to wait for the entire
339  * message to arrive.  The caller knows there is (or should be) a
340  * message to be read.  When we receive and drop a keepalive or
341  * zero-length message, return EAGAIN so the caller knows that
342  * something was received.  This avoids false triggering of the
343  * "server not responding" state machine.
344  */
345 /*ARGSUSED*/
346 static int
347 nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
348     uint8_t *rpcodep)
349 {
350 	TIUSER *tiptr = nbp->nbp_tiptr;
351 	mblk_t *m0;
352 	uint8_t rpcode;
353 	int error;
354 	size_t rlen, len;
355 
356 	/* We should be the only reader. */
357 	ASSERT(nbp->nbp_flags & NBF_RECVLOCK);
358 	if ((nbp->nbp_flags & NBF_CONNECTED) == 0)
359 		return (ENOTCONN);
360 
361 	if (tiptr == NULL)
362 		return (EBADF);
363 	if (mpp) {
364 		if (*mpp) {
365 			NBDEBUG("*mpp not 0 - leak?");
366 		}
367 		*mpp = NULL;
368 	}
369 	m0 = NULL;
370 
371 	/*
372 	 * Get the NetBIOS header (not consumed yet)
373 	 */
374 	error = nbssn_peekhdr(nbp, &len, &rpcode);
375 	if (error) {
376 		if (error != ETIME)
377 			NBDEBUG("peekhdr, error=%d\n", error);
378 		return (error);
379 	}
380 	NBDEBUG("Have pkt, type=0x%x len=0x%x\n",
381 	    (int)rpcode, (int)len);
382 
383 	/*
384 	 * Block here waiting for the whole packet to arrive.
385 	 * If we get a timeout, return without side effects.
386 	 * The data length we wait for here includes both the
387 	 * NetBIOS header and the payload.
388 	 */
389 	error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, len + 4);
390 	if (error) {
391 		NBDEBUG("getmsg(body), error=%d\n", error);
392 		return (error);
393 	}
394 
395 	/*
396 	 * We now have an entire NetBIOS message.
397 	 * Trim off the NetBIOS header and consume it.
398 	 * Note: _peekhdr has done pullupmsg for us,
399 	 * so we know it's safe to advance b_rptr.
400 	 */
401 	m0 = nbp->nbp_frag;
402 	m0->b_rptr += 4;
403 
404 	/*
405 	 * There may be more data after the message
406 	 * we're about to return, in which case we
407 	 * split it and leave the remainder.
408 	 */
409 	rlen = msgdsize(m0);
410 	ASSERT(rlen >= len);
411 	nbp->nbp_frag = NULL;
412 	if (rlen > len)
413 		nbp->nbp_frag = m_split(m0, len, 1);
414 
415 	if (nbp->nbp_state != NBST_SESSION) {
416 		/*
417 		 * No session is established.
418 		 * Return whatever packet we got.
419 		 */
420 		goto out;
421 	}
422 
423 	/*
424 	 * A session is established; the only packets
425 	 * we should see are session message and
426 	 * keep-alive packets.  Drop anything else.
427 	 */
428 	switch (rpcode) {
429 
430 	case NB_SSN_KEEPALIVE:
431 		/*
432 		 * It's a keepalive.  Discard any data in it
433 		 * (there's not supposed to be any, but that
434 		 * doesn't mean some server won't send some)
435 		 */
436 		if (len)
437 			NBDEBUG("Keepalive with data %d\n", (int)len);
438 		error = EAGAIN;
439 		break;
440 
441 	case NB_SSN_MESSAGE:
442 		/*
443 		 * Session message.  Does it have any data?
444 		 */
445 		if (len == 0) {
446 			/*
447 			 * No data - treat as keepalive (drop).
448 			 */
449 			error = EAGAIN;
450 			break;
451 		}
452 		/*
453 		 * Yes, has data.  Return it.
454 		 */
455 		error = 0;
456 		break;
457 
458 	default:
459 		/*
460 		 * Drop anything else.
461 		 */
462 		NBDEBUG("non-session packet %x\n", rpcode);
463 		error = EAGAIN;
464 		break;
465 	}
466 
467 out:
468 	if (error) {
469 		if (m0)
470 			m_freem(m0);
471 		return (error);
472 	}
473 	if (mpp)
474 		*mpp = m0;
475 	else
476 		m_freem(m0);
477 	*lenp = (int)len;
478 	*rpcodep = rpcode;
479 	return (0);
480 }
481 
482 /*
483  * SMB transport interface
484  */
485 /*ARGSUSED*/
486 static int
487 smb_nbst_create(struct smb_vc *vcp, cred_t *cr)
488 {
489 	struct nbpcb *nbp;
490 
491 	nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP);
492 
493 	nbp->nbp_timo.tv_sec = SMB_NBTIMO;
494 	nbp->nbp_state = NBST_CLOSED; /* really IDLE */
495 	nbp->nbp_vc = vcp;
496 	nbp->nbp_sndbuf = smb_tcpsndbuf;
497 	nbp->nbp_rcvbuf = smb_tcprcvbuf;
498 	mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL);
499 	vcp->vc_tdata = nbp;
500 
501 	return (0);
502 }
503 
504 /*ARGSUSED*/
505 static int
506 smb_nbst_done(struct smb_vc *vcp)
507 {
508 	struct nbpcb *nbp = vcp->vc_tdata;
509 
510 	if (nbp == NULL)
511 		return (ENOTCONN);
512 	vcp->vc_tdata = NULL;
513 
514 	/*
515 	 * Don't really need to disconnect here,
516 	 * because the close following will do it.
517 	 * But it's harmless.
518 	 */
519 	if (nbp->nbp_flags & NBF_CONNECTED)
520 		nb_disconnect(nbp);
521 	if (nbp->nbp_tiptr)
522 		t_kclose(nbp->nbp_tiptr, 0);
523 	if (nbp->nbp_laddr)
524 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr);
525 	if (nbp->nbp_paddr)
526 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr);
527 	mutex_destroy(&nbp->nbp_lock);
528 	kmem_free(nbp, sizeof (*nbp));
529 	return (0);
530 }
531 
532 static int
533 smb_nbst_loan_fp(struct smb_vc *vcp, struct file *fp, cred_t *cr)
534 {
535 	struct nbpcb *nbp = vcp->vc_tdata;
536 	TIUSER *tiptr;
537 	int error = 0;
538 
539 	mutex_enter(&nbp->nbp_lock);
540 
541 	/*
542 	 * Un-loan the existing one, if any.
543 	 */
544 	if (nbp->nbp_tiptr != NULL) {
545 		t_kclose(nbp->nbp_tiptr, 0);
546 		nbp->nbp_tiptr = NULL;
547 		nbp->nbp_flags &= ~NBF_CONNECTED;
548 		nbp->nbp_state = NBST_CLOSED;
549 	}
550 
551 	/*
552 	 * Loan the new one passed in.
553 	 */
554 	if (fp != NULL && 0 == (error =
555 	    t_kopen(fp, 0, 0, &tiptr, cr))) {
556 		nbp->nbp_tiptr = tiptr;
557 		nbp->nbp_fmode = tiptr->fp->f_flag;
558 		nbp->nbp_flags |= NBF_CONNECTED;
559 		nbp->nbp_state = NBST_SESSION;
560 	}
561 
562 	mutex_exit(&nbp->nbp_lock);
563 
564 	return (error);
565 }
566 
567 /*ARGSUSED*/
568 static int
569 smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap)
570 {
571 	return (ENOTSUP);
572 }
573 
574 /*ARGSUSED*/
575 static int
576 smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap)
577 {
578 	return (ENOTSUP);
579 }
580 
581 /*ARGSUSED*/
582 static int
583 smb_nbst_disconnect(struct smb_vc *vcp)
584 {
585 	struct nbpcb *nbp = vcp->vc_tdata;
586 
587 	if (nbp == NULL)
588 		return (ENOTCONN);
589 
590 	return (nb_disconnect(nbp));
591 }
592 
593 static int
594 nb_disconnect(struct nbpcb *nbp)
595 {
596 	TIUSER *tiptr;
597 	int save_flags;
598 
599 	tiptr = nbp->nbp_tiptr;
600 	if (tiptr == NULL)
601 		return (EBADF);
602 
603 	mutex_enter(&nbp->nbp_lock);
604 	save_flags = nbp->nbp_flags;
605 	nbp->nbp_flags &= ~NBF_CONNECTED;
606 	if (nbp->nbp_frag) {
607 		freemsg(nbp->nbp_frag);
608 		nbp->nbp_frag = NULL;
609 	}
610 	mutex_exit(&nbp->nbp_lock);
611 
612 	if (save_flags & NBF_CONNECTED)
613 		nb_snddis(tiptr);
614 
615 	if (nbp->nbp_state != NBST_RETARGET) {
616 		nbp->nbp_state = NBST_CLOSED;
617 	}
618 	return (0);
619 }
620 
621 /*
622  * Always consume the message.
623  * (On error too!)
624  */
625 /*ARGSUSED*/
626 static int
627 smb_nbst_send(struct smb_vc *vcp, mblk_t *m)
628 {
629 	struct nbpcb *nbp = vcp->vc_tdata;
630 	ptrdiff_t diff;
631 	uint32_t mlen;
632 	int error;
633 
634 	if (nbp == NULL || nbp->nbp_tiptr == NULL) {
635 		error = EBADF;
636 		goto errout;
637 	}
638 
639 	/*
640 	 * Get the message length, which
641 	 * does NOT include the NetBIOS header
642 	 */
643 	mlen = msgdsize(m);
644 
645 	/*
646 	 * Normally, mb_init() will have left space
647 	 * for us to prepend the NetBIOS header in
648 	 * the data block of the first mblk.
649 	 * However, we have to check in case other
650 	 * code did not leave this space, or if the
651 	 * message is from dupmsg (db_ref > 1)
652 	 *
653 	 * If don't find room in the first data block,
654 	 * we have to allocb a new message and link it
655 	 * on the front of the chain.  We try not to
656 	 * do this becuase it's less efficient.  Also,
657 	 * some network drivers will apparently send
658 	 * each mblk in the chain as separate frames.
659 	 * (That's arguably a driver bug.)
660 	 *
661 	 * Not bothering with allocb_cred_wait below
662 	 * because the message we're prepending to
663 	 * should already have a db_credp.
664 	 */
665 
666 	diff = MBLKHEAD(m);
667 	if (diff == 4 && DB_REF(m) == 1) {
668 		/* We can use the first dblk. */
669 		m->b_rptr -= 4;
670 	} else {
671 		/* Link a new mblk on the head. */
672 		mblk_t *m0;
673 
674 		/* M_PREPEND */
675 		m0 = allocb_wait(4, BPRI_LO, STR_NOSIG, &error);
676 		if (!m0)
677 			goto errout;
678 
679 		m0->b_wptr += 4;
680 		m0->b_cont = m;
681 		m = m0;
682 	}
683 
684 	nb_sethdr(m, NB_SSN_MESSAGE, mlen);
685 	error = tli_send(nbp->nbp_tiptr, m, 0);
686 	return (error);
687 
688 errout:
689 	if (m)
690 		m_freem(m);
691 	return (error);
692 }
693 
694 static int
695 smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp)
696 {
697 	struct nbpcb *nbp = vcp->vc_tdata;
698 	uint8_t rpcode;
699 	int error, rplen;
700 
701 	mutex_enter(&nbp->nbp_lock);
702 	if (nbp->nbp_flags & NBF_RECVLOCK) {
703 		NBDEBUG("attempt to reenter session layer!\n");
704 		mutex_exit(&nbp->nbp_lock);
705 		return (EWOULDBLOCK);
706 	}
707 	nbp->nbp_flags |= NBF_RECVLOCK;
708 	mutex_exit(&nbp->nbp_lock);
709 	error = nbssn_recv(nbp, mpp, &rplen, &rpcode);
710 	mutex_enter(&nbp->nbp_lock);
711 	nbp->nbp_flags &= ~NBF_RECVLOCK;
712 	mutex_exit(&nbp->nbp_lock);
713 	return (error);
714 }
715 
716 /*
717  * Wait for up to "ticks" clock ticks for input on vcp.
718  * Returns zero if input is available, otherwise ETIME
719  * indicating time expired, or other error codes.
720  */
721 /*ARGSUSED*/
722 static int
723 smb_nbst_poll(struct smb_vc *vcp, int ticks)
724 {
725 	int error;
726 	int events = 0;
727 	int waitflg = READWAIT;
728 	struct nbpcb *nbp = vcp->vc_tdata;
729 
730 	error = t_kspoll(nbp->nbp_tiptr, ticks, waitflg, &events);
731 	if (!error && !events)
732 		error = ETIME;
733 
734 	return (error);
735 }
736 
737 static int
738 smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
739 {
740 	struct nbpcb *nbp = vcp->vc_tdata;
741 
742 	switch (param) {
743 	case SMBTP_SNDSZ:
744 		*(int *)data = nbp->nbp_sndbuf;
745 		break;
746 	case SMBTP_RCVSZ:
747 		*(int *)data = nbp->nbp_rcvbuf;
748 		break;
749 	case SMBTP_TIMEOUT:
750 		*(struct timespec *)data = nbp->nbp_timo;
751 		break;
752 #ifdef SMBTP_SELECTID
753 	case SMBTP_SELECTID:
754 		*(void **)data = nbp->nbp_selectid;
755 		break;
756 #endif
757 #ifdef SMBTP_UPCALL
758 	case SMBTP_UPCALL:
759 		*(void **)data = nbp->nbp_upcall;
760 		break;
761 #endif
762 	default:
763 		return (EINVAL);
764 	}
765 	return (0);
766 }
767 
768 /*ARGSUSED*/
769 static int
770 smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
771 {
772 	return (EINVAL);
773 }
774 
775 /*
776  * Check for fatal errors
777  */
778 /*ARGSUSED*/
779 static int
780 smb_nbst_fatal(struct smb_vc *vcp, int error)
781 {
782 	switch (error) {
783 	case ENOTCONN:
784 	case ENETRESET:
785 	case ECONNABORTED:
786 	case EPIPE:
787 		return (1);
788 	}
789 	return (0);
790 }
791 
792 
793 struct smb_tran_desc smb_tran_nbtcp_desc = {
794 	SMBT_NBTCP,
795 	smb_nbst_create,
796 	smb_nbst_done,
797 	smb_nbst_bind,
798 	smb_nbst_connect,
799 	smb_nbst_disconnect,
800 	smb_nbst_send,
801 	smb_nbst_recv,
802 	smb_nbst_poll,
803 	smb_nbst_loan_fp,
804 	smb_nbst_getparam,
805 	smb_nbst_setparam,
806 	smb_nbst_fatal,
807 	{NULL, NULL}
808 };
809