1 /* $NetBSD: smb_trantcp.c,v 1.30 2007/07/09 21:11:15 ad Exp $ */
4 * Copyright (c) 2000-2001 Boris Popov
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * FreeBSD: src/sys/netsmb/smb_trantcp.c,v 1.17 2003/02/19 05:47:38 imp Exp
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: smb_trantcp.c,v 1.30 2007/07/09 21:11:15 ad Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
51 #include <sys/sysctl.h>
54 #include <net/route.h>
56 #include <netinet/in.h>
57 #include <netinet/tcp.h>
59 #include <netsmb/mchain.h>
61 #include <netsmb/netbios.h>
63 #include <netsmb/smb.h>
64 #include <netsmb/smb_conn.h>
65 #include <netsmb/smb_tran.h>
66 #include <netsmb/smb_trantcp.h>
67 #include <netsmb/smb_subr.h>
69 #define M_NBDATA M_PCB
71 static int nb_tcpsndbuf
= NB_SNDQ
;
72 static int nb_tcprcvbuf
= NB_RCVQ
;
73 static const struct timeval nb_timo
= { 15, 0 }; /* XXX sysctl? */
76 SYSCTL_DECL(_net_smb
);
77 SYSCTL_INT(_net_smb
, OID_AUTO
, tcpsndbuf
, CTLFLAG_RW
, &nb_tcpsndbuf
, 0, "");
78 SYSCTL_INT(_net_smb
, OID_AUTO
, tcprcvbuf
, CTLFLAG_RW
, &nb_tcprcvbuf
, 0, "");
82 #define nb_sosend(so,m,flags,p) (so)->so_proto->pr_usrreqs->pru_sosend( \
83 so, NULL, 0, m, 0, flags, p)
85 #define nb_sosend(so,m,flags,l) (*(so)->so_send)(so, NULL, (struct uio *)0, \
86 m, (struct mbuf *)0, flags, l)
89 static int nbssn_recv(struct nbpcb
*nbp
, struct mbuf
**mpp
, int *lenp
,
90 u_int8_t
*rpcodep
, struct lwp
*l
);
91 static int smb_nbst_disconnect(struct smb_vc
*vcp
, struct lwp
*l
);
94 nb_setsockopt_int(struct socket
*so
, int level
, int name
, int val
)
97 return sosetopt(so
, level
, name
, NULL
); /* XXX */
101 bzero(&sopt
, sizeof(sopt
));
102 sopt
.sopt_level
= level
;
103 sopt
.sopt_name
= name
;
104 sopt
.sopt_val
= &val
;
105 sopt
.sopt_valsize
= sizeof(val
);
106 return sosetopt(so
, &sopt
);
111 nb_poll(struct nbpcb
*nbp
, int events
, struct lwp
*l
)
114 return nbp
->nbp_tso
->so_proto
->pr_usrreqs
->pru_sopoll(nbp
->nbp_tso
,
117 /* XXX this is exactly equal to soo_poll() */
118 struct socket
*so
= nbp
->nbp_tso
;
120 int s
= splsoftnet();
122 if (events
& (POLLIN
| POLLRDNORM
))
124 revents
|= events
& (POLLIN
| POLLRDNORM
);
126 if (events
& (POLLOUT
| POLLWRNORM
))
128 revents
|= events
& (POLLOUT
| POLLWRNORM
);
130 if (events
& (POLLPRI
| POLLRDBAND
))
131 if (so
->so_oobmark
|| (so
->so_state
& SS_RCVATMARK
))
132 revents
|= events
& (POLLPRI
| POLLRDBAND
);
135 if (events
& (POLLIN
| POLLPRI
| POLLRDNORM
| POLLRDBAND
)) {
136 selrecord(l
, &so
->so_rcv
.sb_sel
);
137 so
->so_rcv
.sb_flags
|= SB_SEL
;
140 if (events
& (POLLOUT
| POLLWRNORM
)) {
141 selrecord(l
, &so
->so_snd
.sb_sel
);
142 so
->so_snd
.sb_flags
|= SB_SEL
;
151 /* XXX WTF re-implemented select()? */
153 nbssn_rselect(struct nbpcb
*nbp
, const struct timeval
*tv
, int events
,
156 extern kcondvar_t select_cv
;
157 extern kmutex_t select_lock
;
169 return (EWOULDBLOCK
);
173 mutex_enter(&select_lock
);
177 mutex_exit(&select_lock
);
178 error
= nb_poll(nbp
, events
, l
);
179 mutex_enter(&select_lock
);
186 * We have to recalculate the timeout on every retry.
195 if (l
->l_selflag
!= 1 || nselcoll
!= ncoll
)
198 error
= cv_timedwait(&select_cv
, &select_lock
, timo
);
204 mutex_exit(&select_lock
);
205 /* select is not restarted after signals... */
206 if (error
== ERESTART
)
212 nb_intr(struct nbpcb
*nbp
, struct lwp
*l
)
218 nb_upcall(struct socket
*so
, void *arg
, int waitflag
)
220 struct nbpcb
*nbp
= (void *)arg
;
222 if (arg
== NULL
|| nbp
->nbp_selectid
== NULL
)
224 wakeup(nbp
->nbp_selectid
);
228 nb_sethdr(struct mbuf
*m
, u_int8_t type
, u_int32_t len
)
230 u_int32_t
*p
= mtod(m
, u_int32_t
*);
232 *p
= htonl((len
& 0x1FFFF) | (type
<< 24));
237 nb_put_name(struct mbchain
*mbp
, struct sockaddr_nb
*snb
)
245 NBDEBUG("[%s]\n", cp
);
248 error
= mb_put_mem(mbp
, cp
, seglen
, MB_MSYSTEM
);
259 nb_connect_in(struct nbpcb
*nbp
, struct sockaddr_in
*to
, struct lwp
*l
)
267 error
= socreate(AF_INET
, &so
, SOCK_STREAM
, IPPROTO_TCP
, l
);
271 so
->so_upcallarg
= (void *)nbp
;
272 so
->so_upcall
= nb_upcall
;
273 so
->so_rcv
.sb_flags
|= SB_UPCALL
;
274 so
->so_rcv
.sb_timeo
= NB_SNDTIMEO
;
275 so
->so_snd
.sb_timeo
= NB_RCVTIMEO
;
276 error
= soreserve(so
, nb_tcpsndbuf
, nb_tcprcvbuf
);
279 nb_setsockopt_int(so
, SOL_SOCKET
, SO_KEEPALIVE
, 1);
280 nb_setsockopt_int(so
, IPPROTO_TCP
, TCP_NODELAY
, 1);
281 so
->so_rcv
.sb_flags
&= ~SB_NOINTR
;
282 so
->so_snd
.sb_flags
&= ~SB_NOINTR
;
284 error
= soconnect(so
, (struct sockaddr
*)to
, l
);
286 m
= m_get(M_WAIT
, MT_SONAME
);
287 *mtod(m
, struct sockaddr
*) = *(struct sockaddr
*)to
;
288 m
->m_len
= sizeof(struct sockaddr
);
289 error
= soconnect(so
, m
, l
);
295 while ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0) {
296 tsleep(&so
->so_timeo
, PSOCK
, "smbcon", 2 * hz
);
297 if ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0 &&
298 (error
= nb_intr(nbp
, l
)) != 0) {
299 so
->so_state
&= ~SS_ISCONNECTING
;
305 error
= so
->so_error
;
313 smb_nbst_disconnect(nbp
->nbp_vc
, l
);
318 nbssn_rq_request(struct nbpcb
*nbp
, struct lwp
*l
)
320 struct mbchain mb
, *mbp
= &mb
;
321 struct mdchain md
, *mdp
= &md
;
323 struct sockaddr_in sin
;
328 error
= mb_init(mbp
);
331 mb_put_uint32le(mbp
, 0);
332 (void) nb_put_name(mbp
, nbp
->nbp_paddr
);
333 (void) nb_put_name(mbp
, nbp
->nbp_laddr
);
334 nb_sethdr(mbp
->mb_top
, NB_SSN_REQUEST
, mb_fixhdr(mbp
) - 4);
335 error
= nb_sosend(nbp
->nbp_tso
, mbp
->mb_top
, 0, l
);
337 nbp
->nbp_state
= NBST_RQSENT
;
343 error
= nbssn_rselect(nbp
, &nb_timo
, POLLIN
, l
);
344 if (error
== EWOULDBLOCK
) { /* Timeout */
345 NBDEBUG("initial request timeout\n");
348 if (error
) /* restart or interrupt */
350 error
= nbssn_recv(nbp
, &m0
, &rplen
, &rpcode
, l
);
352 NBDEBUG("recv() error %d\n", error
);
356 * Process NETBIOS reply
362 if (rpcode
== NB_SSN_POSRESP
) {
363 nbp
->nbp_state
= NBST_SESSION
;
364 nbp
->nbp_flags
|= NBF_CONNECTED
;
367 if (rpcode
!= NB_SSN_RTGRESP
) {
368 error
= ECONNABORTED
;
372 error
= ECONNABORTED
;
375 md_get_mem(mdp
, (void *)&sin
.sin_addr
, 4, MB_MSYSTEM
);
376 md_get_uint16(mdp
, &port
);
378 nbp
->nbp_state
= NBST_RETARGET
;
379 smb_nbst_disconnect(nbp
->nbp_vc
, l
);
380 error
= nb_connect_in(nbp
, &sin
, l
);
382 error
= nbssn_rq_request(nbp
, l
);
384 smb_nbst_disconnect(nbp
->nbp_vc
, l
);
394 nbssn_recvhdr(struct nbpcb
*nbp
, int *lenp
,
395 u_int8_t
*rpcodep
, int flags
, struct lwp
*l
)
397 struct socket
*so
= nbp
->nbp_tso
;
403 aio
.iov_base
= (void *)&len
;
404 aio
.iov_len
= sizeof(len
);
407 auio
.uio_rw
= UIO_READ
;
409 auio
.uio_resid
= sizeof(len
);
410 UIO_SETUP_SYSSPACE(&auio
);
412 error
= so
->so_proto
->pr_usrreqs
->pru_soreceive
413 (so
, (struct sockaddr
**)NULL
, &auio
,
414 (struct mbuf
**)NULL
, (struct mbuf
**)NULL
, &flags
);
416 error
= (*so
->so_receive
)(so
, (struct mbuf
**)0, &auio
,
417 (struct mbuf
**)NULL
,
418 (struct mbuf
**)NULL
, &flags
);
422 if (auio
.uio_resid
> 0) {
423 SMBSDEBUG("short reply\n");
427 *rpcodep
= (len
>> 24) & 0xFF;
429 if (len
> SMB_MAXPKTLEN
) {
430 SMBERROR("packet too long (%d)\n", len
);
438 nbssn_recv(struct nbpcb
*nbp
, struct mbuf
**mpp
, int *lenp
,
439 u_int8_t
*rpcodep
, struct lwp
*l
)
441 struct socket
*so
= nbp
->nbp_tso
;
443 struct mbuf
*m
, *tm
, *im
;
448 len
= 0; /* XXX gcc */
449 rpcode
= 0; /* XXX gcc */
459 * Poll for a response header.
460 * If we don't have one waiting, return.
462 error
= nbssn_recvhdr(nbp
, &len
, &rpcode
, MSG_DONTWAIT
, l
);
464 (SS_ISDISCONNECTING
| SS_ISDISCONNECTED
| SS_CANTRCVMORE
)) {
465 nbp
->nbp_state
= NBST_CLOSED
;
466 NBDEBUG("session closed by peer\n");
471 if (len
== 0 && nbp
->nbp_state
!= NBST_SESSION
)
473 /* no data, try again */
474 if (rpcode
== NB_SSN_KEEPALIVE
)
478 * Loop, blocking, for data following the response header.
480 * Note that we can't simply block here with MSG_WAITALL for the
481 * entire response size, as it may be larger than the TCP
482 * slow-start window that the sender employs. This will result
483 * in the sender stalling until the delayed ACK is sent, then
484 * resuming slow-start, resulting in very poor performance.
486 * Instead, we never request more than NB_SORECEIVE_CHUNK
487 * bytes at a time, resulting in an ack being pushed by
488 * the TCP code at the completion of each call.
493 rcvflg
= MSG_WAITALL
;
494 bzero(&auio
, sizeof(auio
));
495 auio
.uio_resid
= min(resid
, NB_SORECEIVE_CHUNK
);
496 /* not need to setup uio_vmspace */
497 resid
-= auio
.uio_resid
;
499 * Spin until we have collected everything in
503 rcvflg
= MSG_WAITALL
;
505 error
= (*so
->so_receive
)(so
, (struct mbuf
**)0,
506 &auio
, &tm
, (struct mbuf
**)NULL
,
509 error
= so
->so_proto
->pr_usrreqs
->pru_soreceive
510 (so
, (struct sockaddr
**)NULL
,
511 &auio
, &tm
, (struct mbuf
**)NULL
, &rcvflg
);
513 } while (error
== EWOULDBLOCK
|| error
== EINTR
||
517 /* short return guarantees unhappiness */
518 if (auio
.uio_resid
> 0) {
519 SMBERROR("packet is shorter than expected\n");
523 /* append received chunk to previous chunk(s) */
528 * Just glue the new chain on the end.
529 * Consumer will pullup as required.
531 for (im
= m
; im
->m_next
!= NULL
; im
= im
->m_next
)
536 /* got a session/message packet? */
537 if (nbp
->nbp_state
== NBST_SESSION
&&
538 rpcode
== NB_SSN_MESSAGE
)
540 /* drop packet and try for another */
541 NBDEBUG("non-session packet %x\n", rpcode
);
564 * SMB transport interface
567 smb_nbst_create(struct smb_vc
*vcp
, struct lwp
*l
)
571 MALLOC(nbp
, struct nbpcb
*, sizeof *nbp
, M_NBDATA
, M_WAITOK
);
572 memset(nbp
, 0, sizeof *nbp
);
573 nbp
->nbp_state
= NBST_CLOSED
;
580 smb_nbst_done(struct smb_vc
*vcp
, struct lwp
*l
)
582 struct nbpcb
*nbp
= vcp
->vc_tdata
;
586 smb_nbst_disconnect(vcp
, l
);
588 free(nbp
->nbp_laddr
, M_SONAME
);
590 free(nbp
->nbp_paddr
, M_SONAME
);
596 smb_nbst_bind(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct lwp
*l
)
598 struct nbpcb
*nbp
= vcp
->vc_tdata
;
599 struct sockaddr_nb
*snb
;
605 if (nbp
->nbp_flags
& NBF_LOCADDR
)
608 * It is possible to create NETBIOS name in the kernel,
609 * but nothing prevents us to do it in the user space.
614 if (slen
< NB_MINSALEN
)
616 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
, 1);
621 nbp
->nbp_laddr
= snb
;
622 nbp
->nbp_flags
|= NBF_LOCADDR
;
629 smb_nbst_connect(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct lwp
*l
)
631 struct nbpcb
*nbp
= vcp
->vc_tdata
;
632 struct sockaddr_in sin
;
633 struct sockaddr_nb
*snb
;
637 if (nbp
->nbp_tso
!= NULL
)
639 if (nbp
->nbp_laddr
== NULL
)
642 if (slen
< NB_MINSALEN
)
644 if (nbp
->nbp_paddr
) {
645 free(nbp
->nbp_paddr
, M_SONAME
);
646 nbp
->nbp_paddr
= NULL
;
648 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
, 1);
651 nbp
->nbp_paddr
= snb
;
652 sin
= snb
->snb_addrin
;
653 error
= nb_connect_in(nbp
, &sin
, l
);
656 error
= nbssn_rq_request(nbp
, l
);
658 smb_nbst_disconnect(vcp
, l
);
663 smb_nbst_disconnect(struct smb_vc
*vcp
, struct lwp
*l
)
665 struct nbpcb
*nbp
= vcp
->vc_tdata
;
668 if (nbp
== NULL
|| nbp
->nbp_tso
== NULL
)
670 if ((so
= nbp
->nbp_tso
) != NULL
) {
671 nbp
->nbp_flags
&= ~NBF_CONNECTED
;
672 nbp
->nbp_tso
= (struct socket
*)NULL
;
676 if (nbp
->nbp_state
!= NBST_RETARGET
) {
677 nbp
->nbp_state
= NBST_CLOSED
;
683 smb_nbst_send(struct smb_vc
*vcp
, struct mbuf
*m0
, struct lwp
*l
)
685 struct nbpcb
*nbp
= vcp
->vc_tdata
;
688 if (nbp
->nbp_state
!= NBST_SESSION
) {
692 M_PREPEND(m0
, 4, M_WAITOK
);
695 nb_sethdr(m0
, NB_SSN_MESSAGE
, m_fixhdr(m0
) - 4);
696 error
= nb_sosend(nbp
->nbp_tso
, m0
, 0, l
);
706 smb_nbst_recv(struct smb_vc
*vcp
, struct mbuf
**mpp
, struct lwp
*l
)
708 struct nbpcb
*nbp
= vcp
->vc_tdata
;
712 nbp
->nbp_flags
|= NBF_RECVLOCK
;
713 error
= nbssn_recv(nbp
, mpp
, &rplen
, &rpcode
, l
);
714 nbp
->nbp_flags
&= ~NBF_RECVLOCK
;
719 smb_nbst_timo(struct smb_vc
*vcp
)
726 smb_nbst_intr(struct smb_vc
*vcp
)
728 struct nbpcb
*nbp
= vcp
->vc_tdata
;
730 if (nbp
== NULL
|| nbp
->nbp_tso
== NULL
)
732 sorwakeup(nbp
->nbp_tso
);
733 sowwakeup(nbp
->nbp_tso
);
737 smb_nbst_getparam(struct smb_vc
*vcp
, int param
, void *data
)
741 *(int*)data
= nb_tcpsndbuf
;
744 *(int*)data
= nb_tcprcvbuf
;
747 *(struct timeval
*)data
= nb_timo
;
756 smb_nbst_setparam(struct smb_vc
*vcp
, int param
, void *data
)
758 struct nbpcb
*nbp
= vcp
->vc_tdata
;
762 nbp
->nbp_selectid
= data
;
771 * Check for fatal errors
774 smb_nbst_fatal(struct smb_vc
*vcp
, int error
)
786 struct smb_tran_desc smb_tran_nbtcp_desc
= {
788 smb_nbst_create
, smb_nbst_done
,
789 smb_nbst_bind
, smb_nbst_connect
, smb_nbst_disconnect
,
790 smb_nbst_send
, smb_nbst_recv
,
791 smb_nbst_timo
, smb_nbst_intr
,
792 smb_nbst_getparam
, smb_nbst_setparam
,