2 * Copyright (c) 2000-2001 Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
32 * $FreeBSD: src/sys/netsmb/smb_trantcp.c,v 1.3.2.1 2001/05/22 08:32:34 bp Exp $
33 * $DragonFly: src/sys/netproto/smb/smb_trantcp.c,v 1.21 2008/01/05 14:02:40 swildner Exp $
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
41 #include <sys/protosw.h>
42 #include <sys/resourcevar.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/socketops.h>
48 #include <sys/fcntl.h>
49 #include <sys/sysctl.h>
50 #include <sys/thread2.h>
53 #include <net/route.h>
55 #include <netinet/in.h>
56 #include <netinet/tcp.h>
58 #include <sys/mchain.h>
65 #include "smb_trantcp.h"
68 #define M_NBDATA M_PCB
70 static int smb_tcpsndbuf
= 10 * 1024;
71 static int smb_tcprcvbuf
= 10 * 1024;
73 SYSCTL_DECL(_net_smb
);
74 SYSCTL_INT(_net_smb
, OID_AUTO
, tcpsndbuf
, CTLFLAG_RW
, &smb_tcpsndbuf
, 0, "");
75 SYSCTL_INT(_net_smb
, OID_AUTO
, tcprcvbuf
, CTLFLAG_RW
, &smb_tcprcvbuf
, 0, "");
77 #define nb_sosend(so,m,flags,p) \
78 so_pru_sosend(so, NULL, NULL, m, NULL, flags, td)
80 static int nbssn_recv(struct nbpcb
*nbp
, struct mbuf
**mpp
, int *lenp
,
81 u_int8_t
*rpcodep
, struct thread
*td
);
82 static int smb_nbst_disconnect(struct smb_vc
*vcp
, struct thread
*td
);
85 nb_setsockopt_int(struct socket
*so
, int level
, int name
, int val
)
89 bzero(&sopt
, sizeof(sopt
));
90 sopt
.sopt_level
= level
;
91 sopt
.sopt_name
= name
;
93 sopt
.sopt_valsize
= sizeof(val
);
94 return sosetopt(so
, &sopt
);
98 nb_poll(struct nbpcb
*nbp
, int events
, struct thread
*td
)
100 return so_pru_sopoll(nbp
->nbp_tso
, events
, NULL
);
104 nbssn_rselect(struct nbpcb
*nbp
, struct timeval
*tv
, int events
, struct thread
*td
)
106 struct lwp
*lp
= td
->td_lwp
;
107 struct timeval atv
, rtv
, ttv
;
112 if (itimerfix(&atv
)) {
116 getmicrouptime(&rtv
);
117 timevaladd(&atv
, &rtv
);
122 lp
->lwp_flag
|= LWP_SELECT
;
123 error
= nb_poll(nbp
, events
, td
);
129 getmicrouptime(&rtv
);
130 if (timevalcmp(&rtv
, &atv
, >=))
133 timevalsub(&ttv
, &rtv
);
134 timo
= tvtohz_high(&ttv
);
137 if ((lp
->lwp_flag
& LWP_SELECT
) == 0) {
141 lp
->lwp_flag
&= ~LWP_SELECT
;
142 error
= tsleep((caddr_t
)&selwait
, 0, "nbsel", timo
);
145 lp
->lwp_flag
&= ~LWP_SELECT
;
146 if (error
== ERESTART
)
152 nb_intr(struct nbpcb
*nbp
, struct thread
*td
)
158 nb_upcall(struct socket
*so
, void *arg
, int waitflag
)
160 struct nbpcb
*nbp
= arg
;
162 if (arg
== NULL
|| nbp
->nbp_selectid
== NULL
)
164 wakeup(nbp
->nbp_selectid
);
168 nb_sethdr(struct mbuf
*m
, u_int8_t type
, u_int32_t len
)
170 u_int32_t
*p
= mtod(m
, u_int32_t
*);
172 *p
= htonl((len
& 0x1FFFF) | (type
<< 24));
177 nb_put_name(struct mbchain
*mbp
, struct sockaddr_nb
*snb
)
185 NBDEBUG("[%s]\n", cp
);
188 error
= mb_put_mem(mbp
, cp
, seglen
, MB_MSYSTEM
);
199 nb_connect_in(struct nbpcb
*nbp
, struct sockaddr_in
*to
, struct thread
*td
)
204 error
= socreate(AF_INET
, &so
, SOCK_STREAM
, IPPROTO_TCP
, td
);
208 so
->so_upcallarg
= (caddr_t
)nbp
;
209 so
->so_upcall
= nb_upcall
;
210 so
->so_rcv
.ssb_flags
|= SSB_UPCALL
;
211 so
->so_rcv
.ssb_timeo
= (5 * hz
);
212 so
->so_snd
.ssb_timeo
= (5 * hz
);
213 error
= soreserve(so
, nbp
->nbp_sndbuf
, nbp
->nbp_rcvbuf
,
214 &td
->td_proc
->p_rlimit
[RLIMIT_SBSIZE
]);
217 nb_setsockopt_int(so
, SOL_SOCKET
, SO_KEEPALIVE
, 1);
218 nb_setsockopt_int(so
, IPPROTO_TCP
, TCP_NODELAY
, 1);
219 so
->so_rcv
.ssb_flags
&= ~SSB_NOINTR
;
220 so
->so_snd
.ssb_flags
&= ~SSB_NOINTR
;
221 error
= soconnect(so
, (struct sockaddr
*)to
, td
);
224 * If signals are allowed nbssn_recv() can wind up in a hard loop
227 so
->so_rcv
.ssb_flags
|= SSB_NOINTR
;
228 so
->so_snd
.ssb_flags
|= SSB_NOINTR
;
232 while ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0) {
233 tsleep(&so
->so_timeo
, 0, "nbcon", 2 * hz
);
234 if ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0 &&
235 (error
= nb_intr(nbp
, td
)) != 0) {
236 so
->so_state
&= ~SS_ISCONNECTING
;
242 error
= so
->so_error
;
250 smb_nbst_disconnect(nbp
->nbp_vc
, td
);
255 nbssn_rq_request(struct nbpcb
*nbp
, struct thread
*td
)
257 struct mbchain mb
, *mbp
= &mb
;
258 struct mdchain md
, *mdp
= &md
;
261 struct sockaddr_in sin
;
266 error
= mb_init(mbp
);
269 mb_put_uint32le(mbp
, 0);
270 nb_put_name(mbp
, nbp
->nbp_paddr
);
271 nb_put_name(mbp
, nbp
->nbp_laddr
);
272 nb_sethdr(mbp
->mb_top
, NB_SSN_REQUEST
, mb_fixhdr(mbp
) - 4);
273 error
= nb_sosend(nbp
->nbp_tso
, mbp
->mb_top
, 0, td
);
275 nbp
->nbp_state
= NBST_RQSENT
;
281 TIMESPEC_TO_TIMEVAL(&tv
, &nbp
->nbp_timo
);
282 error
= nbssn_rselect(nbp
, &tv
, POLLIN
, td
);
283 if (error
== EWOULDBLOCK
) { /* Timeout */
284 NBDEBUG("initial request timeout\n");
287 if (error
) /* restart or interrupt */
289 error
= nbssn_recv(nbp
, &m0
, &rplen
, &rpcode
, td
);
291 NBDEBUG("recv() error %d\n", error
);
295 * Process NETBIOS reply
301 if (rpcode
== NB_SSN_POSRESP
) {
302 nbp
->nbp_state
= NBST_SESSION
;
303 nbp
->nbp_flags
|= NBF_CONNECTED
;
306 if (rpcode
!= NB_SSN_RTGRESP
) {
307 error
= ECONNABORTED
;
311 error
= ECONNABORTED
;
314 md_get_mem(mdp
, (caddr_t
)&sin
.sin_addr
, 4, MB_MSYSTEM
);
315 md_get_uint16(mdp
, &port
);
317 nbp
->nbp_state
= NBST_RETARGET
;
318 smb_nbst_disconnect(nbp
->nbp_vc
, td
);
319 error
= nb_connect_in(nbp
, &sin
, td
);
321 error
= nbssn_rq_request(nbp
, td
);
323 smb_nbst_disconnect(nbp
->nbp_vc
, td
);
333 nbssn_recvhdr(struct nbpcb
*nbp
, int *lenp
,
334 u_int8_t
*rpcodep
, int flags
, struct thread
*td
)
336 struct socket
*so
= nbp
->nbp_tso
;
342 aio
.iov_base
= (caddr_t
)&len
;
343 aio
.iov_len
= sizeof(len
);
346 auio
.uio_segflg
= UIO_SYSSPACE
;
347 auio
.uio_rw
= UIO_READ
;
349 auio
.uio_resid
= sizeof(len
);
351 error
= so_pru_soreceive(so
, NULL
, &auio
, NULL
, NULL
, &flags
);
354 if (auio
.uio_resid
> 0) {
355 SMBSDEBUG("short reply\n");
359 *rpcodep
= (len
>> 24) & 0xFF;
361 if (len
> SMB_MAXPKTLEN
) {
362 SMBERROR("packet too long (%d)\n", len
);
370 nbssn_recv(struct nbpcb
*nbp
, struct mbuf
**mpp
, int *lenp
,
371 u_int8_t
*rpcodep
, struct thread
*td
)
373 struct socket
*so
= nbp
->nbp_tso
;
387 error
= nbssn_recvhdr(nbp
, &savelen
, &rpcode
, MSG_DONTWAIT
, td
);
389 (SS_ISDISCONNECTING
| SS_ISDISCONNECTED
| SS_CANTRCVMORE
)) {
390 nbp
->nbp_state
= NBST_CLOSED
;
391 NBDEBUG("session closed by peer\n");
397 if (savelen
== 0 && nbp
->nbp_state
!= NBST_SESSION
)
399 if (rpcode
== NB_SSN_KEEPALIVE
)
402 sbinit(&sio
, savelen
);
403 rcvflg
= MSG_WAITALL
;
404 error
= so_pru_soreceive(so
, NULL
, NULL
, &sio
,
406 } while (error
== EWOULDBLOCK
|| error
== EINTR
||
410 if (sio
.sb_cc
!= savelen
) {
411 SMBERROR("packet is shorter than expected\n");
416 if (nbp
->nbp_state
== NBST_SESSION
&& rpcode
== NB_SSN_MESSAGE
)
418 NBDEBUG("non-session packet %x\n", rpcode
);
435 * SMB transport interface
438 smb_nbst_create(struct smb_vc
*vcp
, struct thread
*td
)
442 MALLOC(nbp
, struct nbpcb
*, sizeof *nbp
, M_NBDATA
, M_WAITOK
| M_ZERO
);
443 nbp
->nbp_timo
.tv_sec
= 15; /* XXX: sysctl ? */
444 nbp
->nbp_state
= NBST_CLOSED
;
446 nbp
->nbp_sndbuf
= smb_tcpsndbuf
;
447 nbp
->nbp_rcvbuf
= smb_tcprcvbuf
;
453 smb_nbst_done(struct smb_vc
*vcp
, struct thread
*td
)
455 struct nbpcb
*nbp
= vcp
->vc_tdata
;
459 smb_nbst_disconnect(vcp
, td
);
461 kfree(nbp
->nbp_laddr
, M_SONAME
);
463 kfree(nbp
->nbp_paddr
, M_SONAME
);
464 kfree(nbp
, M_NBDATA
);
469 smb_nbst_bind(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct thread
*td
)
471 struct nbpcb
*nbp
= vcp
->vc_tdata
;
472 struct sockaddr_nb
*snb
;
478 if (nbp
->nbp_flags
& NBF_LOCADDR
)
481 * It is possible to create NETBIOS name in the kernel,
482 * but nothing prevents us to do it in the user space.
487 if (slen
< NB_MINSALEN
)
489 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
);
494 nbp
->nbp_laddr
= snb
;
495 nbp
->nbp_flags
|= NBF_LOCADDR
;
502 smb_nbst_connect(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct thread
*td
)
504 struct nbpcb
*nbp
= vcp
->vc_tdata
;
505 struct sockaddr_in sin
;
506 struct sockaddr_nb
*snb
;
507 struct timespec ts1
, ts2
;
511 if (nbp
->nbp_tso
!= NULL
)
513 if (nbp
->nbp_laddr
== NULL
)
516 if (slen
< NB_MINSALEN
)
518 if (nbp
->nbp_paddr
) {
519 kfree(nbp
->nbp_paddr
, M_SONAME
);
520 nbp
->nbp_paddr
= NULL
;
522 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
);
525 nbp
->nbp_paddr
= snb
;
526 sin
= snb
->snb_addrin
;
528 error
= nb_connect_in(nbp
, &sin
, td
);
532 timespecsub(&ts2
, &ts1
);
533 if (ts2
.tv_sec
== 0 && ts2
.tv_sec
== 0)
536 timespecadd(&nbp
->nbp_timo
, &ts2
);
537 timespecadd(&nbp
->nbp_timo
, &ts2
);
538 timespecadd(&nbp
->nbp_timo
, &ts2
); /* * 4 */
539 error
= nbssn_rq_request(nbp
, td
);
541 smb_nbst_disconnect(vcp
, td
);
546 smb_nbst_disconnect(struct smb_vc
*vcp
, struct thread
*td
)
548 struct nbpcb
*nbp
= vcp
->vc_tdata
;
551 if (nbp
== NULL
|| nbp
->nbp_tso
== NULL
)
553 if ((so
= nbp
->nbp_tso
) != NULL
) {
554 nbp
->nbp_flags
&= ~NBF_CONNECTED
;
556 soshutdown(so
, SHUT_RDWR
);
557 soclose(so
, FNONBLOCK
);
559 if (nbp
->nbp_state
!= NBST_RETARGET
) {
560 nbp
->nbp_state
= NBST_CLOSED
;
566 smb_nbst_send(struct smb_vc
*vcp
, struct mbuf
*m0
, struct thread
*td
)
568 struct nbpcb
*nbp
= vcp
->vc_tdata
;
571 if (nbp
->nbp_state
!= NBST_SESSION
) {
575 M_PREPEND(m0
, 4, MB_TRYWAIT
);
578 nb_sethdr(m0
, NB_SSN_MESSAGE
, m_fixhdr(m0
) - 4);
579 error
= nb_sosend(nbp
->nbp_tso
, m0
, 0, td
);
589 smb_nbst_recv(struct smb_vc
*vcp
, struct mbuf
**mpp
, struct thread
*td
)
591 struct nbpcb
*nbp
= vcp
->vc_tdata
;
595 nbp
->nbp_flags
|= NBF_RECVLOCK
;
596 error
= nbssn_recv(nbp
, mpp
, &rplen
, &rpcode
, td
);
597 nbp
->nbp_flags
&= ~NBF_RECVLOCK
;
602 smb_nbst_timo(struct smb_vc
*vcp
)
608 smb_nbst_intr(struct smb_vc
*vcp
)
610 struct nbpcb
*nbp
= vcp
->vc_tdata
;
612 if (nbp
== NULL
|| nbp
->nbp_tso
== NULL
)
614 sorwakeup(nbp
->nbp_tso
);
615 sowwakeup(nbp
->nbp_tso
);
619 smb_nbst_getparam(struct smb_vc
*vcp
, int param
, void *data
)
621 struct nbpcb
*nbp
= vcp
->vc_tdata
;
625 *(int*)data
= nbp
->nbp_sndbuf
;
628 *(int*)data
= nbp
->nbp_rcvbuf
;
631 *(struct timespec
*)data
= nbp
->nbp_timo
;
640 smb_nbst_setparam(struct smb_vc
*vcp
, int param
, void *data
)
642 struct nbpcb
*nbp
= vcp
->vc_tdata
;
646 nbp
->nbp_selectid
= data
;
655 * Check for fatal errors
658 smb_nbst_fatal(struct smb_vc
*vcp
, int error
)
670 struct smb_tran_desc smb_tran_nbtcp_desc
= {
672 smb_nbst_create
, smb_nbst_done
,
673 smb_nbst_bind
, smb_nbst_connect
, smb_nbst_disconnect
,
674 smb_nbst_send
, smb_nbst_recv
,
675 smb_nbst_timo
, smb_nbst_intr
,
676 smb_nbst_getparam
, smb_nbst_setparam
,