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