]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netsmb/smb_trantcp.c
This commit was generated by cvs2svn to compensate for changes in r89232,
[FreeBSD/FreeBSD.git] / sys / netsmb / smb_trantcp.c
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  * $FreeBSD$
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/proc.h>
40 #include <sys/protosw.h>
41 #include <sys/socket.h>
42 #include <sys/socketvar.h>
43 #include <sys/poll.h>
44 #include <sys/uio.h>
45 #include <sys/sysctl.h>
46 #include <sys/condvar.h>
47
48 #include <net/if.h>
49 #include <net/route.h>
50
51 #include <netinet/in.h>
52 #include <netinet/tcp.h>
53
54 #include <sys/mchain.h>
55
56 #include <netsmb/netbios.h>
57
58 #include <netsmb/smb.h>
59 #include <netsmb/smb_conn.h>
60 #include <netsmb/smb_tran.h>
61 #include <netsmb/smb_trantcp.h>
62 #include <netsmb/smb_subr.h>
63
64 #define M_NBDATA        M_PCB
65
66 static int smb_tcpsndbuf = 10 * 1024;
67 static int smb_tcprcvbuf = 10 * 1024;
68
69 SYSCTL_DECL(_net_smb);
70 SYSCTL_INT(_net_smb, OID_AUTO, tcpsndbuf, CTLFLAG_RW, &smb_tcpsndbuf, 0, "");
71 SYSCTL_INT(_net_smb, OID_AUTO, tcprcvbuf, CTLFLAG_RW, &smb_tcprcvbuf, 0, "");
72
73 #define nb_sosend(so,m,flags,td) (so)->so_proto->pr_usrreqs->pru_sosend( \
74                                     so, NULL, 0, m, 0, flags, td)
75
76 static int  nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
77         u_int8_t *rpcodep, struct thread *td);
78 static int  smb_nbst_disconnect(struct smb_vc *vcp, struct thread *td);
79
80 static int
81 nb_setsockopt_int(struct socket *so, int level, int name, int val)
82 {
83         struct sockopt sopt;
84
85         bzero(&sopt, sizeof(sopt));
86         sopt.sopt_level = level;
87         sopt.sopt_name = name;
88         sopt.sopt_val = &val;
89         sopt.sopt_valsize = sizeof(val);
90         return sosetopt(so, &sopt);
91 }
92
93 static __inline int
94 nb_poll(struct nbpcb *nbp, int events, struct thread *td)
95 {
96         return nbp->nbp_tso->so_proto->pr_usrreqs->pru_sopoll(nbp->nbp_tso,
97             events, NULL, td);
98 }
99
100 static int
101 nbssn_rselect(struct nbpcb *nbp, struct timeval *tv, int events,
102         struct thread *td)
103 {
104         struct timeval atv, rtv, ttv;
105         struct proc *p;
106         int timo, error;
107
108         if (tv) {
109                 atv = *tv;
110                 if (itimerfix(&atv)) {
111                         error = EINVAL;
112                         goto done_noproclock;
113                 }
114                 getmicrouptime(&rtv);
115                 timevaladd(&atv, &rtv);
116         }
117         timo = 0;
118         p = td->td_proc;
119         PROC_LOCK(p);
120         mtx_lock_spin(&sched_lock);
121         td->td_flags |= TDF_SELECT;
122         mtx_unlock_spin(&sched_lock);
123         PROC_UNLOCK(p);
124         error = nb_poll(nbp, events, td);
125         PROC_LOCK(p);
126         if (error) {
127                 error = 0;
128                 goto done;
129         }
130         if (tv) {
131                 getmicrouptime(&rtv);
132                 if (timevalcmp(&rtv, &atv, >=)) {
133                         /*
134                          * An event of our interest may occur during locking a process.
135                          * In order to avoid missing the event that occurred during locking
136                          * the process, test P_SELECT and rescan file descriptors if
137                          * necessary.
138                          */
139                         mtx_lock_spin(&sched_lock);
140                         if ((td->td_flags & TDF_SELECT) == 0) {
141                                 td->td_flags |= TDF_SELECT;
142                                 mtx_unlock_spin(&sched_lock);
143                                 PROC_UNLOCK(p);
144                                 error = nb_poll(nbp, events, td);
145                                 PROC_LOCK(p);
146                         } else
147                                 mtx_unlock_spin(&sched_lock);
148                         goto done;
149                 }
150                 ttv = atv;
151                 timevalsub(&ttv, &rtv);
152                 timo = tvtohz(&ttv);
153         }
154         mtx_lock_spin(&sched_lock);
155         td->td_flags &= ~TDF_SELECT;
156         mtx_unlock_spin(&sched_lock);
157         if (timo > 0)
158                 error = cv_timedwait(&selwait, &p->p_mtx, timo);
159         else {
160                 cv_wait(&selwait, &p->p_mtx);
161                 error = 0;
162         }
163
164 done:
165         mtx_lock_spin(&sched_lock);
166         td->td_flags &= ~TDF_SELECT;
167         mtx_unlock_spin(&sched_lock);
168         PROC_UNLOCK(p);
169
170 done_noproclock:
171         if (error == ERESTART)
172                 return 0;
173         return error;
174 }
175
176 static int
177 nb_intr(struct nbpcb *nbp, struct proc *p)
178 {
179         return 0;
180 }
181
182 static void
183 nb_upcall(struct socket *so, void *arg, int waitflag)
184 {
185         struct nbpcb *nbp = arg;
186
187         if (arg == NULL || nbp->nbp_selectid == NULL)
188                 return;
189         wakeup(nbp->nbp_selectid);
190 }
191
192 static int
193 nb_sethdr(struct mbuf *m, u_int8_t type, u_int32_t len)
194 {
195         u_int32_t *p = mtod(m, u_int32_t *);
196
197         *p = htonl((len & 0x1FFFF) | (type << 24));
198         return 0;
199 }
200
201 static int
202 nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb)
203 {
204         int error;
205         u_char seglen, *cp;
206
207         cp = snb->snb_name;
208         if (*cp == 0)
209                 return EINVAL;
210         NBDEBUG("[%s]\n", cp);
211         for (;;) {
212                 seglen = (*cp) + 1;
213                 error = mb_put_mem(mbp, cp, seglen, MB_MSYSTEM);
214                 if (error)
215                         return error;
216                 if (seglen == 1)
217                         break;
218                 cp += seglen;
219         }
220         return 0;
221 }
222
223 static int
224 nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct thread *td)
225 {
226         struct socket *so;
227         int error, s;
228
229         error = socreate(AF_INET, &so, SOCK_STREAM, IPPROTO_TCP,
230             td->td_proc->p_ucred, td);
231         if (error)
232                 return error;
233         nbp->nbp_tso = so;
234         so->so_upcallarg = (caddr_t)nbp;
235         so->so_upcall = nb_upcall;
236         so->so_rcv.sb_flags |= SB_UPCALL;
237         so->so_rcv.sb_timeo = (5 * hz);
238         so->so_snd.sb_timeo = (5 * hz);
239         error = soreserve(so, nbp->nbp_sndbuf, nbp->nbp_rcvbuf);
240         if (error)
241                 goto bad;
242         nb_setsockopt_int(so, SOL_SOCKET, SO_KEEPALIVE, 1);
243         nb_setsockopt_int(so, IPPROTO_TCP, TCP_NODELAY, 1);
244         so->so_rcv.sb_flags &= ~SB_NOINTR;
245         so->so_snd.sb_flags &= ~SB_NOINTR;
246         error = soconnect(so, (struct sockaddr*)to, td);
247         if (error)
248                 goto bad;
249         s = splnet();
250         while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
251                 tsleep(&so->so_timeo, PSOCK, "nbcon", 2 * hz);
252                 if ((so->so_state & SS_ISCONNECTING) && so->so_error == 0 &&
253                         (error = nb_intr(nbp, td->td_proc)) != 0) {
254                         so->so_state &= ~SS_ISCONNECTING;
255                         splx(s);
256                         goto bad;
257                 }
258         }
259         if (so->so_error) {
260                 error = so->so_error;
261                 so->so_error = 0;
262                 splx(s);
263                 goto bad;
264         }
265         splx(s);
266         return 0;
267 bad:
268         smb_nbst_disconnect(nbp->nbp_vc, td);
269         return error;
270 }
271
272 static int
273 nbssn_rq_request(struct nbpcb *nbp, struct thread *td)
274 {
275         struct mbchain mb, *mbp = &mb;
276         struct mdchain md, *mdp = &md;
277         struct mbuf *m0;
278         struct timeval tv;
279         struct sockaddr_in sin;
280         u_short port;
281         u_int8_t rpcode;
282         int error, rplen;
283
284         error = mb_init(mbp);
285         if (error)
286                 return error;
287         mb_put_uint32le(mbp, 0);
288         nb_put_name(mbp, nbp->nbp_paddr);
289         nb_put_name(mbp, nbp->nbp_laddr);
290         nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4);
291         error = nb_sosend(nbp->nbp_tso, mbp->mb_top, 0, td);
292         if (!error) {
293                 nbp->nbp_state = NBST_RQSENT;
294         }
295         mb_detach(mbp);
296         mb_done(mbp);
297         if (error)
298                 return error;
299         TIMESPEC_TO_TIMEVAL(&tv, &nbp->nbp_timo);
300         error = nbssn_rselect(nbp, &tv, POLLIN, td);
301         if (error == EWOULDBLOCK) {     /* Timeout */
302                 NBDEBUG("initial request timeout\n");
303                 return ETIMEDOUT;
304         }
305         if (error)                      /* restart or interrupt */
306                 return error;
307         error = nbssn_recv(nbp, &m0, &rplen, &rpcode, td);
308         if (error) {
309                 NBDEBUG("recv() error %d\n", error);
310                 return error;
311         }
312         /*
313          * Process NETBIOS reply
314          */
315         if (m0)
316                 md_initm(mdp, m0);
317         error = 0;
318         do {
319                 if (rpcode == NB_SSN_POSRESP) {
320                         nbp->nbp_state = NBST_SESSION;
321                         nbp->nbp_flags |= NBF_CONNECTED;
322                         break;
323                 }
324                 if (rpcode != NB_SSN_RTGRESP) {
325                         error = ECONNABORTED;
326                         break;
327                 }
328                 if (rplen != 6) {
329                         error = ECONNABORTED;
330                         break;
331                 }
332                 md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM);
333                 md_get_uint16(mdp, &port);
334                 sin.sin_port = port;
335                 nbp->nbp_state = NBST_RETARGET;
336                 smb_nbst_disconnect(nbp->nbp_vc, td);
337                 error = nb_connect_in(nbp, &sin, td);
338                 if (!error)
339                         error = nbssn_rq_request(nbp, td);
340                 if (error) {
341                         smb_nbst_disconnect(nbp->nbp_vc, td);
342                         break;
343                 }
344         } while(0);
345         if (m0)
346                 md_done(mdp);
347         return error;
348 }
349
350 static int
351 nbssn_recvhdr(struct nbpcb *nbp, int *lenp,
352         u_int8_t *rpcodep, int flags, struct thread *td)
353 {
354         struct socket *so = nbp->nbp_tso;
355         struct uio auio;
356         struct iovec aio;
357         u_int32_t len;
358         int error;
359
360         aio.iov_base = (caddr_t)&len;
361         aio.iov_len = sizeof(len);
362         auio.uio_iov = &aio;
363         auio.uio_iovcnt = 1;
364         auio.uio_segflg = UIO_SYSSPACE;
365         auio.uio_rw = UIO_READ;
366         auio.uio_offset = 0;
367         auio.uio_resid = sizeof(len);
368         auio.uio_td = td;
369         error = so->so_proto->pr_usrreqs->pru_soreceive
370             (so, (struct sockaddr **)NULL, &auio,
371             (struct mbuf **)NULL, (struct mbuf **)NULL, &flags);
372         if (error)
373                 return error;
374         if (auio.uio_resid > 0) {
375                 SMBSDEBUG("short reply\n");
376                 return EPIPE;
377         }
378         len = ntohl(len);
379         *rpcodep = (len >> 24) & 0xFF;
380         len &= 0x1ffff;
381         if (len > SMB_MAXPKTLEN) {
382                 SMBERROR("packet too long (%d)\n", len);
383                 return EFBIG;
384         }
385         *lenp = len;
386         return 0;
387 }
388
389 static int
390 nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
391         u_int8_t *rpcodep, struct thread *td)
392 {
393         struct socket *so = nbp->nbp_tso;
394         struct uio auio;
395         struct mbuf *m;
396         u_int8_t rpcode;
397         int len;
398         int error, rcvflg;
399
400         if (so == NULL)
401                 return ENOTCONN;
402
403         if (mpp)
404                 *mpp = NULL;
405         for(;;) {
406                 m = NULL;
407                 error = nbssn_recvhdr(nbp, &len, &rpcode, MSG_DONTWAIT, td);
408                 if (so->so_state &
409                     (SS_ISDISCONNECTING | SS_ISDISCONNECTED | SS_CANTRCVMORE)) {
410                         nbp->nbp_state = NBST_CLOSED;
411                         NBDEBUG("session closed by peer\n");
412                         return ECONNRESET;
413                 }
414                 if (error)
415                         return error;
416                 if (len == 0 && nbp->nbp_state != NBST_SESSION)
417                         break;
418                 if (rpcode == NB_SSN_KEEPALIVE)
419                         continue;
420                 bzero(&auio, sizeof(auio));
421                 auio.uio_resid = len;
422                 auio.uio_td = td;
423                 do {
424                         rcvflg = MSG_WAITALL;
425                         error = so->so_proto->pr_usrreqs->pru_soreceive
426                             (so, (struct sockaddr **)NULL,
427                             &auio, &m, (struct mbuf **)NULL, &rcvflg);
428                 } while (error == EWOULDBLOCK || error == EINTR ||
429                                  error == ERESTART);
430                 if (error)
431                         break;
432                 if (auio.uio_resid > 0) {
433                         SMBERROR("packet is shorter than expected\n");
434                         error = EPIPE;
435                         break;
436                 }
437                 if (nbp->nbp_state == NBST_SESSION &&
438                     rpcode == NB_SSN_MESSAGE)
439                         break;
440                 NBDEBUG("non-session packet %x\n", rpcode);
441                 if (m)
442                         m_freem(m);
443         }
444         if (error) {
445                 if (m)
446                         m_freem(m);
447                 return error;
448         }
449         if (mpp)
450                 *mpp = m;
451         else
452                 m_freem(m);
453         *lenp = len;
454         *rpcodep = rpcode;
455         return 0;
456 }
457
458 /*
459  * SMB transport interface
460  */
461 static int
462 smb_nbst_create(struct smb_vc *vcp, struct thread *td)
463 {
464         struct nbpcb *nbp;
465
466         MALLOC(nbp, struct nbpcb *, sizeof *nbp, M_NBDATA, M_WAITOK);
467         bzero(nbp, sizeof *nbp);
468         nbp->nbp_timo.tv_sec = 15;      /* XXX: sysctl ? */
469         nbp->nbp_state = NBST_CLOSED;
470         nbp->nbp_vc = vcp;
471         nbp->nbp_sndbuf = smb_tcpsndbuf;
472         nbp->nbp_rcvbuf = smb_tcprcvbuf;
473         vcp->vc_tdata = nbp;
474         return 0;
475 }
476
477 static int
478 smb_nbst_done(struct smb_vc *vcp, struct thread *td)
479 {
480         struct nbpcb *nbp = vcp->vc_tdata;
481
482         if (nbp == NULL)
483                 return ENOTCONN;
484         smb_nbst_disconnect(vcp, td);
485         if (nbp->nbp_laddr)
486                 free(nbp->nbp_laddr, M_SONAME);
487         if (nbp->nbp_paddr)
488                 free(nbp->nbp_paddr, M_SONAME);
489         free(nbp, M_NBDATA);
490         return 0;
491 }
492
493 static int
494 smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct thread *td)
495 {
496         struct nbpcb *nbp = vcp->vc_tdata;
497         struct sockaddr_nb *snb;
498         int error, slen;
499
500         NBDEBUG("\n");
501         error = EINVAL;
502         do {
503                 if (nbp->nbp_flags & NBF_LOCADDR)
504                         break;
505                 /*
506                  * It is possible to create NETBIOS name in the kernel,
507                  * but nothing prevents us to do it in the user space.
508                  */
509                 if (sap == NULL)
510                         break;
511                 slen = sap->sa_len;
512                 if (slen < NB_MINSALEN)
513                         break;
514                 snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1);
515                 if (snb == NULL) {
516                         error = ENOMEM;
517                         break;
518                 }
519                 nbp->nbp_laddr = snb;
520                 nbp->nbp_flags |= NBF_LOCADDR;
521                 error = 0;
522         } while(0);
523         return error;
524 }
525
526 static int
527 smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct thread *td)
528 {
529         struct nbpcb *nbp = vcp->vc_tdata;
530         struct sockaddr_in sin;
531         struct sockaddr_nb *snb;
532         struct timespec ts1, ts2;
533         int error, slen;
534
535         NBDEBUG("\n");
536         if (nbp->nbp_tso != NULL)
537                 return EISCONN;
538         if (nbp->nbp_laddr == NULL)
539                 return EINVAL;
540         slen = sap->sa_len;
541         if (slen < NB_MINSALEN)
542                 return EINVAL;
543         if (nbp->nbp_paddr) {
544                 free(nbp->nbp_paddr, M_SONAME);
545                 nbp->nbp_paddr = NULL;
546         }
547         snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1);
548         if (snb == NULL)
549                 return ENOMEM;
550         nbp->nbp_paddr = snb;
551         sin = snb->snb_addrin;
552         getnanotime(&ts1);
553         error = nb_connect_in(nbp, &sin, td);
554         if (error)
555                 return error;
556         getnanotime(&ts2);
557         timespecsub(&ts2, &ts1);
558         if (ts2.tv_sec == 0 && ts2.tv_sec == 0)
559                 ts2.tv_sec = 1;
560         nbp->nbp_timo = ts2;
561         timespecadd(&nbp->nbp_timo, &ts2);
562         timespecadd(&nbp->nbp_timo, &ts2);
563         timespecadd(&nbp->nbp_timo, &ts2);      /*  * 4 */
564         error = nbssn_rq_request(nbp, td);
565         if (error)
566                 smb_nbst_disconnect(vcp, td);
567         return error;
568 }
569
570 static int
571 smb_nbst_disconnect(struct smb_vc *vcp, struct thread *td)
572 {
573         struct nbpcb *nbp = vcp->vc_tdata;
574         struct socket *so;
575
576         if (nbp == NULL || nbp->nbp_tso == NULL)
577                 return ENOTCONN;
578         if ((so = nbp->nbp_tso) != NULL) {
579                 nbp->nbp_flags &= ~NBF_CONNECTED;
580                 nbp->nbp_tso = (struct socket *)NULL;
581                 soshutdown(so, 2);
582                 soclose(so);
583         }
584         if (nbp->nbp_state != NBST_RETARGET) {
585                 nbp->nbp_state = NBST_CLOSED;
586         }
587         return 0;
588 }
589
590 static int
591 smb_nbst_send(struct smb_vc *vcp, struct mbuf *m0, struct thread *td)
592 {
593         struct nbpcb *nbp = vcp->vc_tdata;
594         int error;
595
596         if (nbp->nbp_state != NBST_SESSION) {
597                 error = ENOTCONN;
598                 goto abort;
599         }
600         M_PREPEND(m0, 4, M_WAITOK);
601         if (m0 == NULL)
602                 return ENOBUFS;
603         nb_sethdr(m0, NB_SSN_MESSAGE, m_fixhdr(m0) - 4);
604         error = nb_sosend(nbp->nbp_tso, m0, 0, td);
605         return error;
606 abort:
607         if (m0)
608                 m_freem(m0);
609         return error;
610 }
611
612
613 static int
614 smb_nbst_recv(struct smb_vc *vcp, struct mbuf **mpp, struct thread *td)
615 {
616         struct nbpcb *nbp = vcp->vc_tdata;
617         u_int8_t rpcode;
618         int error, rplen;
619
620         nbp->nbp_flags |= NBF_RECVLOCK;
621         error = nbssn_recv(nbp, mpp, &rplen, &rpcode, td);
622         nbp->nbp_flags &= ~NBF_RECVLOCK;
623         return error;
624 }
625
626 static void
627 smb_nbst_timo(struct smb_vc *vcp)
628 {
629         return;
630 }
631
632 static void
633 smb_nbst_intr(struct smb_vc *vcp)
634 {
635         struct nbpcb *nbp = vcp->vc_tdata;
636
637         if (nbp == NULL || nbp->nbp_tso == NULL)
638                 return;
639         sorwakeup(nbp->nbp_tso);
640         sowwakeup(nbp->nbp_tso);
641 }
642
643 static int
644 smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
645 {
646         struct nbpcb *nbp = vcp->vc_tdata;
647
648         switch (param) {
649             case SMBTP_SNDSZ:
650                 *(int*)data = nbp->nbp_sndbuf;
651                 break;
652             case SMBTP_RCVSZ:
653                 *(int*)data = nbp->nbp_rcvbuf;
654                 break;
655             case SMBTP_TIMEOUT:
656                 *(struct timespec*)data = nbp->nbp_timo;
657                 break;
658             default:
659                 return EINVAL;
660         }
661         return 0;
662 }
663
664 static int
665 smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
666 {
667         struct nbpcb *nbp = vcp->vc_tdata;
668
669         switch (param) {
670             case SMBTP_SELECTID:
671                 nbp->nbp_selectid = data;
672                 break;
673             default:
674                 return EINVAL;
675         }
676         return 0;
677 }
678
679 /*
680  * Check for fatal errors
681  */
682 static int
683 smb_nbst_fatal(struct smb_vc *vcp, int error)
684 {
685         switch (error) {
686             case ENOTCONN:
687             case ENETRESET:
688             case ECONNABORTED:
689                 return 1;
690         }
691         return 0;
692 }
693
694
695 struct smb_tran_desc smb_tran_nbtcp_desc = {
696         SMBT_NBTCP,
697         smb_nbst_create, smb_nbst_done,
698         smb_nbst_bind, smb_nbst_connect, smb_nbst_disconnect,
699         smb_nbst_send, smb_nbst_recv,
700         smb_nbst_timo, smb_nbst_intr,
701         smb_nbst_getparam, smb_nbst_setparam,
702         smb_nbst_fatal
703 };
704