1139823Simp/*-
275374Sbp * Copyright (c) 2000-2001 Boris Popov
375374Sbp * All rights reserved.
475374Sbp *
575374Sbp * Redistribution and use in source and binary forms, with or without
675374Sbp * modification, are permitted provided that the following conditions
775374Sbp * are met:
875374Sbp * 1. Redistributions of source code must retain the above copyright
975374Sbp *    notice, this list of conditions and the following disclaimer.
1075374Sbp * 2. Redistributions in binary form must reproduce the above copyright
1175374Sbp *    notice, this list of conditions and the following disclaimer in the
1275374Sbp *    documentation and/or other materials provided with the distribution.
1375374Sbp *
1475374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1575374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1675374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1775374Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1875374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1975374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2075374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2175374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2275374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2375374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2475374Sbp * SUCH DAMAGE.
2575374Sbp */
26116189Sobrien
27116189Sobrien#include <sys/cdefs.h>
28116189Sobrien__FBSDID("$FreeBSD$");
29116189Sobrien
3075374Sbp#include <sys/param.h>
3195759Stanimura#include <sys/condvar.h>
3275374Sbp#include <sys/kernel.h>
3395759Stanimura#include <sys/lock.h>
3475374Sbp#include <sys/malloc.h>
3575374Sbp#include <sys/mbuf.h>
3695759Stanimura#include <sys/poll.h>
3775374Sbp#include <sys/proc.h>
3875374Sbp#include <sys/protosw.h>
3995759Stanimura#include <sys/signalvar.h>
4075374Sbp#include <sys/socket.h>
4175374Sbp#include <sys/socketvar.h>
4295759Stanimura#include <sys/sx.h>
4395759Stanimura#include <sys/sysctl.h>
4495759Stanimura#include <sys/systm.h>
4575374Sbp#include <sys/uio.h>
4675374Sbp
4775374Sbp#include <net/if.h>
4875374Sbp#include <net/route.h>
49264425Sdteske#include <net/vnet.h>
5075374Sbp
5175374Sbp#include <netinet/in.h>
5275374Sbp#include <netinet/tcp.h>
5375374Sbp
5475374Sbp#include <sys/mchain.h>
5575374Sbp
5675374Sbp#include <netsmb/netbios.h>
5775374Sbp
5875374Sbp#include <netsmb/smb.h>
5975374Sbp#include <netsmb/smb_conn.h>
6075374Sbp#include <netsmb/smb_tran.h>
6175374Sbp#include <netsmb/smb_trantcp.h>
6275374Sbp#include <netsmb/smb_subr.h>
6375374Sbp
6475374Sbp#define M_NBDATA	M_PCB
6575374Sbp
66103528Sbpstatic int smb_tcpsndbuf = NB_SNDQ - 1;
67103528Sbpstatic int smb_tcprcvbuf = NB_RCVQ - 1;
6875374Sbp
6975374SbpSYSCTL_DECL(_net_smb);
7075374SbpSYSCTL_INT(_net_smb, OID_AUTO, tcpsndbuf, CTLFLAG_RW, &smb_tcpsndbuf, 0, "");
7175374SbpSYSCTL_INT(_net_smb, OID_AUTO, tcprcvbuf, CTLFLAG_RW, &smb_tcprcvbuf, 0, "");
7275374Sbp
73160619Srwatson#define nb_sosend(so,m,flags,td) sosend(so, NULL, 0, m, 0, flags, td)
7475374Sbp
7575374Sbpstatic int  nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
7687192Sbp	u_int8_t *rpcodep, struct thread *td);
7787192Sbpstatic int  smb_nbst_disconnect(struct smb_vc *vcp, struct thread *td);
7875374Sbp
7975374Sbpstatic int
8075374Sbpnb_setsockopt_int(struct socket *so, int level, int name, int val)
8175374Sbp{
8275374Sbp	struct sockopt sopt;
83264425Sdteske	int error;
8475374Sbp
8575374Sbp	bzero(&sopt, sizeof(sopt));
8675374Sbp	sopt.sopt_level = level;
8775374Sbp	sopt.sopt_name = name;
8875374Sbp	sopt.sopt_val = &val;
8975374Sbp	sopt.sopt_valsize = sizeof(val);
90264425Sdteske	CURVNET_SET(so->so_vnet);
91264425Sdteske	error = sosetopt(so, &sopt);
92264425Sdteske	CURVNET_RESTORE();
93264425Sdteske	return error;
9475374Sbp}
9575374Sbp
9675374Sbpstatic int
9775374Sbpnb_intr(struct nbpcb *nbp, struct proc *p)
9875374Sbp{
9975374Sbp	return 0;
10075374Sbp}
10175374Sbp
102193272Sjhbstatic int
10375374Sbpnb_upcall(struct socket *so, void *arg, int waitflag)
10475374Sbp{
10575374Sbp	struct nbpcb *nbp = arg;
10675374Sbp
10775374Sbp	if (arg == NULL || nbp->nbp_selectid == NULL)
108193272Sjhb		return (SU_OK);
10975374Sbp	wakeup(nbp->nbp_selectid);
110193272Sjhb	return (SU_OK);
11175374Sbp}
11275374Sbp
11375374Sbpstatic int
11475374Sbpnb_sethdr(struct mbuf *m, u_int8_t type, u_int32_t len)
11575374Sbp{
11675374Sbp	u_int32_t *p = mtod(m, u_int32_t *);
11775374Sbp
11875374Sbp	*p = htonl((len & 0x1FFFF) | (type << 24));
11975374Sbp	return 0;
12075374Sbp}
12175374Sbp
12275374Sbpstatic int
12375374Sbpnb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb)
12475374Sbp{
12575374Sbp	int error;
12675374Sbp	u_char seglen, *cp;
12775374Sbp
12875374Sbp	cp = snb->snb_name;
12975374Sbp	if (*cp == 0)
13075374Sbp		return EINVAL;
13175374Sbp	NBDEBUG("[%s]\n", cp);
13275374Sbp	for (;;) {
13375374Sbp		seglen = (*cp) + 1;
13475374Sbp		error = mb_put_mem(mbp, cp, seglen, MB_MSYSTEM);
13575374Sbp		if (error)
13675374Sbp			return error;
13775374Sbp		if (seglen == 1)
13875374Sbp			break;
13975374Sbp		cp += seglen;
14075374Sbp	}
14175374Sbp	return 0;
14275374Sbp}
14375374Sbp
14475374Sbpstatic int
14587192Sbpnb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct thread *td)
14675374Sbp{
14775374Sbp	struct socket *so;
14875374Sbp	int error, s;
14975374Sbp
15088739Srwatson	error = socreate(AF_INET, &so, SOCK_STREAM, IPPROTO_TCP,
15191406Sjhb	    td->td_ucred, td);
15275374Sbp	if (error)
15375374Sbp		return error;
15475374Sbp	nbp->nbp_tso = so;
155130653Srwatson	SOCKBUF_LOCK(&so->so_rcv);
156193272Sjhb	soupcall_set(so, SO_RCV, nb_upcall, nbp);
157130653Srwatson	SOCKBUF_UNLOCK(&so->so_rcv);
15875374Sbp	so->so_rcv.sb_timeo = (5 * hz);
15975374Sbp	so->so_snd.sb_timeo = (5 * hz);
16075374Sbp	error = soreserve(so, nbp->nbp_sndbuf, nbp->nbp_rcvbuf);
16175374Sbp	if (error)
16275374Sbp		goto bad;
16375374Sbp	nb_setsockopt_int(so, SOL_SOCKET, SO_KEEPALIVE, 1);
16475374Sbp	nb_setsockopt_int(so, IPPROTO_TCP, TCP_NODELAY, 1);
165130653Srwatson	SOCKBUF_LOCK(&so->so_rcv);
16675374Sbp	so->so_rcv.sb_flags &= ~SB_NOINTR;
167130653Srwatson	SOCKBUF_UNLOCK(&so->so_rcv);
168130653Srwatson	SOCKBUF_LOCK(&so->so_snd);
16975374Sbp	so->so_snd.sb_flags &= ~SB_NOINTR;
170130653Srwatson	SOCKBUF_UNLOCK(&so->so_snd);
17187192Sbp	error = soconnect(so, (struct sockaddr*)to, td);
17275374Sbp	if (error)
17375374Sbp		goto bad;
17475374Sbp	s = splnet();
17575374Sbp	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
17697658Stanimura		tsleep(&so->so_timeo, PSOCK, "nbcon", 2 * hz);
17775374Sbp		if ((so->so_state & SS_ISCONNECTING) && so->so_error == 0 &&
17887192Sbp			(error = nb_intr(nbp, td->td_proc)) != 0) {
17975374Sbp			so->so_state &= ~SS_ISCONNECTING;
18075374Sbp			splx(s);
18175374Sbp			goto bad;
18275374Sbp		}
18375374Sbp	}
18475374Sbp	if (so->so_error) {
18575374Sbp		error = so->so_error;
18675374Sbp		so->so_error = 0;
18775374Sbp		splx(s);
18875374Sbp		goto bad;
18975374Sbp	}
19075374Sbp	splx(s);
19175374Sbp	return 0;
19275374Sbpbad:
19387192Sbp	smb_nbst_disconnect(nbp->nbp_vc, td);
19475374Sbp	return error;
19575374Sbp}
19675374Sbp
19775374Sbpstatic int
19887192Sbpnbssn_rq_request(struct nbpcb *nbp, struct thread *td)
19975374Sbp{
20075374Sbp	struct mbchain mb, *mbp = &mb;
20175374Sbp	struct mdchain md, *mdp = &md;
20275374Sbp	struct mbuf *m0;
20375374Sbp	struct timeval tv;
20475374Sbp	struct sockaddr_in sin;
20575374Sbp	u_short port;
20675374Sbp	u_int8_t rpcode;
20775374Sbp	int error, rplen;
20875374Sbp
20975374Sbp	error = mb_init(mbp);
21075374Sbp	if (error)
21175374Sbp		return error;
21275374Sbp	mb_put_uint32le(mbp, 0);
21375374Sbp	nb_put_name(mbp, nbp->nbp_paddr);
21475374Sbp	nb_put_name(mbp, nbp->nbp_laddr);
21575374Sbp	nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4);
21687192Sbp	error = nb_sosend(nbp->nbp_tso, mbp->mb_top, 0, td);
21775374Sbp	if (!error) {
21875374Sbp		nbp->nbp_state = NBST_RQSENT;
21975374Sbp	}
22075374Sbp	mb_detach(mbp);
22175374Sbp	mb_done(mbp);
22275374Sbp	if (error)
22375374Sbp		return error;
22475374Sbp	TIMESPEC_TO_TIMEVAL(&tv, &nbp->nbp_timo);
225174647Sjeff	error = selsocket(nbp->nbp_tso, POLLIN, &tv, td);
22675374Sbp	if (error == EWOULDBLOCK) {	/* Timeout */
22775374Sbp		NBDEBUG("initial request timeout\n");
22875374Sbp		return ETIMEDOUT;
22975374Sbp	}
23075374Sbp	if (error)			/* restart or interrupt */
23175374Sbp		return error;
23287192Sbp	error = nbssn_recv(nbp, &m0, &rplen, &rpcode, td);
23375374Sbp	if (error) {
23475374Sbp		NBDEBUG("recv() error %d\n", error);
23575374Sbp		return error;
23675374Sbp	}
23775374Sbp	/*
23875374Sbp	 * Process NETBIOS reply
23975374Sbp	 */
24075374Sbp	if (m0)
24175374Sbp		md_initm(mdp, m0);
24275374Sbp	error = 0;
24375374Sbp	do {
24475374Sbp		if (rpcode == NB_SSN_POSRESP) {
24575374Sbp			nbp->nbp_state = NBST_SESSION;
24675374Sbp			nbp->nbp_flags |= NBF_CONNECTED;
24775374Sbp			break;
24875374Sbp		}
24975374Sbp		if (rpcode != NB_SSN_RTGRESP) {
25075374Sbp			error = ECONNABORTED;
25175374Sbp			break;
25275374Sbp		}
25375374Sbp		if (rplen != 6) {
25475374Sbp			error = ECONNABORTED;
25575374Sbp			break;
25675374Sbp		}
25775374Sbp		md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM);
25875374Sbp		md_get_uint16(mdp, &port);
25975374Sbp		sin.sin_port = port;
26075374Sbp		nbp->nbp_state = NBST_RETARGET;
26187192Sbp		smb_nbst_disconnect(nbp->nbp_vc, td);
26287192Sbp		error = nb_connect_in(nbp, &sin, td);
26375374Sbp		if (!error)
26487192Sbp			error = nbssn_rq_request(nbp, td);
26575374Sbp		if (error) {
26687192Sbp			smb_nbst_disconnect(nbp->nbp_vc, td);
26775374Sbp			break;
26875374Sbp		}
26975374Sbp	} while(0);
27075374Sbp	if (m0)
27175374Sbp		md_done(mdp);
27275374Sbp	return error;
27375374Sbp}
27475374Sbp
27575374Sbpstatic int
27675374Sbpnbssn_recvhdr(struct nbpcb *nbp, int *lenp,
27787192Sbp	u_int8_t *rpcodep, int flags, struct thread *td)
27875374Sbp{
27975374Sbp	struct socket *so = nbp->nbp_tso;
28075374Sbp	struct uio auio;
28175374Sbp	struct iovec aio;
28275374Sbp	u_int32_t len;
28375374Sbp	int error;
28475374Sbp
28575374Sbp	aio.iov_base = (caddr_t)&len;
28675374Sbp	aio.iov_len = sizeof(len);
28775374Sbp	auio.uio_iov = &aio;
28875374Sbp	auio.uio_iovcnt = 1;
28975374Sbp	auio.uio_segflg = UIO_SYSSPACE;
29075374Sbp	auio.uio_rw = UIO_READ;
29175374Sbp	auio.uio_offset = 0;
29275374Sbp	auio.uio_resid = sizeof(len);
29387192Sbp	auio.uio_td = td;
294264425Sdteske	CURVNET_SET(so->so_vnet);
295160619Srwatson	error = soreceive(so, (struct sockaddr **)NULL, &auio,
29675374Sbp	    (struct mbuf **)NULL, (struct mbuf **)NULL, &flags);
297264425Sdteske	CURVNET_RESTORE();
29875374Sbp	if (error)
29975374Sbp		return error;
30075374Sbp	if (auio.uio_resid > 0) {
30175374Sbp		SMBSDEBUG("short reply\n");
30275374Sbp		return EPIPE;
30375374Sbp	}
30475374Sbp	len = ntohl(len);
30575374Sbp	*rpcodep = (len >> 24) & 0xFF;
30675374Sbp	len &= 0x1ffff;
30775374Sbp	if (len > SMB_MAXPKTLEN) {
30875374Sbp		SMBERROR("packet too long (%d)\n", len);
30975374Sbp		return EFBIG;
31075374Sbp	}
31175374Sbp	*lenp = len;
31275374Sbp	return 0;
31375374Sbp}
31475374Sbp
31575374Sbpstatic int
31675374Sbpnbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
31787192Sbp	u_int8_t *rpcodep, struct thread *td)
31875374Sbp{
31975374Sbp	struct socket *so = nbp->nbp_tso;
32075374Sbp	struct uio auio;
321103528Sbp	struct mbuf *m, *tm, *im;
32275374Sbp	u_int8_t rpcode;
323103528Sbp	int len, resid;
32475374Sbp	int error, rcvflg;
32575374Sbp
32675374Sbp	if (so == NULL)
32775374Sbp		return ENOTCONN;
32875374Sbp
32975374Sbp	if (mpp)
33075374Sbp		*mpp = NULL;
331103528Sbp	m = NULL;
33275374Sbp	for(;;) {
333103528Sbp		/*
334103528Sbp		 * Poll for a response header.
335103528Sbp		 * If we don't have one waiting, return.
336103528Sbp		 */
337170804Smjacob		len = 0;
338170804Smjacob		rpcode = 0;
33987192Sbp		error = nbssn_recvhdr(nbp, &len, &rpcode, MSG_DONTWAIT, td);
340130480Srwatson		if ((so->so_state & (SS_ISDISCONNECTING | SS_ISDISCONNECTED)) ||
341130480Srwatson		    (so->so_rcv.sb_state & SBS_CANTRCVMORE)) {
34275374Sbp			nbp->nbp_state = NBST_CLOSED;
34375374Sbp			NBDEBUG("session closed by peer\n");
34475374Sbp			return ECONNRESET;
34575374Sbp		}
34675374Sbp		if (error)
34775374Sbp			return error;
34875374Sbp		if (len == 0 && nbp->nbp_state != NBST_SESSION)
34975374Sbp			break;
350103528Sbp		/* no data, try again */
35175374Sbp		if (rpcode == NB_SSN_KEEPALIVE)
35275374Sbp			continue;
353103528Sbp
354103528Sbp		/*
355103528Sbp		 * Loop, blocking, for data following the response header.
356103528Sbp		 *
357103528Sbp		 * Note that we can't simply block here with MSG_WAITALL for the
358103528Sbp		 * entire response size, as it may be larger than the TCP
359103528Sbp		 * slow-start window that the sender employs.  This will result
360103528Sbp		 * in the sender stalling until the delayed ACK is sent, then
361103528Sbp		 * resuming slow-start, resulting in very poor performance.
362103528Sbp		 *
363103528Sbp		 * Instead, we never request more than NB_SORECEIVE_CHUNK
364103528Sbp		 * bytes at a time, resulting in an ack being pushed by
365103528Sbp		 * the TCP code at the completion of each call.
366103528Sbp		 */
367103528Sbp		resid = len;
368103528Sbp		while (resid > 0) {
369103528Sbp			tm = NULL;
37075374Sbp			rcvflg = MSG_WAITALL;
371103528Sbp			bzero(&auio, sizeof(auio));
372103528Sbp			auio.uio_resid = min(resid, NB_SORECEIVE_CHUNK);
373103528Sbp			auio.uio_td = td;
374103528Sbp			resid -= auio.uio_resid;
375103528Sbp			/*
376103528Sbp			 * Spin until we have collected everything in
377103528Sbp			 * this chunk.
378103528Sbp			 */
379103528Sbp			do {
380103528Sbp				rcvflg = MSG_WAITALL;
381264425Sdteske				CURVNET_SET(so->so_vnet);
382160619Srwatson				error = soreceive(so, (struct sockaddr **)NULL,
383107293Stjr				    &auio, &tm, (struct mbuf **)NULL, &rcvflg);
384264425Sdteske				CURVNET_RESTORE();
385103528Sbp			} while (error == EWOULDBLOCK || error == EINTR ||
38675374Sbp				 error == ERESTART);
387103528Sbp			if (error)
388103528Sbp				goto out;
389103528Sbp			/* short return guarantees unhappiness */
390103528Sbp			if (auio.uio_resid > 0) {
391103528Sbp				SMBERROR("packet is shorter than expected\n");
392103528Sbp				error = EPIPE;
393103528Sbp				goto out;
394103528Sbp			}
395103528Sbp			/* append received chunk to previous chunk(s) */
396103528Sbp			if (m == NULL) {
397103528Sbp				m = tm;
398103528Sbp			} else {
399103528Sbp				/*
400103528Sbp				 * Just glue the new chain on the end.
401103528Sbp				 * Consumer will pullup as required.
402103528Sbp				 */
403103528Sbp				for (im = m; im->m_next != NULL; im = im->m_next)
404103528Sbp					;
405103528Sbp				im->m_next = tm;
406103528Sbp			}
40775374Sbp		}
408103528Sbp		/* got a session/message packet? */
40975374Sbp		if (nbp->nbp_state == NBST_SESSION &&
41075374Sbp		    rpcode == NB_SSN_MESSAGE)
41175374Sbp			break;
412103528Sbp		/* drop packet and try for another */
41375374Sbp		NBDEBUG("non-session packet %x\n", rpcode);
414103528Sbp		if (m) {
41575374Sbp			m_freem(m);
416103528Sbp			m = NULL;
417103528Sbp		}
41875374Sbp	}
419103528Sbp
420103528Sbpout:
42175374Sbp	if (error) {
42275374Sbp		if (m)
42375374Sbp			m_freem(m);
42475374Sbp		return error;
42575374Sbp	}
42675374Sbp	if (mpp)
42775374Sbp		*mpp = m;
42875374Sbp	else
42975374Sbp		m_freem(m);
43075374Sbp	*lenp = len;
43175374Sbp	*rpcodep = rpcode;
43275374Sbp	return 0;
43375374Sbp}
43475374Sbp
43575374Sbp/*
43675374Sbp * SMB transport interface
43775374Sbp */
43875374Sbpstatic int
43987192Sbpsmb_nbst_create(struct smb_vc *vcp, struct thread *td)
44075374Sbp{
44175374Sbp	struct nbpcb *nbp;
44275374Sbp
443184205Sdes	nbp = malloc(sizeof *nbp, M_NBDATA, M_WAITOK);
44475374Sbp	bzero(nbp, sizeof *nbp);
44575374Sbp	nbp->nbp_timo.tv_sec = 15;	/* XXX: sysctl ? */
44675374Sbp	nbp->nbp_state = NBST_CLOSED;
44775374Sbp	nbp->nbp_vc = vcp;
44875374Sbp	nbp->nbp_sndbuf = smb_tcpsndbuf;
44975374Sbp	nbp->nbp_rcvbuf = smb_tcprcvbuf;
45075374Sbp	vcp->vc_tdata = nbp;
45175374Sbp	return 0;
45275374Sbp}
45375374Sbp
45475374Sbpstatic int
45587192Sbpsmb_nbst_done(struct smb_vc *vcp, struct thread *td)
45675374Sbp{
45775374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
45875374Sbp
45975374Sbp	if (nbp == NULL)
46075374Sbp		return ENOTCONN;
46187192Sbp	smb_nbst_disconnect(vcp, td);
46275374Sbp	if (nbp->nbp_laddr)
46375374Sbp		free(nbp->nbp_laddr, M_SONAME);
46475374Sbp	if (nbp->nbp_paddr)
46575374Sbp		free(nbp->nbp_paddr, M_SONAME);
46675374Sbp	free(nbp, M_NBDATA);
46775374Sbp	return 0;
46875374Sbp}
46975374Sbp
47075374Sbpstatic int
47187192Sbpsmb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct thread *td)
47275374Sbp{
47375374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
47475374Sbp	struct sockaddr_nb *snb;
47575374Sbp	int error, slen;
47675374Sbp
47775374Sbp	NBDEBUG("\n");
47875374Sbp	error = EINVAL;
47975374Sbp	do {
48075374Sbp		if (nbp->nbp_flags & NBF_LOCADDR)
48175374Sbp			break;
48275374Sbp		/*
48375374Sbp		 * It is possible to create NETBIOS name in the kernel,
48475374Sbp		 * but nothing prevents us to do it in the user space.
48575374Sbp		 */
48675374Sbp		if (sap == NULL)
48775374Sbp			break;
48875374Sbp		slen = sap->sa_len;
48975374Sbp		if (slen < NB_MINSALEN)
49075374Sbp			break;
491126425Srwatson		snb = (struct sockaddr_nb*)sodupsockaddr(sap, M_WAITOK);
49275374Sbp		if (snb == NULL) {
49375374Sbp			error = ENOMEM;
49475374Sbp			break;
49575374Sbp		}
49675374Sbp		nbp->nbp_laddr = snb;
49775374Sbp		nbp->nbp_flags |= NBF_LOCADDR;
49875374Sbp		error = 0;
49975374Sbp	} while(0);
50075374Sbp	return error;
50175374Sbp}
50275374Sbp
50375374Sbpstatic int
50487192Sbpsmb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct thread *td)
50575374Sbp{
50675374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
50775374Sbp	struct sockaddr_in sin;
50875374Sbp	struct sockaddr_nb *snb;
50975374Sbp	struct timespec ts1, ts2;
51075374Sbp	int error, slen;
51175374Sbp
51275374Sbp	NBDEBUG("\n");
51375374Sbp	if (nbp->nbp_tso != NULL)
51475374Sbp		return EISCONN;
51575374Sbp	if (nbp->nbp_laddr == NULL)
51675374Sbp		return EINVAL;
51775374Sbp	slen = sap->sa_len;
51875374Sbp	if (slen < NB_MINSALEN)
51975374Sbp		return EINVAL;
52075374Sbp	if (nbp->nbp_paddr) {
52175374Sbp		free(nbp->nbp_paddr, M_SONAME);
52275374Sbp		nbp->nbp_paddr = NULL;
52375374Sbp	}
524126425Srwatson	snb = (struct sockaddr_nb*)sodupsockaddr(sap, M_WAITOK);
52575374Sbp	if (snb == NULL)
52675374Sbp		return ENOMEM;
52775374Sbp	nbp->nbp_paddr = snb;
52875374Sbp	sin = snb->snb_addrin;
52975374Sbp	getnanotime(&ts1);
53087192Sbp	error = nb_connect_in(nbp, &sin, td);
53175374Sbp	if (error)
53275374Sbp		return error;
53375374Sbp	getnanotime(&ts2);
53475374Sbp	timespecsub(&ts2, &ts1);
53575374Sbp	if (ts2.tv_sec == 0 && ts2.tv_sec == 0)
53675374Sbp		ts2.tv_sec = 1;
53775374Sbp	nbp->nbp_timo = ts2;
53875374Sbp	timespecadd(&nbp->nbp_timo, &ts2);
53975374Sbp	timespecadd(&nbp->nbp_timo, &ts2);
54075374Sbp	timespecadd(&nbp->nbp_timo, &ts2);	/*  * 4 */
54187192Sbp	error = nbssn_rq_request(nbp, td);
54275374Sbp	if (error)
54387192Sbp		smb_nbst_disconnect(vcp, td);
54475374Sbp	return error;
54575374Sbp}
54675374Sbp
54775374Sbpstatic int
54887192Sbpsmb_nbst_disconnect(struct smb_vc *vcp, struct thread *td)
54975374Sbp{
55075374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
55175374Sbp	struct socket *so;
55275374Sbp
55375374Sbp	if (nbp == NULL || nbp->nbp_tso == NULL)
55475374Sbp		return ENOTCONN;
55575374Sbp	if ((so = nbp->nbp_tso) != NULL) {
55675374Sbp		nbp->nbp_flags &= ~NBF_CONNECTED;
55775374Sbp		nbp->nbp_tso = (struct socket *)NULL;
55875374Sbp		soshutdown(so, 2);
55975374Sbp		soclose(so);
56075374Sbp	}
56175374Sbp	if (nbp->nbp_state != NBST_RETARGET) {
56275374Sbp		nbp->nbp_state = NBST_CLOSED;
56375374Sbp	}
56475374Sbp	return 0;
56575374Sbp}
56675374Sbp
56775374Sbpstatic int
56887192Sbpsmb_nbst_send(struct smb_vc *vcp, struct mbuf *m0, struct thread *td)
56975374Sbp{
57075374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
57175374Sbp	int error;
57275374Sbp
57375374Sbp	if (nbp->nbp_state != NBST_SESSION) {
57475374Sbp		error = ENOTCONN;
57575374Sbp		goto abort;
57675374Sbp	}
577177599Sru	M_PREPEND(m0, 4, M_WAIT);
57875374Sbp	nb_sethdr(m0, NB_SSN_MESSAGE, m_fixhdr(m0) - 4);
57987192Sbp	error = nb_sosend(nbp->nbp_tso, m0, 0, td);
58075374Sbp	return error;
58175374Sbpabort:
58275374Sbp	if (m0)
58375374Sbp		m_freem(m0);
58475374Sbp	return error;
58575374Sbp}
58675374Sbp
58775374Sbp
58875374Sbpstatic int
58987192Sbpsmb_nbst_recv(struct smb_vc *vcp, struct mbuf **mpp, struct thread *td)
59075374Sbp{
59175374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
59275374Sbp	u_int8_t rpcode;
59375374Sbp	int error, rplen;
59475374Sbp
59575374Sbp	nbp->nbp_flags |= NBF_RECVLOCK;
59687192Sbp	error = nbssn_recv(nbp, mpp, &rplen, &rpcode, td);
59775374Sbp	nbp->nbp_flags &= ~NBF_RECVLOCK;
59875374Sbp	return error;
59975374Sbp}
60075374Sbp
60175374Sbpstatic void
60275374Sbpsmb_nbst_timo(struct smb_vc *vcp)
60375374Sbp{
60475374Sbp	return;
60575374Sbp}
60675374Sbp
60775374Sbpstatic void
60875374Sbpsmb_nbst_intr(struct smb_vc *vcp)
60975374Sbp{
61075374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
61175374Sbp
61275374Sbp	if (nbp == NULL || nbp->nbp_tso == NULL)
61375374Sbp		return;
61475374Sbp	sorwakeup(nbp->nbp_tso);
61575374Sbp	sowwakeup(nbp->nbp_tso);
61675374Sbp}
61775374Sbp
61875374Sbpstatic int
61975374Sbpsmb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
62075374Sbp{
62175374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
62275374Sbp
62375374Sbp	switch (param) {
62475374Sbp	    case SMBTP_SNDSZ:
62575374Sbp		*(int*)data = nbp->nbp_sndbuf;
62675374Sbp		break;
62775374Sbp	    case SMBTP_RCVSZ:
62875374Sbp		*(int*)data = nbp->nbp_rcvbuf;
62975374Sbp		break;
63075374Sbp	    case SMBTP_TIMEOUT:
63175374Sbp		*(struct timespec*)data = nbp->nbp_timo;
63275374Sbp		break;
63375374Sbp	    default:
63475374Sbp		return EINVAL;
63575374Sbp	}
63675374Sbp	return 0;
63775374Sbp}
63875374Sbp
63975374Sbpstatic int
64075374Sbpsmb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
64175374Sbp{
64275374Sbp	struct nbpcb *nbp = vcp->vc_tdata;
64375374Sbp
64475374Sbp	switch (param) {
64575374Sbp	    case SMBTP_SELECTID:
64675374Sbp		nbp->nbp_selectid = data;
64775374Sbp		break;
64875374Sbp	    default:
64975374Sbp		return EINVAL;
65075374Sbp	}
65175374Sbp	return 0;
65275374Sbp}
65375374Sbp
65475374Sbp/*
65575374Sbp * Check for fatal errors
65675374Sbp */
65775374Sbpstatic int
65875374Sbpsmb_nbst_fatal(struct smb_vc *vcp, int error)
65975374Sbp{
66075374Sbp	switch (error) {
66175374Sbp	    case ENOTCONN:
66275374Sbp	    case ENETRESET:
66375374Sbp	    case ECONNABORTED:
66475374Sbp		return 1;
66575374Sbp	}
66675374Sbp	return 0;
66775374Sbp}
66875374Sbp
66975374Sbp
67075374Sbpstruct smb_tran_desc smb_tran_nbtcp_desc = {
67175374Sbp	SMBT_NBTCP,
67275374Sbp	smb_nbst_create, smb_nbst_done,
67375374Sbp	smb_nbst_bind, smb_nbst_connect, smb_nbst_disconnect,
67475374Sbp	smb_nbst_send, smb_nbst_recv,
67575374Sbp	smb_nbst_timo, smb_nbst_intr,
67675374Sbp	smb_nbst_getparam, smb_nbst_setparam,
67775374Sbp	smb_nbst_fatal
67875374Sbp};
67975374Sbp
680