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