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