smb_rq.c revision 103554
175374Sbp/*
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 * 3. All advertising materials mentioning features or use of this software
1475374Sbp *    must display the following acknowledgement:
1575374Sbp *    This product includes software developed by Boris Popov.
1675374Sbp * 4. Neither the name of the author nor the names of any co-contributors
1775374Sbp *    may be used to endorse or promote products derived from this software
1875374Sbp *    without specific prior written permission.
1975374Sbp *
2075374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2175374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2275374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2375374Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2475374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2575374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2675374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2775374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2875374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2975374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3075374Sbp * SUCH DAMAGE.
3175374Sbp *
3275374Sbp * $FreeBSD: head/sys/netsmb/smb_rq.c 103554 2002-09-18 19:44:14Z phk $
3375374Sbp */
3475374Sbp#include <sys/param.h>
3575374Sbp#include <sys/systm.h>
3695533Smike#include <sys/endian.h>
3775374Sbp#include <sys/kernel.h>
3875374Sbp#include <sys/malloc.h>
3975374Sbp#include <sys/proc.h>
4075374Sbp#include <sys/lock.h>
4175374Sbp#include <sys/sysctl.h>
4275374Sbp#include <sys/socket.h>
4375374Sbp#include <sys/socketvar.h>
4475374Sbp#include <sys/mbuf.h>
4575374Sbp
4675374Sbp#include <netsmb/smb.h>
4775374Sbp#include <netsmb/smb_conn.h>
4875374Sbp#include <netsmb/smb_rq.h>
4975374Sbp#include <netsmb/smb_subr.h>
5075374Sbp#include <netsmb/smb_tran.h>
5175374Sbp
5275374SbpMALLOC_DEFINE(M_SMBRQ, "SMBRQ", "SMB request");
5375374Sbp
5475374SbpMODULE_DEPEND(netsmb, libmchain, 1, 1, 1);
5575374Sbp
5675374Sbpstatic int  smb_rq_reply(struct smb_rq *rqp);
5775374Sbpstatic int  smb_rq_enqueue(struct smb_rq *rqp);
5875374Sbpstatic int  smb_rq_getenv(struct smb_connobj *layer,
5975374Sbp		struct smb_vc **vcpp, struct smb_share **sspp);
6075374Sbpstatic int  smb_rq_new(struct smb_rq *rqp, u_char cmd);
6175374Sbpstatic int  smb_t2_reply(struct smb_t2rq *t2p);
6275374Sbp
6375374Sbpint
6475374Sbpsmb_rq_alloc(struct smb_connobj *layer, u_char cmd, struct smb_cred *scred,
6575374Sbp	struct smb_rq **rqpp)
6675374Sbp{
6775374Sbp	struct smb_rq *rqp;
6875374Sbp	int error;
6975374Sbp
7075374Sbp	MALLOC(rqp, struct smb_rq *, sizeof(*rqp), M_SMBRQ, M_WAITOK);
7175374Sbp	if (rqp == NULL)
7275374Sbp		return ENOMEM;
7375374Sbp	error = smb_rq_init(rqp, layer, cmd, scred);
7475374Sbp	rqp->sr_flags |= SMBR_ALLOCED;
7575374Sbp	if (error) {
7675374Sbp		smb_rq_done(rqp);
7775374Sbp		return error;
7875374Sbp	}
7975374Sbp	*rqpp = rqp;
8075374Sbp	return 0;
8175374Sbp}
8275374Sbp
8375374Sbpstatic char tzero[12];
8475374Sbp
8575374Sbpint
8675374Sbpsmb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd,
8775374Sbp	struct smb_cred *scred)
8875374Sbp{
8975374Sbp	int error;
9075374Sbp
9175374Sbp	bzero(rqp, sizeof(*rqp));
9275374Sbp	smb_sl_init(&rqp->sr_slock, "srslock");
9375374Sbp	error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share);
9475374Sbp	if (error)
9575374Sbp		return error;
9675374Sbp	error = smb_vc_access(rqp->sr_vc, scred, SMBM_EXEC);
9775374Sbp	if (error)
9875374Sbp		return error;
9975374Sbp	if (rqp->sr_share) {
10075374Sbp		error = smb_share_access(rqp->sr_share, scred, SMBM_EXEC);
10175374Sbp		if (error)
10275374Sbp			return error;
10375374Sbp	}
10475374Sbp	rqp->sr_cred = scred;
10575374Sbp	rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc);
10675374Sbp	return smb_rq_new(rqp, cmd);
10775374Sbp}
10875374Sbp
10975374Sbpstatic int
11075374Sbpsmb_rq_new(struct smb_rq *rqp, u_char cmd)
11175374Sbp{
11275374Sbp	struct smb_vc *vcp = rqp->sr_vc;
11375374Sbp	struct mbchain *mbp = &rqp->sr_rq;
11475374Sbp	int error;
11575374Sbp
11675374Sbp	rqp->sr_sendcnt = 0;
11775374Sbp	mb_done(mbp);
11875374Sbp	md_done(&rqp->sr_rp);
11975374Sbp	error = mb_init(mbp);
12075374Sbp	if (error)
12175374Sbp		return error;
12275374Sbp	mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM);
12375374Sbp	mb_put_uint8(mbp, cmd);
12475374Sbp	mb_put_uint32le(mbp, 0);		/* DosError */
12575374Sbp	mb_put_uint8(mbp, vcp->vc_hflags);
126103391Sbp	if (cmd == SMB_COM_TRANSACTION || cmd == SMB_COM_TRANSACTION_SECONDARY)
127103391Sbp		mb_put_uint16le(mbp, (vcp->vc_hflags2 & ~SMB_FLAGS2_UNICODE));
128103391Sbp	else
129103391Sbp		mb_put_uint16le(mbp, vcp->vc_hflags2);
13075374Sbp	mb_put_mem(mbp, tzero, 12, MB_MSYSTEM);
13175374Sbp	rqp->sr_rqtid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
13275374Sbp	mb_put_uint16le(mbp, 1 /*scred->sc_p->p_pid & 0xffff*/);
13375374Sbp	rqp->sr_rquid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
13475374Sbp	mb_put_uint16le(mbp, rqp->sr_mid);
13575374Sbp	return 0;
13675374Sbp}
13775374Sbp
13875374Sbpvoid
13975374Sbpsmb_rq_done(struct smb_rq *rqp)
14075374Sbp{
14175374Sbp	mb_done(&rqp->sr_rq);
14275374Sbp	md_done(&rqp->sr_rp);
14375374Sbp	smb_sl_destroy(&rqp->sr_slock);
14475374Sbp	if (rqp->sr_flags & SMBR_ALLOCED)
14575374Sbp		free(rqp, M_SMBRQ);
14675374Sbp}
14775374Sbp
14875374Sbp/*
14975374Sbp * Simple request-reply exchange
15075374Sbp */
15175374Sbpint
15275374Sbpsmb_rq_simple(struct smb_rq *rqp)
15375374Sbp{
15475374Sbp	struct smb_vc *vcp = rqp->sr_vc;
15575374Sbp	int error = EINVAL, i;
15675374Sbp
15775374Sbp	for (i = 0; i < SMB_MAXRCN; i++) {
15875374Sbp		rqp->sr_flags &= ~SMBR_RESTART;
15975374Sbp		rqp->sr_timo = vcp->vc_timo;
16075374Sbp		rqp->sr_state = SMBRQ_NOTSENT;
16175374Sbp		error = smb_rq_enqueue(rqp);
16275374Sbp		if (error)
16375374Sbp			return error;
16475374Sbp		error = smb_rq_reply(rqp);
16575374Sbp		if (error == 0)
16675374Sbp			break;
16775374Sbp		if ((rqp->sr_flags & (SMBR_RESTART | SMBR_NORESTART)) != SMBR_RESTART)
16875374Sbp			break;
16975374Sbp	}
17075374Sbp	return error;
17175374Sbp}
17275374Sbp
17375374Sbpstatic int
17475374Sbpsmb_rq_enqueue(struct smb_rq *rqp)
17575374Sbp{
17675374Sbp	struct smb_share *ssp = rqp->sr_share;
17775374Sbp	int error;
17875374Sbp
17975374Sbp	if (ssp == NULL || rqp->sr_cred == &rqp->sr_vc->vc_iod->iod_scred) {
18075374Sbp		return smb_iod_addrq(rqp);
18175374Sbp	}
18275374Sbp	for (;;) {
18375374Sbp		SMBS_ST_LOCK(ssp);
18475374Sbp		if (ssp->ss_flags & SMBS_RECONNECTING) {
18575374Sbp			msleep(&ssp->ss_vcgenid, SMBS_ST_LOCKPTR(ssp),
18675374Sbp			    PWAIT | PDROP, "90trcn", hz);
18787192Sbp			if (smb_proc_intr(rqp->sr_cred->scr_td->td_proc))
18875374Sbp				return EINTR;
18975374Sbp			continue;
19075374Sbp		}
19175374Sbp		if (smb_share_valid(ssp) || (ssp->ss_flags & SMBS_CONNECTED) == 0) {
19275374Sbp			SMBS_ST_UNLOCK(ssp);
19375374Sbp		} else {
19475374Sbp			SMBS_ST_UNLOCK(ssp);
19575374Sbp			error = smb_iod_request(rqp->sr_vc->vc_iod,
19675374Sbp			    SMBIOD_EV_TREECONNECT | SMBIOD_EV_SYNC, ssp);
19775374Sbp			if (error)
19875374Sbp				return error;
19975374Sbp		}
20075374Sbp		error = smb_iod_addrq(rqp);
20175374Sbp		if (error != EXDEV)
20275374Sbp			break;
20375374Sbp	}
20475374Sbp	return error;
20575374Sbp}
20675374Sbp
20775374Sbpvoid
20875374Sbpsmb_rq_wstart(struct smb_rq *rqp)
20975374Sbp{
21075374Sbp	rqp->sr_wcount = mb_reserve(&rqp->sr_rq, sizeof(u_int8_t));
21175374Sbp	rqp->sr_rq.mb_count = 0;
21275374Sbp}
21375374Sbp
21475374Sbpvoid
21575374Sbpsmb_rq_wend(struct smb_rq *rqp)
21675374Sbp{
21775374Sbp	if (rqp->sr_wcount == NULL) {
21875374Sbp		SMBERROR("no wcount\n");	/* actually panic */
21975374Sbp		return;
22075374Sbp	}
22175374Sbp	if (rqp->sr_rq.mb_count & 1)
22275374Sbp		SMBERROR("odd word count\n");
22375374Sbp	*rqp->sr_wcount = rqp->sr_rq.mb_count / 2;
22475374Sbp}
22575374Sbp
22675374Sbpvoid
22775374Sbpsmb_rq_bstart(struct smb_rq *rqp)
22875374Sbp{
22975374Sbp	rqp->sr_bcount = (u_short*)mb_reserve(&rqp->sr_rq, sizeof(u_short));
23075374Sbp	rqp->sr_rq.mb_count = 0;
23175374Sbp}
23275374Sbp
23375374Sbpvoid
23475374Sbpsmb_rq_bend(struct smb_rq *rqp)
23575374Sbp{
23675374Sbp	int bcnt;
23775374Sbp
23875374Sbp	if (rqp->sr_bcount == NULL) {
23975374Sbp		SMBERROR("no bcount\n");	/* actually panic */
24075374Sbp		return;
24175374Sbp	}
24275374Sbp	bcnt = rqp->sr_rq.mb_count;
24375374Sbp	if (bcnt > 0xffff)
24475374Sbp		SMBERROR("byte count too large (%d)\n", bcnt);
24582037Sbp	*rqp->sr_bcount = htoles(bcnt);
24675374Sbp}
24775374Sbp
24875374Sbpint
24975374Sbpsmb_rq_intr(struct smb_rq *rqp)
25075374Sbp{
25187192Sbp	struct proc *p = rqp->sr_cred->scr_td->td_proc;
25275374Sbp
25375374Sbp	if (rqp->sr_flags & SMBR_INTR)
25475374Sbp		return EINTR;
25575374Sbp	return smb_proc_intr(p);
25675374Sbp}
25775374Sbp
25875374Sbpint
25975374Sbpsmb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp)
26075374Sbp{
26175374Sbp	*mbpp = &rqp->sr_rq;
26275374Sbp	return 0;
26375374Sbp}
26475374Sbp
26575374Sbpint
26675374Sbpsmb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp)
26775374Sbp{
26875374Sbp	*mbpp = &rqp->sr_rp;
26975374Sbp	return 0;
27075374Sbp}
27175374Sbp
27275374Sbpstatic int
27375374Sbpsmb_rq_getenv(struct smb_connobj *layer,
27475374Sbp	struct smb_vc **vcpp, struct smb_share **sspp)
27575374Sbp{
27675374Sbp	struct smb_vc *vcp = NULL;
27775374Sbp	struct smb_share *ssp = NULL;
27875374Sbp	struct smb_connobj *cp;
27975374Sbp	int error = 0;
28075374Sbp
28175374Sbp	switch (layer->co_level) {
28275374Sbp	    case SMBL_VC:
28375374Sbp		vcp = CPTOVC(layer);
28475374Sbp		if (layer->co_parent == NULL) {
28575374Sbp			SMBERROR("zombie VC %s\n", vcp->vc_srvname);
28675374Sbp			error = EINVAL;
28775374Sbp			break;
28875374Sbp		}
28975374Sbp		break;
29075374Sbp	    case SMBL_SHARE:
29175374Sbp		ssp = CPTOSS(layer);
29275374Sbp		cp = layer->co_parent;
29375374Sbp		if (cp == NULL) {
29475374Sbp			SMBERROR("zombie share %s\n", ssp->ss_name);
29575374Sbp			error = EINVAL;
29675374Sbp			break;
29775374Sbp		}
29875374Sbp		error = smb_rq_getenv(cp, &vcp, NULL);
29975374Sbp		if (error)
30075374Sbp			break;
30175374Sbp		break;
30275374Sbp	    default:
30375374Sbp		SMBERROR("invalid layer %d passed\n", layer->co_level);
30475374Sbp		error = EINVAL;
30575374Sbp	}
30675374Sbp	if (vcpp)
30775374Sbp		*vcpp = vcp;
30875374Sbp	if (sspp)
30975374Sbp		*sspp = ssp;
31075374Sbp	return error;
31175374Sbp}
31275374Sbp
31375374Sbp/*
31475374Sbp * Wait for reply on the request
31575374Sbp */
31675374Sbpstatic int
31775374Sbpsmb_rq_reply(struct smb_rq *rqp)
31875374Sbp{
31975374Sbp	struct mdchain *mdp = &rqp->sr_rp;
32075374Sbp	u_int32_t tdw;
32175374Sbp	u_int8_t tb;
32275374Sbp	int error, rperror = 0;
32375374Sbp
32475374Sbp	error = smb_iod_waitrq(rqp);
32575374Sbp	if (error)
32675374Sbp		return error;
32775374Sbp	error = md_get_uint32(mdp, &tdw);
32875374Sbp	if (error)
32975374Sbp		return error;
33075374Sbp	error = md_get_uint8(mdp, &tb);
33175374Sbp	if (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_ERR_STATUS) {
33275374Sbp		error = md_get_uint32le(mdp, &rqp->sr_error);
33375374Sbp	} else {
33475374Sbp		error = md_get_uint8(mdp, &rqp->sr_errclass);
33575374Sbp		error = md_get_uint8(mdp, &tb);
33675374Sbp		error = md_get_uint16le(mdp, &rqp->sr_serror);
33775374Sbp		if (!error)
33875374Sbp			rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror);
33975374Sbp	}
34075374Sbp	error = md_get_uint8(mdp, &rqp->sr_rpflags);
34175374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rpflags2);
34275374Sbp
34375374Sbp	error = md_get_uint32(mdp, &tdw);
34475374Sbp	error = md_get_uint32(mdp, &tdw);
34575374Sbp	error = md_get_uint32(mdp, &tdw);
34675374Sbp
34775374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rptid);
34875374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rppid);
34975374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rpuid);
35075374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rpmid);
35175374Sbp
35275374Sbp	SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n",
35375374Sbp	    rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid,
35475374Sbp	    rqp->sr_errclass, rqp->sr_serror);
35575374Sbp	return error ? error : rperror;
35675374Sbp}
35775374Sbp
35875374Sbp
35975374Sbp#define ALIGN4(a)	(((a) + 3) & ~3)
36075374Sbp
36175374Sbp/*
36275374Sbp * TRANS2 request implementation
36375374Sbp */
36475374Sbpint
36575374Sbpsmb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred,
36675374Sbp	struct smb_t2rq **t2pp)
36775374Sbp{
36875374Sbp	struct smb_t2rq *t2p;
36975374Sbp	int error;
37075374Sbp
37175374Sbp	MALLOC(t2p, struct smb_t2rq *, sizeof(*t2p), M_SMBRQ, M_WAITOK);
37275374Sbp	if (t2p == NULL)
37375374Sbp		return ENOMEM;
37475374Sbp	error = smb_t2_init(t2p, layer, setup, scred);
37575374Sbp	t2p->t2_flags |= SMBT2_ALLOCED;
37675374Sbp	if (error) {
37775374Sbp		smb_t2_done(t2p);
37875374Sbp		return error;
37975374Sbp	}
38075374Sbp	*t2pp = t2p;
38175374Sbp	return 0;
38275374Sbp}
38375374Sbp
38475374Sbpint
38575374Sbpsmb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, u_short setup,
38675374Sbp	struct smb_cred *scred)
38775374Sbp{
38875374Sbp	int error;
38975374Sbp
39075374Sbp	bzero(t2p, sizeof(*t2p));
39175374Sbp	t2p->t2_source = source;
39275374Sbp	t2p->t2_setupcount = 1;
39375374Sbp	t2p->t2_setupdata = t2p->t2_setup;
39475374Sbp	t2p->t2_setup[0] = setup;
39575374Sbp	t2p->t2_fid = 0xffff;
39675374Sbp	t2p->t2_cred = scred;
39775374Sbp	error = smb_rq_getenv(source, &t2p->t2_vc, NULL);
39875374Sbp	if (error)
39975374Sbp		return error;
40075374Sbp	return 0;
40175374Sbp}
40275374Sbp
40375374Sbpvoid
40475374Sbpsmb_t2_done(struct smb_t2rq *t2p)
40575374Sbp{
40675374Sbp	mb_done(&t2p->t2_tparam);
40775374Sbp	mb_done(&t2p->t2_tdata);
40875374Sbp	md_done(&t2p->t2_rparam);
40975374Sbp	md_done(&t2p->t2_rdata);
41075374Sbp	if (t2p->t2_flags & SMBT2_ALLOCED)
41175374Sbp		free(t2p, M_SMBRQ);
41275374Sbp}
41375374Sbp
41475374Sbpstatic int
41575374Sbpsmb_t2_placedata(struct mbuf *mtop, u_int16_t offset, u_int16_t count,
41675374Sbp	struct mdchain *mdp)
41775374Sbp{
41875374Sbp	struct mbuf *m, *m0;
41975374Sbp	int len;
42075374Sbp
42187565Sarr	m0 = m_split(mtop, offset, M_TRYWAIT);
42275374Sbp	if (m0 == NULL)
42375374Sbp		return EBADRPC;
424103554Sphk	len = m_length(m0, &m);
42575374Sbp	m->m_len -= len - count;
42675374Sbp	if (mdp->md_top == NULL) {
42775374Sbp		md_initm(mdp, m0);
42875374Sbp	} else
42975374Sbp		m_cat(mdp->md_top, m0);
43075374Sbp	return 0;
43175374Sbp}
43275374Sbp
43375374Sbpstatic int
43475374Sbpsmb_t2_reply(struct smb_t2rq *t2p)
43575374Sbp{
43675374Sbp	struct mdchain *mdp;
43775374Sbp	struct smb_rq *rqp = t2p->t2_rq;
43875374Sbp	int error, totpgot, totdgot;
43975374Sbp	u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp;
44075374Sbp	u_int16_t tmp, bc, dcount;
44175374Sbp	u_int8_t wc;
44275374Sbp
44375374Sbp	error = smb_rq_reply(rqp);
44475374Sbp	if (error)
44575374Sbp		return error;
44675374Sbp	if ((t2p->t2_flags & SMBT2_ALLSENT) == 0) {
44775374Sbp		/*
44875374Sbp		 * this is an interim response, ignore it.
44975374Sbp		 */
45075374Sbp		SMBRQ_SLOCK(rqp);
45175374Sbp		md_next_record(&rqp->sr_rp);
45275374Sbp		SMBRQ_SUNLOCK(rqp);
45375374Sbp		return 0;
45475374Sbp	}
45575374Sbp	/*
45688741Sbp	 * Now we have to get all subsequent responses. The CIFS specification
45788741Sbp	 * says that they can be disordered which is weird.
45875374Sbp	 * TODO: timo
45975374Sbp	 */
46075374Sbp	totpgot = totdgot = 0;
46175374Sbp	totpcount = totdcount = 0xffff;
46275374Sbp	mdp = &rqp->sr_rp;
46375374Sbp	for (;;) {
46475374Sbp		m_dumpm(mdp->md_top);
46575374Sbp		if ((error = md_get_uint8(mdp, &wc)) != 0)
46675374Sbp			break;
46775374Sbp		if (wc < 10) {
46875374Sbp			error = ENOENT;
46975374Sbp			break;
47075374Sbp		}
47175374Sbp		if ((error = md_get_uint16le(mdp, &tmp)) != 0)
47275374Sbp			break;
47375374Sbp		if (totpcount > tmp)
47475374Sbp			totpcount = tmp;
47575374Sbp		md_get_uint16le(mdp, &tmp);
47675374Sbp		if (totdcount > tmp)
47775374Sbp			totdcount = tmp;
47875374Sbp		if ((error = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */
47975374Sbp		    (error = md_get_uint16le(mdp, &pcount)) != 0 ||
48075374Sbp		    (error = md_get_uint16le(mdp, &poff)) != 0 ||
48175374Sbp		    (error = md_get_uint16le(mdp, &pdisp)) != 0)
48275374Sbp			break;
48375374Sbp		if (pcount != 0 && pdisp != totpgot) {
48488741Sbp			SMBERROR("Can't handle disordered parameters %d:%d\n",
48575374Sbp			    pdisp, totpgot);
48675374Sbp			error = EINVAL;
48775374Sbp			break;
48875374Sbp		}
48975374Sbp		if ((error = md_get_uint16le(mdp, &dcount)) != 0 ||
49075374Sbp		    (error = md_get_uint16le(mdp, &doff)) != 0 ||
49175374Sbp		    (error = md_get_uint16le(mdp, &ddisp)) != 0)
49275374Sbp			break;
49375374Sbp		if (dcount != 0 && ddisp != totdgot) {
49488741Sbp			SMBERROR("Can't handle disordered data\n");
49575374Sbp			error = EINVAL;
49675374Sbp			break;
49775374Sbp		}
49875374Sbp		md_get_uint8(mdp, &wc);
49975374Sbp		md_get_uint8(mdp, NULL);
50075374Sbp		tmp = wc;
50175374Sbp		while (tmp--)
50275374Sbp			md_get_uint16(mdp, NULL);
50375374Sbp		if ((error = md_get_uint16le(mdp, &bc)) != 0)
50475374Sbp			break;
50575374Sbp/*		tmp = SMB_HDRLEN + 1 + 10 * 2 + 2 * wc + 2;*/
50675374Sbp		if (dcount) {
50775374Sbp			error = smb_t2_placedata(mdp->md_top, doff, dcount,
50875374Sbp			    &t2p->t2_rdata);
50975374Sbp			if (error)
51075374Sbp				break;
51175374Sbp		}
51275374Sbp		if (pcount) {
51375374Sbp			error = smb_t2_placedata(mdp->md_top, poff, pcount,
51475374Sbp			    &t2p->t2_rparam);
51575374Sbp			if (error)
51675374Sbp				break;
51775374Sbp		}
51875374Sbp		totpgot += pcount;
51975374Sbp		totdgot += dcount;
52075374Sbp		if (totpgot >= totpcount && totdgot >= totdcount) {
52175374Sbp			error = 0;
52275374Sbp			t2p->t2_flags |= SMBT2_ALLRECV;
52375374Sbp			break;
52475374Sbp		}
52575374Sbp		/*
52675374Sbp		 * We're done with this reply, look for the next one.
52775374Sbp		 */
52875374Sbp		SMBRQ_SLOCK(rqp);
52975374Sbp		md_next_record(&rqp->sr_rp);
53075374Sbp		SMBRQ_SUNLOCK(rqp);
53175374Sbp		error = smb_rq_reply(rqp);
53275374Sbp		if (error)
53375374Sbp			break;
53475374Sbp	}
53575374Sbp	return error;
53675374Sbp}
53775374Sbp
53875374Sbp/*
53975374Sbp * Perform a full round of TRANS2 request
54075374Sbp */
54175374Sbpstatic int
54275374Sbpsmb_t2_request_int(struct smb_t2rq *t2p)
54375374Sbp{
54475374Sbp	struct smb_vc *vcp = t2p->t2_vc;
54575374Sbp	struct smb_cred *scred = t2p->t2_cred;
54675374Sbp	struct mbchain *mbp;
54775374Sbp	struct mdchain *mdp, mbparam, mbdata;
54875374Sbp	struct mbuf *m;
54975374Sbp	struct smb_rq *rqp;
55075374Sbp	int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i;
55175374Sbp	int error, doff, poff, txdcount, txpcount, nmlen;
55275374Sbp
55375374Sbp	m = t2p->t2_tparam.mb_top;
55475374Sbp	if (m) {
55575374Sbp		md_initm(&mbparam, m);	/* do not free it! */
55675374Sbp		totpcount = m_fixhdr(m);
55775374Sbp		if (totpcount > 0xffff)		/* maxvalue for u_short */
55875374Sbp			return EINVAL;
55975374Sbp	} else
56075374Sbp		totpcount = 0;
56175374Sbp	m = t2p->t2_tdata.mb_top;
56275374Sbp	if (m) {
56375374Sbp		md_initm(&mbdata, m);	/* do not free it! */
56475374Sbp		totdcount =  m_fixhdr(m);
56575374Sbp		if (totdcount > 0xffff)
56675374Sbp			return EINVAL;
56775374Sbp	} else
56875374Sbp		totdcount = 0;
56975374Sbp	leftdcount = totdcount;
57075374Sbp	leftpcount = totpcount;
57175374Sbp	txmax = vcp->vc_txmax;
57275374Sbp	error = smb_rq_alloc(t2p->t2_source, t2p->t_name ?
57375374Sbp	    SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp);
57475374Sbp	if (error)
57575374Sbp		return error;
57675374Sbp	rqp->sr_flags |= SMBR_MULTIPACKET;
57775374Sbp	t2p->t2_rq = rqp;
57875374Sbp	mbp = &rqp->sr_rq;
57975374Sbp	smb_rq_wstart(rqp);
58075374Sbp	mb_put_uint16le(mbp, totpcount);
58175374Sbp	mb_put_uint16le(mbp, totdcount);
58275374Sbp	mb_put_uint16le(mbp, t2p->t2_maxpcount);
58375374Sbp	mb_put_uint16le(mbp, t2p->t2_maxdcount);
58475374Sbp	mb_put_uint8(mbp, t2p->t2_maxscount);
58575374Sbp	mb_put_uint8(mbp, 0);			/* reserved */
58675374Sbp	mb_put_uint16le(mbp, 0);			/* flags */
58775374Sbp	mb_put_uint32le(mbp, 0);			/* Timeout */
58875374Sbp	mb_put_uint16le(mbp, 0);			/* reserved 2 */
58975374Sbp	len = mb_fixhdr(mbp);
59075374Sbp	/*
59175374Sbp	 * now we have known packet size as
59275374Sbp	 * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + strlen(name) + 1),
59375374Sbp	 * and need to decide which parts should go into the first request
59475374Sbp	 */
59575374Sbp	nmlen = t2p->t_name ? strlen(t2p->t_name) : 0;
59675374Sbp	len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmlen + 1);
59775374Sbp	if (len + leftpcount > txmax) {
59875374Sbp		txpcount = min(leftpcount, txmax - len);
59975374Sbp		poff = len;
60075374Sbp		txdcount = 0;
60175374Sbp		doff = 0;
60275374Sbp	} else {
60375374Sbp		txpcount = leftpcount;
60475374Sbp		poff = txpcount ? len : 0;
60575374Sbp		len = ALIGN4(len + txpcount);
60675374Sbp		txdcount = min(leftdcount, txmax - len);
60775374Sbp		doff = txdcount ? len : 0;
60875374Sbp	}
60975374Sbp	leftpcount -= txpcount;
61075374Sbp	leftdcount -= txdcount;
61175374Sbp	mb_put_uint16le(mbp, txpcount);
61275374Sbp	mb_put_uint16le(mbp, poff);
61375374Sbp	mb_put_uint16le(mbp, txdcount);
61475374Sbp	mb_put_uint16le(mbp, doff);
61575374Sbp	mb_put_uint8(mbp, t2p->t2_setupcount);
61675374Sbp	mb_put_uint8(mbp, 0);
61775374Sbp	for (i = 0; i < t2p->t2_setupcount; i++)
61875374Sbp		mb_put_uint16le(mbp, t2p->t2_setupdata[i]);
61975374Sbp	smb_rq_wend(rqp);
62075374Sbp	smb_rq_bstart(rqp);
62175374Sbp	/* TDUNICODE */
62275374Sbp	if (t2p->t_name)
62375374Sbp		mb_put_mem(mbp, t2p->t_name, nmlen, MB_MSYSTEM);
62475374Sbp	mb_put_uint8(mbp, 0);	/* terminating zero */
62575374Sbp	len = mb_fixhdr(mbp);
62675374Sbp	if (txpcount) {
62775374Sbp		mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
62875374Sbp		error = md_get_mbuf(&mbparam, txpcount, &m);
62975374Sbp		SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax);
63075374Sbp		if (error)
63175374Sbp			goto freerq;
63275374Sbp		mb_put_mbuf(mbp, m);
63375374Sbp	}
63475374Sbp	len = mb_fixhdr(mbp);
63575374Sbp	if (txdcount) {
63675374Sbp		mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
63775374Sbp		error = md_get_mbuf(&mbdata, txdcount, &m);
63875374Sbp		if (error)
63975374Sbp			goto freerq;
64075374Sbp		mb_put_mbuf(mbp, m);
64175374Sbp	}
64275374Sbp	smb_rq_bend(rqp);	/* incredible, but thats it... */
64375374Sbp	error = smb_rq_enqueue(rqp);
64475374Sbp	if (error)
64575374Sbp		goto freerq;
64675374Sbp	if (leftpcount == 0 && leftdcount == 0)
64775374Sbp		t2p->t2_flags |= SMBT2_ALLSENT;
64875374Sbp	error = smb_t2_reply(t2p);
64975374Sbp	if (error)
65075374Sbp		goto bad;
65175374Sbp	while (leftpcount || leftdcount) {
65275374Sbp		error = smb_rq_new(rqp, t2p->t_name ?
65375374Sbp		    SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY);
65475374Sbp		if (error)
65575374Sbp			goto bad;
65675374Sbp		mbp = &rqp->sr_rq;
65775374Sbp		smb_rq_wstart(rqp);
65875374Sbp		mb_put_uint16le(mbp, totpcount);
65975374Sbp		mb_put_uint16le(mbp, totdcount);
66075374Sbp		len = mb_fixhdr(mbp);
66175374Sbp		/*
66275374Sbp		 * now we have known packet size as
66375374Sbp		 * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one,
66475374Sbp		 * and need to decide which parts should go into request
66575374Sbp		 */
66675374Sbp		len = ALIGN4(len + 6 * 2 + 2);
66775374Sbp		if (t2p->t_name == NULL)
66875374Sbp			len += 2;
66975374Sbp		if (len + leftpcount > txmax) {
67075374Sbp			txpcount = min(leftpcount, txmax - len);
67175374Sbp			poff = len;
67275374Sbp			txdcount = 0;
67375374Sbp			doff = 0;
67475374Sbp		} else {
67575374Sbp			txpcount = leftpcount;
67675374Sbp			poff = txpcount ? len : 0;
67775374Sbp			len = ALIGN4(len + txpcount);
67875374Sbp			txdcount = min(leftdcount, txmax - len);
67975374Sbp			doff = txdcount ? len : 0;
68075374Sbp		}
68175374Sbp		mb_put_uint16le(mbp, txpcount);
68275374Sbp		mb_put_uint16le(mbp, poff);
68375374Sbp		mb_put_uint16le(mbp, totpcount - leftpcount);
68475374Sbp		mb_put_uint16le(mbp, txdcount);
68575374Sbp		mb_put_uint16le(mbp, doff);
68675374Sbp		mb_put_uint16le(mbp, totdcount - leftdcount);
68775374Sbp		leftpcount -= txpcount;
68875374Sbp		leftdcount -= txdcount;
68975374Sbp		if (t2p->t_name == NULL)
69075374Sbp			mb_put_uint16le(mbp, t2p->t2_fid);
69175374Sbp		smb_rq_wend(rqp);
69275374Sbp		smb_rq_bstart(rqp);
69375374Sbp		mb_put_uint8(mbp, 0);	/* name */
69475374Sbp		len = mb_fixhdr(mbp);
69575374Sbp		if (txpcount) {
69675374Sbp			mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
69775374Sbp			error = md_get_mbuf(&mbparam, txpcount, &m);
69875374Sbp			if (error)
69975374Sbp				goto bad;
70075374Sbp			mb_put_mbuf(mbp, m);
70175374Sbp		}
70275374Sbp		len = mb_fixhdr(mbp);
70375374Sbp		if (txdcount) {
70475374Sbp			mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
70575374Sbp			error = md_get_mbuf(&mbdata, txdcount, &m);
70675374Sbp			if (error)
70775374Sbp				goto bad;
70875374Sbp			mb_put_mbuf(mbp, m);
70975374Sbp		}
71075374Sbp		smb_rq_bend(rqp);
71175374Sbp		rqp->sr_state = SMBRQ_NOTSENT;
71275374Sbp		error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_NEWRQ, NULL);
71375374Sbp		if (error)
71475374Sbp			goto bad;
71575374Sbp	}	/* while left params or data */
71675374Sbp	t2p->t2_flags |= SMBT2_ALLSENT;
71775374Sbp	mdp = &t2p->t2_rdata;
71875374Sbp	if (mdp->md_top) {
71975374Sbp		m_fixhdr(mdp->md_top);
72075374Sbp		md_initm(mdp, mdp->md_top);
72175374Sbp	}
72275374Sbp	mdp = &t2p->t2_rparam;
72375374Sbp	if (mdp->md_top) {
72475374Sbp		m_fixhdr(mdp->md_top);
72575374Sbp		md_initm(mdp, mdp->md_top);
72675374Sbp	}
72775374Sbpbad:
72875374Sbp	smb_iod_removerq(rqp);
72975374Sbpfreerq:
73075374Sbp	smb_rq_done(rqp);
73175374Sbp	if (error) {
73275374Sbp		if (rqp->sr_flags & SMBR_RESTART)
73375374Sbp			t2p->t2_flags |= SMBT2_RESTART;
73475374Sbp		md_done(&t2p->t2_rparam);
73575374Sbp		md_done(&t2p->t2_rdata);
73675374Sbp	}
73775374Sbp	return error;
73875374Sbp}
73975374Sbp
74075374Sbpint
74175374Sbpsmb_t2_request(struct smb_t2rq *t2p)
74275374Sbp{
74375374Sbp	int error = EINVAL, i;
74475374Sbp
74575374Sbp	for (i = 0; i < SMB_MAXRCN; i++) {
74675374Sbp		t2p->t2_flags &= ~SMBR_RESTART;
74775374Sbp		error = smb_t2_request_int(t2p);
74875374Sbp		if (error == 0)
74975374Sbp			break;
75075374Sbp		if ((t2p->t2_flags & (SMBT2_RESTART | SMBT2_NORESTART)) != SMBT2_RESTART)
75175374Sbp			break;
75275374Sbp	}
75375374Sbp	return error;
75475374Sbp}
755