1 /* $NetBSD: smb_trantcp.c,v 1.42 2009/03/29 19:21:20 christos Exp $ */
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
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.
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
30 * Copyright (c) 2000-2001 Boris Popov
31 * All rights reserved.
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by Boris Popov.
44 * 4. Neither the name of the author nor the names of any co-contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * FreeBSD: src/sys/netsmb/smb_trantcp.c,v 1.17 2003/02/19 05:47:38 imp Exp
63 #include <sys/cdefs.h>
64 __KERNEL_RCSID(0, "$NetBSD: smb_trantcp.c,v 1.42 2009/03/29 19:21:20 christos Exp $");
66 #include <sys/param.h>
67 #include <sys/systm.h>
68 #include <sys/kernel.h>
69 #include <sys/malloc.h>
72 #include <sys/protosw.h>
73 #include <sys/socket.h>
74 #include <sys/socketvar.h>
77 #include <sys/select.h>
80 #include <net/route.h>
82 #include <netinet/in.h>
83 #include <netinet/tcp.h>
85 #include <netsmb/mchain.h>
87 #include <netsmb/netbios.h>
89 #include <netsmb/smb.h>
90 #include <netsmb/smb_conn.h>
91 #include <netsmb/smb_tran.h>
92 #include <netsmb/smb_trantcp.h>
93 #include <netsmb/smb_subr.h>
95 #define M_NBDATA M_PCB
97 static int nb_tcpsndbuf
= NB_SNDQ
;
98 static int nb_tcprcvbuf
= NB_RCVQ
;
99 static const struct timespec nb_timo
= { 15, 0 }; /* XXX sysctl? */
101 #define nb_sosend(so,m,flags,l) (*(so)->so_send)(so, NULL, (struct uio *)0, \
102 m, (struct mbuf *)0, flags, l)
104 static int nbssn_recv(struct nbpcb
*nbp
, struct mbuf
**mpp
, int *lenp
,
105 u_int8_t
*rpcodep
, struct lwp
*l
);
106 static int smb_nbst_disconnect(struct smb_vc
*vcp
, struct lwp
*l
);
109 nb_setsockopt_int(struct socket
*so
, int level
, int name
, int val
)
112 return so_setsockopt(NULL
, so
, level
, name
, &val
, sizeof(val
)); /* XXX */
116 nbssn_rselect(struct nbpcb
*nbp
, const struct timespec
*ts
, int events
,
120 return pollsock(nbp
->nbp_tso
, ts
, events
);
124 nb_intr(struct nbpcb
*nbp
, struct lwp
*l
)
130 nb_upcall(struct socket
*so
, void *arg
, int events
, int waitflag
)
132 struct nbpcb
*nbp
= (void *)arg
;
134 if (arg
== NULL
|| nbp
->nbp_selectid
== NULL
)
136 wakeup(nbp
->nbp_selectid
);
140 nb_sethdr(struct mbuf
*m
, u_int8_t type
, u_int32_t len
)
142 u_int32_t
*p
= mtod(m
, u_int32_t
*);
144 *p
= htonl((len
& 0x1FFFF) | (type
<< 24));
149 nb_put_name(struct mbchain
*mbp
, struct sockaddr_nb
*snb
)
157 NBDEBUG(("[%s]\n", cp
));
160 error
= mb_put_mem(mbp
, cp
, seglen
, MB_MSYSTEM
);
171 nb_connect_in(struct nbpcb
*nbp
, struct sockaddr_in
*to
, struct lwp
*l
)
177 error
= socreate(AF_INET
, &so
, SOCK_STREAM
, IPPROTO_TCP
, l
, NULL
);
182 so
->so_upcallarg
= (void *)nbp
;
183 so
->so_upcall
= nb_upcall
;
184 so
->so_rcv
.sb_flags
|= SB_UPCALL
;
185 so
->so_rcv
.sb_flags
&= ~SB_NOINTR
;
186 so
->so_snd
.sb_flags
&= ~SB_NOINTR
;
187 so
->so_rcv
.sb_timeo
= NB_SNDTIMEO
;
188 so
->so_snd
.sb_timeo
= NB_RCVTIMEO
;
189 error
= soreserve(so
, nb_tcpsndbuf
, nb_tcprcvbuf
);
193 nb_setsockopt_int(so
, SOL_SOCKET
, SO_KEEPALIVE
, 1);
194 nb_setsockopt_int(so
, IPPROTO_TCP
, TCP_NODELAY
, 1);
195 m
= m_get(M_WAIT
, MT_SONAME
);
196 *mtod(m
, struct sockaddr
*) = *(struct sockaddr
*)to
;
197 m
->m_len
= sizeof(struct sockaddr
);
199 error
= soconnect(so
, m
, l
);
205 while ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0) {
206 sowait(so
, false, 2 * hz
);
207 if ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0 &&
208 (error
= nb_intr(nbp
, l
)) != 0) {
209 so
->so_state
&= ~SS_ISCONNECTING
;
215 error
= so
->so_error
;
223 smb_nbst_disconnect(nbp
->nbp_vc
, l
);
228 nbssn_rq_request(struct nbpcb
*nbp
, struct lwp
*l
)
230 struct mbchain mb
, *mbp
= &mb
;
231 struct mdchain md
, *mdp
= &md
;
233 struct sockaddr_in sin
;
238 error
= mb_init(mbp
);
241 mb_put_uint32le(mbp
, 0);
242 (void) nb_put_name(mbp
, nbp
->nbp_paddr
);
243 (void) nb_put_name(mbp
, nbp
->nbp_laddr
);
244 nb_sethdr(mbp
->mb_top
, NB_SSN_REQUEST
, mb_fixhdr(mbp
) - 4);
245 error
= nb_sosend(nbp
->nbp_tso
, mbp
->mb_top
, 0, l
);
247 nbp
->nbp_state
= NBST_RQSENT
;
253 error
= nbssn_rselect(nbp
, &nb_timo
, POLLIN
, l
);
254 if (error
== EWOULDBLOCK
) { /* Timeout */
255 NBDEBUG(("initial request timeout\n"));
258 if (error
) /* restart or interrupt */
260 error
= nbssn_recv(nbp
, &m0
, &rplen
, &rpcode
, l
);
262 NBDEBUG(("recv() error %d\n", error
));
266 * Process NETBIOS reply
272 if (rpcode
== NB_SSN_POSRESP
) {
273 nbp
->nbp_state
= NBST_SESSION
;
274 nbp
->nbp_flags
|= NBF_CONNECTED
;
277 if (rpcode
!= NB_SSN_RTGRESP
) {
278 error
= ECONNABORTED
;
282 error
= ECONNABORTED
;
285 md_get_mem(mdp
, (void *)&sin
.sin_addr
, 4, MB_MSYSTEM
);
286 md_get_uint16(mdp
, &port
);
288 nbp
->nbp_state
= NBST_RETARGET
;
289 smb_nbst_disconnect(nbp
->nbp_vc
, l
);
290 error
= nb_connect_in(nbp
, &sin
, l
);
292 error
= nbssn_rq_request(nbp
, l
);
294 smb_nbst_disconnect(nbp
->nbp_vc
, l
);
304 nbssn_recvhdr(struct nbpcb
*nbp
, int *lenp
,
305 u_int8_t
*rpcodep
, int flags
, struct lwp
*l
)
307 struct socket
*so
= nbp
->nbp_tso
;
313 aio
.iov_base
= (void *)&len
;
314 aio
.iov_len
= sizeof(len
);
317 auio
.uio_rw
= UIO_READ
;
319 auio
.uio_resid
= sizeof(len
);
320 UIO_SETUP_SYSSPACE(&auio
);
321 error
= (*so
->so_receive
)(so
, NULL
, &auio
, NULL
, NULL
, &flags
);
324 if (auio
.uio_resid
> 0) {
325 SMBSDEBUG(("short reply\n"));
329 *rpcodep
= (len
>> 24) & 0xFF;
331 if (len
> SMB_MAXPKTLEN
) {
332 SMBERROR(("packet too long (%d)\n", len
));
340 nbssn_recv(struct nbpcb
*nbp
, struct mbuf
**mpp
, int *lenp
,
341 u_int8_t
*rpcodep
, struct lwp
*l
)
343 struct socket
*so
= nbp
->nbp_tso
;
345 struct mbuf
*m
, *tm
, *im
;
350 len
= 0; /* XXX gcc */
351 rpcode
= 0; /* XXX gcc */
361 * Poll for a response header.
362 * If we don't have one waiting, return.
364 error
= nbssn_recvhdr(nbp
, &len
, &rpcode
, MSG_DONTWAIT
, l
);
366 (SS_ISDISCONNECTING
| SS_ISDISCONNECTED
| SS_CANTRCVMORE
)) {
367 nbp
->nbp_state
= NBST_CLOSED
;
368 NBDEBUG(("session closed by peer\n"));
373 if (len
== 0 && nbp
->nbp_state
!= NBST_SESSION
)
375 /* no data, try again */
376 if (rpcode
== NB_SSN_KEEPALIVE
)
380 * Loop, blocking, for data following the response header.
382 * Note that we can't simply block here with MSG_WAITALL for the
383 * entire response size, as it may be larger than the TCP
384 * slow-start window that the sender employs. This will result
385 * in the sender stalling until the delayed ACK is sent, then
386 * resuming slow-start, resulting in very poor performance.
388 * Instead, we never request more than NB_SORECEIVE_CHUNK
389 * bytes at a time, resulting in an ack being pushed by
390 * the TCP code at the completion of each call.
395 rcvflg
= MSG_WAITALL
;
396 memset(&auio
, 0, sizeof(auio
));
397 auio
.uio_resid
= min(resid
, NB_SORECEIVE_CHUNK
);
398 /* not need to setup uio_vmspace */
399 resid
-= auio
.uio_resid
;
401 * Spin until we have collected everything in
405 rcvflg
= MSG_WAITALL
;
406 error
= (*so
->so_receive
)(so
, NULL
, &auio
, &tm
,
408 } while (error
== EWOULDBLOCK
|| error
== EINTR
||
412 /* short return guarantees unhappiness */
413 if (auio
.uio_resid
> 0) {
414 SMBERROR(("packet is shorter than expected\n"));
418 /* append received chunk to previous chunk(s) */
423 * Just glue the new chain on the end.
424 * Consumer will pullup as required.
426 for (im
= m
; im
->m_next
!= NULL
; im
= im
->m_next
)
431 /* got a session/message packet? */
432 if (nbp
->nbp_state
== NBST_SESSION
&&
433 rpcode
== NB_SSN_MESSAGE
)
435 /* drop packet and try for another */
436 NBDEBUG(("non-session packet %x\n", rpcode
));
459 * SMB transport interface
462 smb_nbst_create(struct smb_vc
*vcp
, struct lwp
*l
)
466 nbp
= malloc(sizeof *nbp
, M_NBDATA
, M_WAITOK
|M_ZERO
);
467 nbp
->nbp_state
= NBST_CLOSED
;
474 smb_nbst_done(struct smb_vc
*vcp
, struct lwp
*l
)
476 struct nbpcb
*nbp
= vcp
->vc_tdata
;
480 smb_nbst_disconnect(vcp
, l
);
482 free(nbp
->nbp_laddr
, M_SONAME
);
484 free(nbp
->nbp_paddr
, M_SONAME
);
490 smb_nbst_bind(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct lwp
*l
)
492 struct nbpcb
*nbp
= vcp
->vc_tdata
;
493 struct sockaddr_nb
*snb
;
499 if (nbp
->nbp_flags
& NBF_LOCADDR
)
502 * It is possible to create NETBIOS name in the kernel,
503 * but nothing prevents us to do it in the user space.
508 if (slen
< NB_MINSALEN
)
510 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
, 1);
515 nbp
->nbp_laddr
= snb
;
516 nbp
->nbp_flags
|= NBF_LOCADDR
;
523 smb_nbst_connect(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct lwp
*l
)
525 struct nbpcb
*nbp
= vcp
->vc_tdata
;
526 struct sockaddr_in sin
;
527 struct sockaddr_nb
*snb
;
531 if (nbp
->nbp_tso
!= NULL
)
533 if (nbp
->nbp_laddr
== NULL
)
536 if (slen
< NB_MINSALEN
)
538 if (nbp
->nbp_paddr
) {
539 free(nbp
->nbp_paddr
, M_SONAME
);
540 nbp
->nbp_paddr
= NULL
;
542 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
, 1);
545 nbp
->nbp_paddr
= snb
;
546 sin
= snb
->snb_addrin
;
547 error
= nb_connect_in(nbp
, &sin
, l
);
550 error
= nbssn_rq_request(nbp
, l
);
552 smb_nbst_disconnect(vcp
, l
);
557 smb_nbst_disconnect(struct smb_vc
*vcp
, struct lwp
*l
)
559 struct nbpcb
*nbp
= vcp
->vc_tdata
;
562 if (nbp
== NULL
|| nbp
->nbp_tso
== NULL
)
564 if ((so
= nbp
->nbp_tso
) != NULL
) {
565 nbp
->nbp_flags
&= ~NBF_CONNECTED
;
566 nbp
->nbp_tso
= (struct socket
*)NULL
;
572 if (nbp
->nbp_state
!= NBST_RETARGET
) {
573 nbp
->nbp_state
= NBST_CLOSED
;
579 smb_nbst_send(struct smb_vc
*vcp
, struct mbuf
*m0
, struct lwp
*l
)
581 struct nbpcb
*nbp
= vcp
->vc_tdata
;
584 if (nbp
->nbp_state
!= NBST_SESSION
) {
588 M_PREPEND(m0
, 4, M_WAITOK
);
591 nb_sethdr(m0
, NB_SSN_MESSAGE
, m_fixhdr(m0
) - 4);
592 error
= nb_sosend(nbp
->nbp_tso
, m0
, 0, l
);
602 smb_nbst_recv(struct smb_vc
*vcp
, struct mbuf
**mpp
, struct lwp
*l
)
604 struct nbpcb
*nbp
= vcp
->vc_tdata
;
608 nbp
->nbp_flags
|= NBF_RECVLOCK
;
609 error
= nbssn_recv(nbp
, mpp
, &rplen
, &rpcode
, l
);
610 nbp
->nbp_flags
&= ~NBF_RECVLOCK
;
615 smb_nbst_timo(struct smb_vc
*vcp
)
622 smb_nbst_intr(struct smb_vc
*vcp
)
624 struct nbpcb
*nbp
= vcp
->vc_tdata
;
627 if (nbp
== NULL
|| (so
= nbp
->nbp_tso
) == NULL
)
637 smb_nbst_getparam(struct smb_vc
*vcp
, int param
, void *data
)
642 *(int*)data
= nb_tcpsndbuf
;
645 *(int*)data
= nb_tcprcvbuf
;
648 tvp
= (struct timeval
*)data
;
649 tvp
->tv_sec
= nb_timo
.tv_sec
;
650 tvp
->tv_usec
= nb_timo
.tv_nsec
/ 1000;
659 smb_nbst_setparam(struct smb_vc
*vcp
, int param
, void *data
)
661 struct nbpcb
*nbp
= vcp
->vc_tdata
;
665 nbp
->nbp_selectid
= data
;
674 * Check for fatal errors
677 smb_nbst_fatal(struct smb_vc
*vcp
, int error
)
689 struct smb_tran_desc smb_tran_nbtcp_desc
= {
691 smb_nbst_create
, smb_nbst_done
,
692 smb_nbst_bind
, smb_nbst_connect
, smb_nbst_disconnect
,
693 smb_nbst_send
, smb_nbst_recv
,
694 smb_nbst_timo
, smb_nbst_intr
,
695 smb_nbst_getparam
, smb_nbst_setparam
,