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 $
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
40 #include <sys/protosw.h>
41 #include <sys/resourcevar.h>
42 #include <sys/socket.h>
43 #include <sys/socketvar.h>
44 #include <sys/socketvar2.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_intr(struct nbpcb
*nbp
, struct thread
*td
)
104 nb_upcall(struct socket
*so
, void *arg
, int waitflag
)
106 struct nbpcb
*nbp
= arg
;
108 if (arg
== NULL
|| nbp
->nbp_selectid
== NULL
)
110 wakeup(nbp
->nbp_selectid
);
114 nb_sethdr(struct mbuf
*m
, u_int8_t type
, u_int32_t len
)
116 u_int32_t
*p
= mtod(m
, u_int32_t
*);
118 *p
= htonl((len
& 0x1FFFF) | (type
<< 24));
123 nb_put_name(struct mbchain
*mbp
, struct sockaddr_nb
*snb
)
131 NBDEBUG("[%s]\n", cp
);
134 error
= mb_put_mem(mbp
, cp
, seglen
, MB_MSYSTEM
);
145 nb_connect_in(struct nbpcb
*nbp
, struct sockaddr_in
*to
, struct thread
*td
)
150 error
= socreate(AF_INET
, &so
, SOCK_STREAM
, IPPROTO_TCP
, td
);
154 so
->so_upcallarg
= (caddr_t
)nbp
;
155 so
->so_upcall
= nb_upcall
;
156 atomic_set_int(&so
->so_rcv
.ssb_flags
, SSB_UPCALL
);
157 so
->so_rcv
.ssb_timeo
= (5 * hz
);
158 so
->so_snd
.ssb_timeo
= (5 * hz
);
159 error
= soreserve(so
, nbp
->nbp_sndbuf
, nbp
->nbp_rcvbuf
,
160 &td
->td_proc
->p_rlimit
[RLIMIT_SBSIZE
]);
163 nb_setsockopt_int(so
, SOL_SOCKET
, SO_KEEPALIVE
, 1);
164 nb_setsockopt_int(so
, IPPROTO_TCP
, TCP_NODELAY
, 1);
165 atomic_clear_int(&so
->so_rcv
.ssb_flags
, SSB_NOINTR
);
166 atomic_clear_int(&so
->so_snd
.ssb_flags
, SSB_NOINTR
);
167 error
= soconnect(so
, (struct sockaddr
*)to
, td
, TRUE
);
170 * If signals are allowed nbssn_recv() can wind up in a hard loop
173 atomic_set_int(&so
->so_rcv
.ssb_flags
, SSB_NOINTR
);
174 atomic_set_int(&so
->so_snd
.ssb_flags
, SSB_NOINTR
);
178 while ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0) {
179 tsleep(&so
->so_timeo
, 0, "nbcon", 2 * hz
);
180 if ((so
->so_state
& SS_ISCONNECTING
) && so
->so_error
== 0 &&
181 (error
= nb_intr(nbp
, td
)) != 0) {
182 soclrstate(so
, SS_ISCONNECTING
);
188 error
= so
->so_error
;
196 smb_nbst_disconnect(nbp
->nbp_vc
, td
);
201 nbssn_rq_request(struct nbpcb
*nbp
, struct thread
*td
)
203 struct mbchain mb
, *mbp
= &mb
;
204 struct mdchain md
, *mdp
= &md
;
206 struct sockaddr_in sin
;
209 int error
, rplen
, res
;
211 error
= mb_init(mbp
);
214 mb_put_uint32le(mbp
, 0);
215 nb_put_name(mbp
, nbp
->nbp_paddr
);
216 nb_put_name(mbp
, nbp
->nbp_laddr
);
217 nb_sethdr(mbp
->mb_top
, NB_SSN_REQUEST
, mb_fixhdr(mbp
) - 4);
218 error
= nb_sosend(nbp
->nbp_tso
, mbp
->mb_top
, 0, td
);
220 nbp
->nbp_state
= NBST_RQSENT
;
226 error
= socket_wait(nbp
->nbp_tso
, &nbp
->nbp_timo
, &res
);
227 if (error
== EWOULDBLOCK
) { /* Timeout */
228 NBDEBUG("initial request timeout\n");
231 if (error
) /* restart or interrupt */
233 error
= nbssn_recv(nbp
, &m0
, &rplen
, &rpcode
, td
);
235 NBDEBUG("recv() error %d\n", error
);
239 * Process NETBIOS reply
245 if (rpcode
== NB_SSN_POSRESP
) {
246 nbp
->nbp_state
= NBST_SESSION
;
247 nbp
->nbp_flags
|= NBF_CONNECTED
;
250 if (rpcode
!= NB_SSN_RTGRESP
) {
251 error
= ECONNABORTED
;
255 error
= ECONNABORTED
;
258 md_get_mem(mdp
, (caddr_t
)&sin
.sin_addr
, 4, MB_MSYSTEM
);
259 md_get_uint16(mdp
, &port
);
261 nbp
->nbp_state
= NBST_RETARGET
;
262 smb_nbst_disconnect(nbp
->nbp_vc
, td
);
263 error
= nb_connect_in(nbp
, &sin
, td
);
265 error
= nbssn_rq_request(nbp
, td
);
267 smb_nbst_disconnect(nbp
->nbp_vc
, td
);
277 nbssn_recvhdr(struct nbpcb
*nbp
, int *lenp
,
278 u_int8_t
*rpcodep
, int flags
, struct thread
*td
)
280 struct socket
*so
= nbp
->nbp_tso
;
286 aio
.iov_base
= (caddr_t
)&len
;
287 aio
.iov_len
= sizeof(len
);
290 auio
.uio_segflg
= UIO_SYSSPACE
;
291 auio
.uio_rw
= UIO_READ
;
293 auio
.uio_resid
= sizeof(len
);
295 error
= so_pru_soreceive(so
, NULL
, &auio
, NULL
, NULL
, &flags
);
298 if (auio
.uio_resid
> 0) {
299 SMBSDEBUG("short reply\n");
303 *rpcodep
= (len
>> 24) & 0xFF;
305 if (len
> SMB_MAXPKTLEN
) {
306 SMBERROR("packet too long (%d)\n", len
);
314 nbssn_recv(struct nbpcb
*nbp
, struct mbuf
**mpp
, int *lenp
,
315 u_int8_t
*rpcodep
, struct thread
*td
)
317 struct socket
*so
= nbp
->nbp_tso
;
331 error
= nbssn_recvhdr(nbp
, &savelen
, &rpcode
, MSG_DONTWAIT
, td
);
333 (SS_ISDISCONNECTING
| SS_ISDISCONNECTED
| SS_CANTRCVMORE
)) {
334 nbp
->nbp_state
= NBST_CLOSED
;
335 NBDEBUG("session closed by peer\n");
341 if (savelen
== 0 && nbp
->nbp_state
!= NBST_SESSION
)
343 if (rpcode
== NB_SSN_KEEPALIVE
)
346 sbinit(&sio
, savelen
);
347 rcvflg
= MSG_WAITALL
;
348 error
= so_pru_soreceive(so
, NULL
, NULL
, &sio
,
350 } while (error
== EWOULDBLOCK
|| error
== EINTR
||
354 if (sio
.sb_cc
!= savelen
) {
355 SMBERROR("packet is shorter than expected\n");
360 if (nbp
->nbp_state
== NBST_SESSION
&& rpcode
== NB_SSN_MESSAGE
)
362 NBDEBUG("non-session packet %x\n", rpcode
);
379 * SMB transport interface
382 smb_nbst_create(struct smb_vc
*vcp
, struct thread
*td
)
386 nbp
= kmalloc(sizeof *nbp
, M_NBDATA
, M_WAITOK
| M_ZERO
);
387 nbp
->nbp_timo
.tv_sec
= 15; /* XXX: sysctl ? */
388 nbp
->nbp_state
= NBST_CLOSED
;
390 nbp
->nbp_sndbuf
= smb_tcpsndbuf
;
391 nbp
->nbp_rcvbuf
= smb_tcprcvbuf
;
397 smb_nbst_done(struct smb_vc
*vcp
, struct thread
*td
)
399 struct nbpcb
*nbp
= vcp
->vc_tdata
;
403 smb_nbst_disconnect(vcp
, td
);
405 kfree(nbp
->nbp_laddr
, M_SONAME
);
407 kfree(nbp
->nbp_paddr
, M_SONAME
);
408 kfree(nbp
, M_NBDATA
);
413 smb_nbst_bind(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct thread
*td
)
415 struct nbpcb
*nbp
= vcp
->vc_tdata
;
416 struct sockaddr_nb
*snb
;
422 if (nbp
->nbp_flags
& NBF_LOCADDR
)
425 * It is possible to create NETBIOS name in the kernel,
426 * but nothing prevents us to do it in the user space.
431 if (slen
< NB_MINSALEN
)
433 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
);
438 nbp
->nbp_laddr
= snb
;
439 nbp
->nbp_flags
|= NBF_LOCADDR
;
446 smb_nbst_connect(struct smb_vc
*vcp
, struct sockaddr
*sap
, struct thread
*td
)
448 struct nbpcb
*nbp
= vcp
->vc_tdata
;
449 struct sockaddr_in sin
;
450 struct sockaddr_nb
*snb
;
451 struct timespec ts1
, ts2
;
455 if (nbp
->nbp_tso
!= NULL
)
457 if (nbp
->nbp_laddr
== NULL
)
460 if (slen
< NB_MINSALEN
)
462 if (nbp
->nbp_paddr
) {
463 kfree(nbp
->nbp_paddr
, M_SONAME
);
464 nbp
->nbp_paddr
= NULL
;
466 snb
= (struct sockaddr_nb
*)dup_sockaddr(sap
);
469 nbp
->nbp_paddr
= snb
;
470 sin
= snb
->snb_addrin
;
472 error
= nb_connect_in(nbp
, &sin
, td
);
476 timespecsub(&ts2
, &ts1
);
477 if (ts2
.tv_sec
== 0) {
482 timespecadd(&nbp
->nbp_timo
, &ts2
);
483 timespecadd(&nbp
->nbp_timo
, &ts2
);
484 timespecadd(&nbp
->nbp_timo
, &ts2
); /* * 4 */
485 error
= nbssn_rq_request(nbp
, td
);
487 smb_nbst_disconnect(vcp
, td
);
492 smb_nbst_disconnect(struct smb_vc
*vcp
, struct thread
*td
)
494 struct nbpcb
*nbp
= vcp
->vc_tdata
;
497 if (nbp
== NULL
|| nbp
->nbp_tso
== NULL
)
499 if ((so
= nbp
->nbp_tso
) != NULL
) {
500 nbp
->nbp_flags
&= ~NBF_CONNECTED
;
502 soshutdown(so
, SHUT_RDWR
);
503 soclose(so
, FNONBLOCK
);
505 if (nbp
->nbp_state
!= NBST_RETARGET
) {
506 nbp
->nbp_state
= NBST_CLOSED
;
512 smb_nbst_send(struct smb_vc
*vcp
, struct mbuf
*m0
, struct thread
*td
)
514 struct nbpcb
*nbp
= vcp
->vc_tdata
;
517 if (nbp
->nbp_state
!= NBST_SESSION
) {
521 M_PREPEND(m0
, 4, M_WAITOK
);
524 nb_sethdr(m0
, NB_SSN_MESSAGE
, m_fixhdr(m0
) - 4);
525 error
= nb_sosend(nbp
->nbp_tso
, m0
, 0, td
);
535 smb_nbst_recv(struct smb_vc
*vcp
, struct mbuf
**mpp
, struct thread
*td
)
537 struct nbpcb
*nbp
= vcp
->vc_tdata
;
541 nbp
->nbp_flags
|= NBF_RECVLOCK
;
542 error
= nbssn_recv(nbp
, mpp
, &rplen
, &rpcode
, td
);
543 nbp
->nbp_flags
&= ~NBF_RECVLOCK
;
548 smb_nbst_timo(struct smb_vc
*vcp
)
554 smb_nbst_intr(struct smb_vc
*vcp
)
556 struct nbpcb
*nbp
= vcp
->vc_tdata
;
558 if (nbp
== NULL
|| nbp
->nbp_tso
== NULL
)
560 sorwakeup(nbp
->nbp_tso
);
561 sowwakeup(nbp
->nbp_tso
);
565 smb_nbst_getparam(struct smb_vc
*vcp
, int param
, void *data
)
567 struct nbpcb
*nbp
= vcp
->vc_tdata
;
571 *(int*)data
= nbp
->nbp_sndbuf
;
574 *(int*)data
= nbp
->nbp_rcvbuf
;
577 *(struct timespec
*)data
= nbp
->nbp_timo
;
586 smb_nbst_setparam(struct smb_vc
*vcp
, int param
, void *data
)
588 struct nbpcb
*nbp
= vcp
->vc_tdata
;
592 nbp
->nbp_selectid
= data
;
601 * Check for fatal errors
604 smb_nbst_fatal(struct smb_vc
*vcp
, int error
)
616 struct smb_tran_desc smb_tran_nbtcp_desc
= {
618 smb_nbst_create
, smb_nbst_done
,
619 smb_nbst_bind
, smb_nbst_connect
, smb_nbst_disconnect
,
620 smb_nbst_send
, smb_nbst_recv
,
621 smb_nbst_timo
, smb_nbst_intr
,
622 smb_nbst_getparam
, smb_nbst_setparam
,