1139823Simp/*-
2206361Sjoel * 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>
3175374Sbp#include <sys/systm.h>
3295533Smike#include <sys/endian.h>
3375374Sbp#include <sys/kernel.h>
3475374Sbp#include <sys/malloc.h>
35129880Sphk#include <sys/module.h>
3675374Sbp#include <sys/proc.h>
3775374Sbp#include <sys/lock.h>
3875374Sbp#include <sys/sysctl.h>
3975374Sbp#include <sys/socket.h>
4075374Sbp#include <sys/socketvar.h>
4175374Sbp#include <sys/mbuf.h>
4275374Sbp
4375374Sbp#include <netsmb/smb.h>
4475374Sbp#include <netsmb/smb_conn.h>
4575374Sbp#include <netsmb/smb_rq.h>
4675374Sbp#include <netsmb/smb_subr.h>
4775374Sbp#include <netsmb/smb_tran.h>
4875374Sbp
49227293Sedstatic MALLOC_DEFINE(M_SMBRQ, "SMBRQ", "SMB request");
5075374Sbp
5175374SbpMODULE_DEPEND(netsmb, libmchain, 1, 1, 1);
5275374Sbp
5375374Sbpstatic int  smb_rq_reply(struct smb_rq *rqp);
5475374Sbpstatic int  smb_rq_enqueue(struct smb_rq *rqp);
5575374Sbpstatic int  smb_rq_getenv(struct smb_connobj *layer,
5675374Sbp		struct smb_vc **vcpp, struct smb_share **sspp);
5775374Sbpstatic int  smb_rq_new(struct smb_rq *rqp, u_char cmd);
5875374Sbpstatic int  smb_t2_reply(struct smb_t2rq *t2p);
5975374Sbp
6075374Sbpint
6175374Sbpsmb_rq_alloc(struct smb_connobj *layer, u_char cmd, struct smb_cred *scred,
6275374Sbp	struct smb_rq **rqpp)
6375374Sbp{
6475374Sbp	struct smb_rq *rqp;
6575374Sbp	int error;
6675374Sbp
67184205Sdes	rqp = malloc(sizeof(*rqp), M_SMBRQ, M_WAITOK);
6875374Sbp	if (rqp == NULL)
6975374Sbp		return ENOMEM;
7075374Sbp	error = smb_rq_init(rqp, layer, cmd, scred);
7175374Sbp	rqp->sr_flags |= SMBR_ALLOCED;
7275374Sbp	if (error) {
7375374Sbp		smb_rq_done(rqp);
7475374Sbp		return error;
7575374Sbp	}
7675374Sbp	*rqpp = rqp;
7775374Sbp	return 0;
7875374Sbp}
7975374Sbp
8075374Sbpstatic char tzero[12];
8175374Sbp
8275374Sbpint
8375374Sbpsmb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd,
8475374Sbp	struct smb_cred *scred)
8575374Sbp{
8675374Sbp	int error;
8775374Sbp
8875374Sbp	bzero(rqp, sizeof(*rqp));
8975374Sbp	smb_sl_init(&rqp->sr_slock, "srslock");
9075374Sbp	error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share);
9175374Sbp	if (error)
9275374Sbp		return error;
9375374Sbp	error = smb_vc_access(rqp->sr_vc, scred, SMBM_EXEC);
9475374Sbp	if (error)
9575374Sbp		return error;
9675374Sbp	if (rqp->sr_share) {
9775374Sbp		error = smb_share_access(rqp->sr_share, scred, SMBM_EXEC);
9875374Sbp		if (error)
9975374Sbp			return error;
10075374Sbp	}
10175374Sbp	rqp->sr_cred = scred;
10275374Sbp	rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc);
10375374Sbp	return smb_rq_new(rqp, cmd);
10475374Sbp}
10575374Sbp
10675374Sbpstatic int
10775374Sbpsmb_rq_new(struct smb_rq *rqp, u_char cmd)
10875374Sbp{
10975374Sbp	struct smb_vc *vcp = rqp->sr_vc;
11075374Sbp	struct mbchain *mbp = &rqp->sr_rq;
11175374Sbp	int error;
112124087Stjr	u_int16_t flags2;
11375374Sbp
11475374Sbp	rqp->sr_sendcnt = 0;
11575374Sbp	mb_done(mbp);
11675374Sbp	md_done(&rqp->sr_rp);
11775374Sbp	error = mb_init(mbp);
11875374Sbp	if (error)
11975374Sbp		return error;
12075374Sbp	mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM);
12175374Sbp	mb_put_uint8(mbp, cmd);
12275374Sbp	mb_put_uint32le(mbp, 0);		/* DosError */
12375374Sbp	mb_put_uint8(mbp, vcp->vc_hflags);
124124087Stjr	flags2 = vcp->vc_hflags2;
125103391Sbp	if (cmd == SMB_COM_TRANSACTION || cmd == SMB_COM_TRANSACTION_SECONDARY)
126124087Stjr		flags2 &= ~SMB_FLAGS2_UNICODE;
127124087Stjr	if (cmd == SMB_COM_NEGOTIATE)
128124087Stjr		flags2 &= ~SMB_FLAGS2_SECURITY_SIGNATURE;
129124087Stjr	mb_put_uint16le(mbp, flags2);
130124087Stjr	if ((flags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0) {
131124087Stjr		mb_put_mem(mbp, tzero, 12, MB_MSYSTEM);
132124087Stjr		rqp->sr_rqsig = NULL;
133124087Stjr	} else {
134124087Stjr		mb_put_uint16le(mbp, 0 /*scred->sc_p->p_pid >> 16*/);
135124087Stjr		rqp->sr_rqsig = (u_int8_t *)mb_reserve(mbp, 8);
136124087Stjr		mb_put_uint16le(mbp, 0);
137124087Stjr	}
138161523Smarcel	rqp->sr_rqtid = mb_reserve(mbp, sizeof(u_int16_t));
13975374Sbp	mb_put_uint16le(mbp, 1 /*scred->sc_p->p_pid & 0xffff*/);
140161523Smarcel	rqp->sr_rquid = mb_reserve(mbp, sizeof(u_int16_t));
14175374Sbp	mb_put_uint16le(mbp, rqp->sr_mid);
14275374Sbp	return 0;
14375374Sbp}
14475374Sbp
14575374Sbpvoid
14675374Sbpsmb_rq_done(struct smb_rq *rqp)
14775374Sbp{
14875374Sbp	mb_done(&rqp->sr_rq);
14975374Sbp	md_done(&rqp->sr_rp);
15075374Sbp	smb_sl_destroy(&rqp->sr_slock);
15175374Sbp	if (rqp->sr_flags & SMBR_ALLOCED)
15275374Sbp		free(rqp, M_SMBRQ);
15375374Sbp}
15475374Sbp
15575374Sbp/*
15675374Sbp * Simple request-reply exchange
15775374Sbp */
15875374Sbpint
15975374Sbpsmb_rq_simple(struct smb_rq *rqp)
16075374Sbp{
16175374Sbp	struct smb_vc *vcp = rqp->sr_vc;
16275374Sbp	int error = EINVAL, i;
16375374Sbp
16475374Sbp	for (i = 0; i < SMB_MAXRCN; i++) {
16575374Sbp		rqp->sr_flags &= ~SMBR_RESTART;
16675374Sbp		rqp->sr_timo = vcp->vc_timo;
16775374Sbp		rqp->sr_state = SMBRQ_NOTSENT;
16875374Sbp		error = smb_rq_enqueue(rqp);
16975374Sbp		if (error)
17075374Sbp			return error;
17175374Sbp		error = smb_rq_reply(rqp);
17275374Sbp		if (error == 0)
17375374Sbp			break;
17475374Sbp		if ((rqp->sr_flags & (SMBR_RESTART | SMBR_NORESTART)) != SMBR_RESTART)
17575374Sbp			break;
17675374Sbp	}
17775374Sbp	return error;
17875374Sbp}
17975374Sbp
18075374Sbpstatic int
18175374Sbpsmb_rq_enqueue(struct smb_rq *rqp)
18275374Sbp{
18375374Sbp	struct smb_share *ssp = rqp->sr_share;
18475374Sbp	int error;
18575374Sbp
18675374Sbp	if (ssp == NULL || rqp->sr_cred == &rqp->sr_vc->vc_iod->iod_scred) {
18775374Sbp		return smb_iod_addrq(rqp);
18875374Sbp	}
18975374Sbp	for (;;) {
19075374Sbp		SMBS_ST_LOCK(ssp);
19175374Sbp		if (ssp->ss_flags & SMBS_RECONNECTING) {
19275374Sbp			msleep(&ssp->ss_vcgenid, SMBS_ST_LOCKPTR(ssp),
19375374Sbp			    PWAIT | PDROP, "90trcn", hz);
194112888Sjeff			if (smb_td_intr(rqp->sr_cred->scr_td))
19575374Sbp				return EINTR;
19675374Sbp			continue;
19775374Sbp		}
19875374Sbp		if (smb_share_valid(ssp) || (ssp->ss_flags & SMBS_CONNECTED) == 0) {
19975374Sbp			SMBS_ST_UNLOCK(ssp);
20075374Sbp		} else {
20175374Sbp			SMBS_ST_UNLOCK(ssp);
20275374Sbp			error = smb_iod_request(rqp->sr_vc->vc_iod,
20375374Sbp			    SMBIOD_EV_TREECONNECT | SMBIOD_EV_SYNC, ssp);
20475374Sbp			if (error)
20575374Sbp				return error;
20675374Sbp		}
20775374Sbp		error = smb_iod_addrq(rqp);
20875374Sbp		if (error != EXDEV)
20975374Sbp			break;
21075374Sbp	}
21175374Sbp	return error;
21275374Sbp}
21375374Sbp
21475374Sbpvoid
21575374Sbpsmb_rq_wstart(struct smb_rq *rqp)
21675374Sbp{
21775374Sbp	rqp->sr_wcount = mb_reserve(&rqp->sr_rq, sizeof(u_int8_t));
21875374Sbp	rqp->sr_rq.mb_count = 0;
21975374Sbp}
22075374Sbp
22175374Sbpvoid
22275374Sbpsmb_rq_wend(struct smb_rq *rqp)
22375374Sbp{
22475374Sbp	if (rqp->sr_wcount == NULL) {
22575374Sbp		SMBERROR("no wcount\n");	/* actually panic */
22675374Sbp		return;
22775374Sbp	}
22875374Sbp	if (rqp->sr_rq.mb_count & 1)
22975374Sbp		SMBERROR("odd word count\n");
23075374Sbp	*rqp->sr_wcount = rqp->sr_rq.mb_count / 2;
23175374Sbp}
23275374Sbp
23375374Sbpvoid
23475374Sbpsmb_rq_bstart(struct smb_rq *rqp)
23575374Sbp{
236161523Smarcel	rqp->sr_bcount = mb_reserve(&rqp->sr_rq, sizeof(u_short));
23775374Sbp	rqp->sr_rq.mb_count = 0;
23875374Sbp}
23975374Sbp
24075374Sbpvoid
24175374Sbpsmb_rq_bend(struct smb_rq *rqp)
24275374Sbp{
24375374Sbp	int bcnt;
24475374Sbp
24575374Sbp	if (rqp->sr_bcount == NULL) {
24675374Sbp		SMBERROR("no bcount\n");	/* actually panic */
24775374Sbp		return;
24875374Sbp	}
24975374Sbp	bcnt = rqp->sr_rq.mb_count;
25075374Sbp	if (bcnt > 0xffff)
25175374Sbp		SMBERROR("byte count too large (%d)\n", bcnt);
252161523Smarcel	le16enc(rqp->sr_bcount, bcnt);
25375374Sbp}
25475374Sbp
25575374Sbpint
25675374Sbpsmb_rq_intr(struct smb_rq *rqp)
25775374Sbp{
25875374Sbp	if (rqp->sr_flags & SMBR_INTR)
25975374Sbp		return EINTR;
260112888Sjeff	return smb_td_intr(rqp->sr_cred->scr_td);
26175374Sbp}
26275374Sbp
26375374Sbpint
26475374Sbpsmb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp)
26575374Sbp{
26675374Sbp	*mbpp = &rqp->sr_rq;
26775374Sbp	return 0;
26875374Sbp}
26975374Sbp
27075374Sbpint
27175374Sbpsmb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp)
27275374Sbp{
27375374Sbp	*mbpp = &rqp->sr_rp;
27475374Sbp	return 0;
27575374Sbp}
27675374Sbp
27775374Sbpstatic int
27875374Sbpsmb_rq_getenv(struct smb_connobj *layer,
27975374Sbp	struct smb_vc **vcpp, struct smb_share **sspp)
28075374Sbp{
28175374Sbp	struct smb_vc *vcp = NULL;
28275374Sbp	struct smb_share *ssp = NULL;
28375374Sbp	struct smb_connobj *cp;
28475374Sbp	int error = 0;
28575374Sbp
28675374Sbp	switch (layer->co_level) {
28775374Sbp	    case SMBL_VC:
28875374Sbp		vcp = CPTOVC(layer);
28975374Sbp		if (layer->co_parent == NULL) {
29075374Sbp			SMBERROR("zombie VC %s\n", vcp->vc_srvname);
29175374Sbp			error = EINVAL;
29275374Sbp			break;
29375374Sbp		}
29475374Sbp		break;
29575374Sbp	    case SMBL_SHARE:
29675374Sbp		ssp = CPTOSS(layer);
29775374Sbp		cp = layer->co_parent;
29875374Sbp		if (cp == NULL) {
29975374Sbp			SMBERROR("zombie share %s\n", ssp->ss_name);
30075374Sbp			error = EINVAL;
30175374Sbp			break;
30275374Sbp		}
30375374Sbp		error = smb_rq_getenv(cp, &vcp, NULL);
30475374Sbp		if (error)
30575374Sbp			break;
30675374Sbp		break;
30775374Sbp	    default:
30875374Sbp		SMBERROR("invalid layer %d passed\n", layer->co_level);
30975374Sbp		error = EINVAL;
31075374Sbp	}
31175374Sbp	if (vcpp)
31275374Sbp		*vcpp = vcp;
31375374Sbp	if (sspp)
31475374Sbp		*sspp = ssp;
31575374Sbp	return error;
31675374Sbp}
31775374Sbp
31875374Sbp/*
31975374Sbp * Wait for reply on the request
32075374Sbp */
32175374Sbpstatic int
32275374Sbpsmb_rq_reply(struct smb_rq *rqp)
32375374Sbp{
32475374Sbp	struct mdchain *mdp = &rqp->sr_rp;
32575374Sbp	u_int32_t tdw;
32675374Sbp	u_int8_t tb;
32775374Sbp	int error, rperror = 0;
32875374Sbp
32975374Sbp	error = smb_iod_waitrq(rqp);
33075374Sbp	if (error)
33175374Sbp		return error;
33275374Sbp	error = md_get_uint32(mdp, &tdw);
33375374Sbp	if (error)
33475374Sbp		return error;
33575374Sbp	error = md_get_uint8(mdp, &tb);
33675374Sbp	if (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_ERR_STATUS) {
33775374Sbp		error = md_get_uint32le(mdp, &rqp->sr_error);
33875374Sbp	} else {
33975374Sbp		error = md_get_uint8(mdp, &rqp->sr_errclass);
34075374Sbp		error = md_get_uint8(mdp, &tb);
34175374Sbp		error = md_get_uint16le(mdp, &rqp->sr_serror);
34275374Sbp		if (!error)
34375374Sbp			rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror);
34475374Sbp	}
34575374Sbp	error = md_get_uint8(mdp, &rqp->sr_rpflags);
34675374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rpflags2);
34775374Sbp
34875374Sbp	error = md_get_uint32(mdp, &tdw);
34975374Sbp	error = md_get_uint32(mdp, &tdw);
35075374Sbp	error = md_get_uint32(mdp, &tdw);
35175374Sbp
35275374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rptid);
35375374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rppid);
35475374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rpuid);
35575374Sbp	error = md_get_uint16le(mdp, &rqp->sr_rpmid);
35675374Sbp
357124087Stjr	if (error == 0 &&
358124087Stjr	    (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE))
359124087Stjr		error = smb_rq_verify(rqp);
360124087Stjr
36175374Sbp	SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n",
36275374Sbp	    rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid,
36375374Sbp	    rqp->sr_errclass, rqp->sr_serror);
36475374Sbp	return error ? error : rperror;
36575374Sbp}
36675374Sbp
36775374Sbp
36875374Sbp#define ALIGN4(a)	(((a) + 3) & ~3)
36975374Sbp
37075374Sbp/*
37175374Sbp * TRANS2 request implementation
37275374Sbp */
37375374Sbpint
37475374Sbpsmb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred,
37575374Sbp	struct smb_t2rq **t2pp)
37675374Sbp{
37775374Sbp	struct smb_t2rq *t2p;
37875374Sbp	int error;
37975374Sbp
380184205Sdes	t2p = malloc(sizeof(*t2p), M_SMBRQ, M_WAITOK);
38175374Sbp	if (t2p == NULL)
38275374Sbp		return ENOMEM;
38375374Sbp	error = smb_t2_init(t2p, layer, setup, scred);
38475374Sbp	t2p->t2_flags |= SMBT2_ALLOCED;
38575374Sbp	if (error) {
38675374Sbp		smb_t2_done(t2p);
38775374Sbp		return error;
38875374Sbp	}
38975374Sbp	*t2pp = t2p;
39075374Sbp	return 0;
39175374Sbp}
39275374Sbp
39375374Sbpint
39475374Sbpsmb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, u_short setup,
39575374Sbp	struct smb_cred *scred)
39675374Sbp{
39775374Sbp	int error;
39875374Sbp
39975374Sbp	bzero(t2p, sizeof(*t2p));
40075374Sbp	t2p->t2_source = source;
40175374Sbp	t2p->t2_setupcount = 1;
40275374Sbp	t2p->t2_setupdata = t2p->t2_setup;
40375374Sbp	t2p->t2_setup[0] = setup;
40475374Sbp	t2p->t2_fid = 0xffff;
40575374Sbp	t2p->t2_cred = scred;
40675374Sbp	error = smb_rq_getenv(source, &t2p->t2_vc, NULL);
40775374Sbp	if (error)
40875374Sbp		return error;
40975374Sbp	return 0;
41075374Sbp}
41175374Sbp
41275374Sbpvoid
41375374Sbpsmb_t2_done(struct smb_t2rq *t2p)
41475374Sbp{
41575374Sbp	mb_done(&t2p->t2_tparam);
41675374Sbp	mb_done(&t2p->t2_tdata);
41775374Sbp	md_done(&t2p->t2_rparam);
41875374Sbp	md_done(&t2p->t2_rdata);
41975374Sbp	if (t2p->t2_flags & SMBT2_ALLOCED)
42075374Sbp		free(t2p, M_SMBRQ);
42175374Sbp}
42275374Sbp
42375374Sbpstatic int
42475374Sbpsmb_t2_placedata(struct mbuf *mtop, u_int16_t offset, u_int16_t count,
42575374Sbp	struct mdchain *mdp)
42675374Sbp{
42775374Sbp	struct mbuf *m, *m0;
42875374Sbp	int len;
42975374Sbp
430243882Sglebius	m0 = m_split(mtop, offset, M_WAITOK);
431103554Sphk	len = m_length(m0, &m);
43275374Sbp	m->m_len -= len - count;
43375374Sbp	if (mdp->md_top == NULL) {
43475374Sbp		md_initm(mdp, m0);
43575374Sbp	} else
43675374Sbp		m_cat(mdp->md_top, m0);
43775374Sbp	return 0;
43875374Sbp}
43975374Sbp
44075374Sbpstatic int
44175374Sbpsmb_t2_reply(struct smb_t2rq *t2p)
44275374Sbp{
44375374Sbp	struct mdchain *mdp;
44475374Sbp	struct smb_rq *rqp = t2p->t2_rq;
44575374Sbp	int error, totpgot, totdgot;
44675374Sbp	u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp;
44775374Sbp	u_int16_t tmp, bc, dcount;
44875374Sbp	u_int8_t wc;
44975374Sbp
45075374Sbp	error = smb_rq_reply(rqp);
45175374Sbp	if (error)
45275374Sbp		return error;
45375374Sbp	if ((t2p->t2_flags & SMBT2_ALLSENT) == 0) {
45475374Sbp		/*
45575374Sbp		 * this is an interim response, ignore it.
45675374Sbp		 */
45775374Sbp		SMBRQ_SLOCK(rqp);
45875374Sbp		md_next_record(&rqp->sr_rp);
45975374Sbp		SMBRQ_SUNLOCK(rqp);
46075374Sbp		return 0;
46175374Sbp	}
46275374Sbp	/*
46388741Sbp	 * Now we have to get all subsequent responses. The CIFS specification
46488741Sbp	 * says that they can be disordered which is weird.
46575374Sbp	 * TODO: timo
46675374Sbp	 */
46775374Sbp	totpgot = totdgot = 0;
46875374Sbp	totpcount = totdcount = 0xffff;
46975374Sbp	mdp = &rqp->sr_rp;
47075374Sbp	for (;;) {
47175374Sbp		m_dumpm(mdp->md_top);
47275374Sbp		if ((error = md_get_uint8(mdp, &wc)) != 0)
47375374Sbp			break;
47475374Sbp		if (wc < 10) {
47575374Sbp			error = ENOENT;
47675374Sbp			break;
47775374Sbp		}
47875374Sbp		if ((error = md_get_uint16le(mdp, &tmp)) != 0)
47975374Sbp			break;
48075374Sbp		if (totpcount > tmp)
48175374Sbp			totpcount = tmp;
48275374Sbp		md_get_uint16le(mdp, &tmp);
48375374Sbp		if (totdcount > tmp)
48475374Sbp			totdcount = tmp;
48575374Sbp		if ((error = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */
48675374Sbp		    (error = md_get_uint16le(mdp, &pcount)) != 0 ||
48775374Sbp		    (error = md_get_uint16le(mdp, &poff)) != 0 ||
48875374Sbp		    (error = md_get_uint16le(mdp, &pdisp)) != 0)
48975374Sbp			break;
49075374Sbp		if (pcount != 0 && pdisp != totpgot) {
49188741Sbp			SMBERROR("Can't handle disordered parameters %d:%d\n",
49275374Sbp			    pdisp, totpgot);
49375374Sbp			error = EINVAL;
49475374Sbp			break;
49575374Sbp		}
49675374Sbp		if ((error = md_get_uint16le(mdp, &dcount)) != 0 ||
49775374Sbp		    (error = md_get_uint16le(mdp, &doff)) != 0 ||
49875374Sbp		    (error = md_get_uint16le(mdp, &ddisp)) != 0)
49975374Sbp			break;
50075374Sbp		if (dcount != 0 && ddisp != totdgot) {
50188741Sbp			SMBERROR("Can't handle disordered data\n");
50275374Sbp			error = EINVAL;
50375374Sbp			break;
50475374Sbp		}
50575374Sbp		md_get_uint8(mdp, &wc);
50675374Sbp		md_get_uint8(mdp, NULL);
50775374Sbp		tmp = wc;
50875374Sbp		while (tmp--)
50975374Sbp			md_get_uint16(mdp, NULL);
51075374Sbp		if ((error = md_get_uint16le(mdp, &bc)) != 0)
51175374Sbp			break;
51275374Sbp/*		tmp = SMB_HDRLEN + 1 + 10 * 2 + 2 * wc + 2;*/
51375374Sbp		if (dcount) {
51475374Sbp			error = smb_t2_placedata(mdp->md_top, doff, dcount,
51575374Sbp			    &t2p->t2_rdata);
51675374Sbp			if (error)
51775374Sbp				break;
51875374Sbp		}
51975374Sbp		if (pcount) {
52075374Sbp			error = smb_t2_placedata(mdp->md_top, poff, pcount,
52175374Sbp			    &t2p->t2_rparam);
52275374Sbp			if (error)
52375374Sbp				break;
52475374Sbp		}
52575374Sbp		totpgot += pcount;
52675374Sbp		totdgot += dcount;
52775374Sbp		if (totpgot >= totpcount && totdgot >= totdcount) {
52875374Sbp			error = 0;
52975374Sbp			t2p->t2_flags |= SMBT2_ALLRECV;
53075374Sbp			break;
53175374Sbp		}
53275374Sbp		/*
53375374Sbp		 * We're done with this reply, look for the next one.
53475374Sbp		 */
53575374Sbp		SMBRQ_SLOCK(rqp);
53675374Sbp		md_next_record(&rqp->sr_rp);
53775374Sbp		SMBRQ_SUNLOCK(rqp);
53875374Sbp		error = smb_rq_reply(rqp);
53975374Sbp		if (error)
54075374Sbp			break;
54175374Sbp	}
54275374Sbp	return error;
54375374Sbp}
54475374Sbp
54575374Sbp/*
54675374Sbp * Perform a full round of TRANS2 request
54775374Sbp */
54875374Sbpstatic int
54975374Sbpsmb_t2_request_int(struct smb_t2rq *t2p)
55075374Sbp{
55175374Sbp	struct smb_vc *vcp = t2p->t2_vc;
55275374Sbp	struct smb_cred *scred = t2p->t2_cred;
55375374Sbp	struct mbchain *mbp;
55475374Sbp	struct mdchain *mdp, mbparam, mbdata;
55575374Sbp	struct mbuf *m;
55675374Sbp	struct smb_rq *rqp;
55775374Sbp	int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i;
55875374Sbp	int error, doff, poff, txdcount, txpcount, nmlen;
55975374Sbp
56075374Sbp	m = t2p->t2_tparam.mb_top;
56175374Sbp	if (m) {
56275374Sbp		md_initm(&mbparam, m);	/* do not free it! */
56375374Sbp		totpcount = m_fixhdr(m);
56475374Sbp		if (totpcount > 0xffff)		/* maxvalue for u_short */
56575374Sbp			return EINVAL;
56675374Sbp	} else
56775374Sbp		totpcount = 0;
56875374Sbp	m = t2p->t2_tdata.mb_top;
56975374Sbp	if (m) {
57075374Sbp		md_initm(&mbdata, m);	/* do not free it! */
57175374Sbp		totdcount =  m_fixhdr(m);
57275374Sbp		if (totdcount > 0xffff)
57375374Sbp			return EINVAL;
57475374Sbp	} else
57575374Sbp		totdcount = 0;
57675374Sbp	leftdcount = totdcount;
57775374Sbp	leftpcount = totpcount;
57875374Sbp	txmax = vcp->vc_txmax;
57975374Sbp	error = smb_rq_alloc(t2p->t2_source, t2p->t_name ?
58075374Sbp	    SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp);
58175374Sbp	if (error)
58275374Sbp		return error;
58375374Sbp	rqp->sr_flags |= SMBR_MULTIPACKET;
58475374Sbp	t2p->t2_rq = rqp;
585124087Stjr	rqp->sr_t2 = t2p;
58675374Sbp	mbp = &rqp->sr_rq;
58775374Sbp	smb_rq_wstart(rqp);
58875374Sbp	mb_put_uint16le(mbp, totpcount);
58975374Sbp	mb_put_uint16le(mbp, totdcount);
59075374Sbp	mb_put_uint16le(mbp, t2p->t2_maxpcount);
59175374Sbp	mb_put_uint16le(mbp, t2p->t2_maxdcount);
59275374Sbp	mb_put_uint8(mbp, t2p->t2_maxscount);
59375374Sbp	mb_put_uint8(mbp, 0);			/* reserved */
59475374Sbp	mb_put_uint16le(mbp, 0);			/* flags */
59575374Sbp	mb_put_uint32le(mbp, 0);			/* Timeout */
59675374Sbp	mb_put_uint16le(mbp, 0);			/* reserved 2 */
59775374Sbp	len = mb_fixhdr(mbp);
59875374Sbp	/*
59975374Sbp	 * now we have known packet size as
60075374Sbp	 * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + strlen(name) + 1),
60175374Sbp	 * and need to decide which parts should go into the first request
60275374Sbp	 */
60375374Sbp	nmlen = t2p->t_name ? strlen(t2p->t_name) : 0;
60475374Sbp	len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmlen + 1);
60575374Sbp	if (len + leftpcount > txmax) {
60675374Sbp		txpcount = min(leftpcount, txmax - len);
60775374Sbp		poff = len;
60875374Sbp		txdcount = 0;
60975374Sbp		doff = 0;
61075374Sbp	} else {
61175374Sbp		txpcount = leftpcount;
61275374Sbp		poff = txpcount ? len : 0;
61375374Sbp		len = ALIGN4(len + txpcount);
61475374Sbp		txdcount = min(leftdcount, txmax - len);
61575374Sbp		doff = txdcount ? len : 0;
61675374Sbp	}
61775374Sbp	leftpcount -= txpcount;
61875374Sbp	leftdcount -= txdcount;
61975374Sbp	mb_put_uint16le(mbp, txpcount);
62075374Sbp	mb_put_uint16le(mbp, poff);
62175374Sbp	mb_put_uint16le(mbp, txdcount);
62275374Sbp	mb_put_uint16le(mbp, doff);
62375374Sbp	mb_put_uint8(mbp, t2p->t2_setupcount);
62475374Sbp	mb_put_uint8(mbp, 0);
62575374Sbp	for (i = 0; i < t2p->t2_setupcount; i++)
62675374Sbp		mb_put_uint16le(mbp, t2p->t2_setupdata[i]);
62775374Sbp	smb_rq_wend(rqp);
62875374Sbp	smb_rq_bstart(rqp);
62975374Sbp	/* TDUNICODE */
63075374Sbp	if (t2p->t_name)
63175374Sbp		mb_put_mem(mbp, t2p->t_name, nmlen, MB_MSYSTEM);
63275374Sbp	mb_put_uint8(mbp, 0);	/* terminating zero */
63375374Sbp	len = mb_fixhdr(mbp);
63475374Sbp	if (txpcount) {
63575374Sbp		mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
63675374Sbp		error = md_get_mbuf(&mbparam, txpcount, &m);
63775374Sbp		SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax);
63875374Sbp		if (error)
63975374Sbp			goto freerq;
64075374Sbp		mb_put_mbuf(mbp, m);
64175374Sbp	}
64275374Sbp	len = mb_fixhdr(mbp);
64375374Sbp	if (txdcount) {
64475374Sbp		mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
64575374Sbp		error = md_get_mbuf(&mbdata, txdcount, &m);
64675374Sbp		if (error)
64775374Sbp			goto freerq;
64875374Sbp		mb_put_mbuf(mbp, m);
64975374Sbp	}
65075374Sbp	smb_rq_bend(rqp);	/* incredible, but thats it... */
65175374Sbp	error = smb_rq_enqueue(rqp);
65275374Sbp	if (error)
65375374Sbp		goto freerq;
65475374Sbp	if (leftpcount == 0 && leftdcount == 0)
65575374Sbp		t2p->t2_flags |= SMBT2_ALLSENT;
65675374Sbp	error = smb_t2_reply(t2p);
65775374Sbp	if (error)
65875374Sbp		goto bad;
65975374Sbp	while (leftpcount || leftdcount) {
660124087Stjr		t2p->t2_flags |= SMBT2_SECONDARY;
66175374Sbp		error = smb_rq_new(rqp, t2p->t_name ?
66275374Sbp		    SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY);
66375374Sbp		if (error)
66475374Sbp			goto bad;
66575374Sbp		mbp = &rqp->sr_rq;
66675374Sbp		smb_rq_wstart(rqp);
66775374Sbp		mb_put_uint16le(mbp, totpcount);
66875374Sbp		mb_put_uint16le(mbp, totdcount);
66975374Sbp		len = mb_fixhdr(mbp);
67075374Sbp		/*
67175374Sbp		 * now we have known packet size as
67275374Sbp		 * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one,
67375374Sbp		 * and need to decide which parts should go into request
67475374Sbp		 */
67575374Sbp		len = ALIGN4(len + 6 * 2 + 2);
67675374Sbp		if (t2p->t_name == NULL)
67775374Sbp			len += 2;
67875374Sbp		if (len + leftpcount > txmax) {
67975374Sbp			txpcount = min(leftpcount, txmax - len);
68075374Sbp			poff = len;
68175374Sbp			txdcount = 0;
68275374Sbp			doff = 0;
68375374Sbp		} else {
68475374Sbp			txpcount = leftpcount;
68575374Sbp			poff = txpcount ? len : 0;
68675374Sbp			len = ALIGN4(len + txpcount);
68775374Sbp			txdcount = min(leftdcount, txmax - len);
68875374Sbp			doff = txdcount ? len : 0;
68975374Sbp		}
69075374Sbp		mb_put_uint16le(mbp, txpcount);
69175374Sbp		mb_put_uint16le(mbp, poff);
69275374Sbp		mb_put_uint16le(mbp, totpcount - leftpcount);
69375374Sbp		mb_put_uint16le(mbp, txdcount);
69475374Sbp		mb_put_uint16le(mbp, doff);
69575374Sbp		mb_put_uint16le(mbp, totdcount - leftdcount);
69675374Sbp		leftpcount -= txpcount;
69775374Sbp		leftdcount -= txdcount;
69875374Sbp		if (t2p->t_name == NULL)
69975374Sbp			mb_put_uint16le(mbp, t2p->t2_fid);
70075374Sbp		smb_rq_wend(rqp);
70175374Sbp		smb_rq_bstart(rqp);
70275374Sbp		mb_put_uint8(mbp, 0);	/* name */
70375374Sbp		len = mb_fixhdr(mbp);
70475374Sbp		if (txpcount) {
70575374Sbp			mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
70675374Sbp			error = md_get_mbuf(&mbparam, txpcount, &m);
70775374Sbp			if (error)
70875374Sbp				goto bad;
70975374Sbp			mb_put_mbuf(mbp, m);
71075374Sbp		}
71175374Sbp		len = mb_fixhdr(mbp);
71275374Sbp		if (txdcount) {
71375374Sbp			mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
71475374Sbp			error = md_get_mbuf(&mbdata, txdcount, &m);
71575374Sbp			if (error)
71675374Sbp				goto bad;
71775374Sbp			mb_put_mbuf(mbp, m);
71875374Sbp		}
71975374Sbp		smb_rq_bend(rqp);
72075374Sbp		rqp->sr_state = SMBRQ_NOTSENT;
72175374Sbp		error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_NEWRQ, NULL);
72275374Sbp		if (error)
72375374Sbp			goto bad;
72475374Sbp	}	/* while left params or data */
72575374Sbp	t2p->t2_flags |= SMBT2_ALLSENT;
72675374Sbp	mdp = &t2p->t2_rdata;
72775374Sbp	if (mdp->md_top) {
72875374Sbp		m_fixhdr(mdp->md_top);
72975374Sbp		md_initm(mdp, mdp->md_top);
73075374Sbp	}
73175374Sbp	mdp = &t2p->t2_rparam;
73275374Sbp	if (mdp->md_top) {
73375374Sbp		m_fixhdr(mdp->md_top);
73475374Sbp		md_initm(mdp, mdp->md_top);
73575374Sbp	}
73675374Sbpbad:
73775374Sbp	smb_iod_removerq(rqp);
73875374Sbpfreerq:
73975374Sbp	smb_rq_done(rqp);
74075374Sbp	if (error) {
74175374Sbp		if (rqp->sr_flags & SMBR_RESTART)
74275374Sbp			t2p->t2_flags |= SMBT2_RESTART;
74375374Sbp		md_done(&t2p->t2_rparam);
74475374Sbp		md_done(&t2p->t2_rdata);
74575374Sbp	}
74675374Sbp	return error;
74775374Sbp}
74875374Sbp
74975374Sbpint
75075374Sbpsmb_t2_request(struct smb_t2rq *t2p)
75175374Sbp{
75275374Sbp	int error = EINVAL, i;
75375374Sbp
75475374Sbp	for (i = 0; i < SMB_MAXRCN; i++) {
75575374Sbp		t2p->t2_flags &= ~SMBR_RESTART;
75675374Sbp		error = smb_t2_request_int(t2p);
75775374Sbp		if (error == 0)
75875374Sbp			break;
75975374Sbp		if ((t2p->t2_flags & (SMBT2_RESTART | SMBT2_NORESTART)) != SMBT2_RESTART)
76075374Sbp			break;
76175374Sbp	}
76275374Sbp	return error;
76375374Sbp}
764