#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/kpi_mbuf.h>
#include <sys/mount.h>
#include <sys/kauth.h>
#include <sys/smb_apple.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_tran.h>
#include <netsmb/smb_compat4.h>
#include <sys/vnode.h>
#include <smbfs/smbfs.h>
#include <netsmb/smb_converter.h>
MALLOC_DEFINE(M_SMBRQ, "SMBRQ", "SMB request");
static int smb_rq_reply(struct smb_rq *rqp);
static int smb_rq_getenv(struct smb_connobj *layer,
struct smb_vc **vcpp, struct smb_share **sspp);
static int smb_rq_new(struct smb_rq *rqp, u_char cmd);
static int smb_t2_reply(struct smb_t2rq *t2p);
void
mbuf_cat_internal(mbuf_t md_top, mbuf_t m0)
{
mbuf_t m;
for (m = md_top; mbuf_next(m) != NULL; m = mbuf_next(m))
;
mbuf_setnext(m, m0);
}
int smb_rq_alloc(struct smb_connobj *layer, u_char cmd,
vfs_context_t context, struct smb_rq **rqpp)
{
struct smb_rq *rqp;
int error;
MALLOC(rqp, struct smb_rq *, sizeof(*rqp), M_SMBRQ, M_WAITOK);
if (rqp == NULL)
return ENOMEM;
error = smb_rq_init(rqp, layer, cmd, context);
rqp->sr_flags |= SMBR_ALLOCED;
if (error) {
smb_rq_done(rqp);
return error;
}
*rqpp = rqp;
return 0;
}
int
smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd, vfs_context_t context)
{
int error;
bzero(rqp, sizeof(*rqp));
lck_mtx_init(&rqp->sr_slock, srs_lck_group, srs_lck_attr);
error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share);
if (error)
return error;
error = smb_vc_access(rqp->sr_vc, context);
if (error)
return error;
rqp->sr_context = context;
rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc);
error = smb_rq_new(rqp, cmd);
if (!error) {
rqp->sr_flags |= SMBR_VCREF;
smb_vc_ref(rqp->sr_vc);
}
return (error);
}
static int
smb_rq_new(struct smb_rq *rqp, u_char cmd)
{
struct smb_vc *vcp = rqp->sr_vc;
struct mbchain *mbp = &rqp->sr_rq;
int error;
u_int16_t flags2;
rqp->sr_cmd = cmd;
mb_done(mbp);
md_done(&rqp->sr_rp);
error = mb_init(mbp);
if (error)
return error;
mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM);
mb_put_uint8(mbp, cmd);
mb_put_uint32le(mbp, 0);
mb_put_uint8(mbp, vcp->vc_hflags);
flags2 = vcp->vc_hflags2;
if (cmd == SMB_COM_NEGOTIATE)
flags2 &= ~SMB_FLAGS2_SECURITY_SIGNATURE;
mb_put_uint16le(mbp, flags2);
mb_put_uint16le(mbp, 0);
rqp->sr_rqsig = (u_int8_t *)mb_reserve(mbp, 8);
bzero(rqp->sr_rqsig, 8);
mb_put_uint16le(mbp, 0);
rqp->sr_rqtid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
mb_put_uint16le(mbp, 1 );
rqp->sr_rquid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
mb_put_uint16le(mbp, rqp->sr_mid);
return 0;
}
void
smb_rq_done(struct smb_rq *rqp)
{
if (rqp->sr_flags & SMBR_VCREF) {
rqp->sr_flags &= ~SMBR_VCREF;
smb_vc_rele(rqp->sr_vc, rqp->sr_context);
}
mb_done(&rqp->sr_rq);
md_done(&rqp->sr_rp);
lck_mtx_destroy(&rqp->sr_slock, srs_lck_group);
if (rqp->sr_flags & SMBR_ALLOCED)
free(rqp, M_SMBRQ);
}
static int smb_rq_isforce(struct smb_share* share)
{
int error = 0;
lck_mtx_lock(&share->ss_mntlock);
if ((share->ss_mount) && (vfs_isforce(share->ss_mount->sm_mp))) {
wakeup(&share->ss_mount->sm_status);
error = ENXIO;
}
lck_mtx_unlock(&share->ss_mntlock);
return error;
}
int
smb_rq_simple_timed(struct smb_rq *rqp, int timo)
{
int error = 0;
if (rqp->sr_share)
error = smb_rq_isforce(rqp->sr_share);
if (! error) {
rqp->sr_timo = timo;
rqp->sr_state = SMBRQ_NOTSENT;
error = smb_iod_rq_enqueue(rqp);
}
if (! error)
error = smb_rq_reply(rqp);
return (error);
}
int
smb_rq_simple(struct smb_rq *rqp)
{
return (smb_rq_simple_timed(rqp, rqp->sr_vc->vc_timo));
}
void
smb_rq_wstart(struct smb_rq *rqp)
{
rqp->sr_wcount = (u_char *)mb_reserve(&rqp->sr_rq, sizeof(u_int8_t));
rqp->sr_rq.mb_count = 0;
}
void
smb_rq_wend(struct smb_rq *rqp)
{
if (rqp->sr_wcount == NULL) {
SMBERROR("no wcount\n");
return;
}
if (rqp->sr_rq.mb_count & 1)
SMBERROR("odd word count\n");
*rqp->sr_wcount = rqp->sr_rq.mb_count / 2;
}
void
smb_rq_bstart(struct smb_rq *rqp)
{
rqp->sr_bcount = (u_short*)mb_reserve(&rqp->sr_rq, sizeof(u_short));
rqp->sr_rq.mb_count = 0;
}
void smb_rq_bend(struct smb_rq *rqp)
{
u_int16_t bcnt;
DBG_ASSERT(rqp->sr_bcount);
if (rqp->sr_bcount == NULL) {
SMBERROR("no bcount\n");
return;
}
if (((rqp->sr_vc->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) && (rqp->sr_cmd == SMB_COM_READ_ANDX)) ||
((rqp->sr_vc->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) && (rqp->sr_cmd == SMB_COM_WRITE_ANDX))) {
if (rqp->sr_rq.mb_count > 0x0ffff)
bcnt = 0;
else
bcnt = (u_int16_t)rqp->sr_rq.mb_count;
} else if (rqp->sr_rq.mb_count > 0xffff) {
SMBERROR("byte count too large (%ld)\n", rqp->sr_rq.mb_count);
bcnt = 0xffff;
} else
bcnt = (u_int16_t)rqp->sr_rq.mb_count;
*rqp->sr_bcount = htoles(bcnt);
}
int
smb_rq_intr(struct smb_rq *rqp)
{
if (rqp->sr_flags & SMBR_INTR)
return EINTR;
return (smb_sigintr(rqp->sr_context));
}
int
smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp)
{
*mbpp = &rqp->sr_rq;
return 0;
}
int
smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp)
{
*mbpp = &rqp->sr_rp;
return 0;
}
static int
smb_rq_getenv(struct smb_connobj *layer,
struct smb_vc **vcpp, struct smb_share **sspp)
{
struct smb_vc *vcp = NULL;
struct smb_share *ssp = NULL;
struct smb_connobj *cp;
int error = 0;
switch (layer->co_level) {
case SMBL_VC:
vcp = CPTOVC(layer);
if (layer->co_parent == NULL) {
SMBERROR("zombie VC %s\n", vcp->vc_srvname);
error = EINVAL;
break;
}
break;
case SMBL_SHARE:
ssp = CPTOSS(layer);
cp = layer->co_parent;
if (cp == NULL) {
SMBERROR("zombie share %s\n", ssp->ss_name);
error = EINVAL;
break;
}
error = smb_rq_getenv(cp, &vcp, NULL);
if (error)
break;
break;
default:
SMBERROR("invalid layer %d passed\n", layer->co_level);
error = EINVAL;
}
if (vcpp)
*vcpp = vcp;
if (sspp)
*sspp = ssp;
return error;
}
static int
smb_rq_reply(struct smb_rq *rqp)
{
struct mdchain *mdp = &rqp->sr_rp;
u_int32_t tdw;
u_int8_t tb;
int error, rperror = 0;
if (rqp->sr_flags & SMBR_ASYNC)
smb_iod_removerq(rqp);
else {
if (rqp->sr_timo == SMBNOREPLYWAIT)
return (smb_iod_removerq(rqp));
error = smb_iod_waitrq(rqp);
if (error)
return error;
}
error = md_get_uint32(mdp, &tdw);
if (error)
return error;
error = md_get_uint8(mdp, &tb);
error = md_get_uint32le(mdp, &rqp->sr_error);
error = md_get_uint8(mdp, &rqp->sr_rpflags);
error = md_get_uint16le(mdp, &rqp->sr_rpflags2);
if (rqp->sr_rpflags2 & SMB_FLAGS2_ERR_STATUS) {
if (rqp->sr_error == NT_STATUS_BUFFER_OVERFLOW) {
rperror = 0;
} else
rperror = smb_maperr32(rqp->sr_error);
} else {
rqp->sr_errclass = rqp->sr_error & 0xff;
rqp->sr_serror = rqp->sr_error >> 16;
rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror);
}
if (rperror == EMOREDATA) {
rperror = E2BIG;
rqp->sr_flags |= SMBR_MOREDATA;
} else
rqp->sr_flags &= ~SMBR_MOREDATA;
error = md_get_uint32(mdp, &tdw);
error = md_get_uint32(mdp, &tdw);
error = md_get_uint32(mdp, &tdw);
error = md_get_uint16le(mdp, &rqp->sr_rptid);
error = md_get_uint16le(mdp, &rqp->sr_rppid);
error = md_get_uint16le(mdp, &rqp->sr_rpuid);
error = md_get_uint16le(mdp, &rqp->sr_rpmid);
if (error == 0 &&
(rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE))
error = smb_rq_verify(rqp);
SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n",
rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid,
rqp->sr_errclass, rqp->sr_serror);
return error ? error : rperror;
}
static int smb_nt_init(struct smb_ntrq *ntp, struct smb_connobj *source,
u_short fn, vfs_context_t context)
{
int error;
bzero(ntp, sizeof(*ntp));
ntp->nt_source = source;
ntp->nt_function = fn;
ntp->nt_context = context;
ntp->nt_share = (source->co_level == SMBL_SHARE ?
CPTOSS(source) : NULL);
error = smb_rq_getenv(source, &ntp->nt_vc, NULL);
if (error)
return error;
return 0;
}
int smb_nt_alloc(struct smb_connobj *layer, u_short fn,
vfs_context_t context, struct smb_ntrq **ntpp)
{
struct smb_ntrq *ntp;
int error;
MALLOC(ntp, struct smb_ntrq *, sizeof(*ntp), M_SMBRQ, M_WAITOK);
if (ntp == NULL)
return ENOMEM;
error = smb_nt_init(ntp, layer, fn, context);
ntp->nt_flags |= SMBT2_ALLOCED;
if (error) {
smb_nt_done(ntp);
return error;
}
*ntpp = ntp;
return 0;
}
#define ALIGN4(a) (((a) + 3) & ~3)
int smb_t2_alloc(struct smb_connobj *layer, u_short setup,
vfs_context_t context, struct smb_t2rq **t2pp)
{
struct smb_t2rq *t2p;
int error;
MALLOC(t2p, struct smb_t2rq *, sizeof(*t2p), M_SMBRQ, M_WAITOK);
if (t2p == NULL)
return ENOMEM;
error = smb_t2_init(t2p, layer, &setup, 1, context);
t2p->t2_flags |= SMBT2_ALLOCED;
if (error) {
smb_t2_done(t2p);
return error;
}
*t2pp = t2p;
return 0;
}
int
smb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, u_short *setup,
int setupcnt, vfs_context_t context)
{
int i;
int error;
bzero(t2p, sizeof(*t2p));
t2p->t2_source = source;
t2p->t2_setupcount = setupcnt;
t2p->t2_setupdata = t2p->t2_setup;
for (i = 0; i < setupcnt; i++)
t2p->t2_setup[i] = setup[i];
t2p->t2_fid = 0xffff;
t2p->t2_context = context;
t2p->t2_share = (source->co_level == SMBL_SHARE ?
CPTOSS(source) : NULL);
error = smb_rq_getenv(source, &t2p->t2_vc, NULL);
if (error)
return error;
return 0;
}
void
smb_t2_done(struct smb_t2rq *t2p)
{
mb_done(&t2p->t2_tparam);
mb_done(&t2p->t2_tdata);
md_done(&t2p->t2_rparam);
md_done(&t2p->t2_rdata);
if (t2p->t2_flags & SMBT2_ALLOCED)
free(t2p, M_SMBRQ);
}
void
smb_nt_done(struct smb_ntrq *ntp)
{
mb_done(&ntp->nt_tsetup);
mb_done(&ntp->nt_tparam);
mb_done(&ntp->nt_tdata);
md_done(&ntp->nt_rparam);
md_done(&ntp->nt_rdata);
if (ntp->nt_flags & SMBT2_ALLOCED)
free(ntp, M_SMBRQ);
}
static int smb_t2_placedata(mbuf_t mtop, u_int16_t offset, u_int16_t count,
struct mdchain *mdp)
{
mbuf_t m, m0;
u_int16_t len = 0;
size_t check_len = 0;
if (mbuf_split(mtop, offset, MBUF_WAITOK, &m0))
return EBADRPC;
for(m = m0; m; m = mbuf_next(m))
check_len += mbuf_len(m);
if (check_len > 0xffff)
return EINVAL;
else
len = (u_int16_t)check_len;
if (len > count) {
mbuf_adj(m0, count - len);
}
else if (len < count) {
return EINVAL;
}
if (mdp->md_top == NULL) {
md_initm(mdp, m0);
} else
mbuf_cat_internal(mdp->md_top, m0);
return 0;
}
static int
smb_t2_reply(struct smb_t2rq *t2p)
{
struct mdchain *mdp;
struct smb_rq *rqp = t2p->t2_rq;
int error, error2, totpgot, totdgot;
u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp;
u_int16_t tmp, bc, dcount;
u_int8_t wc;
t2p->t2_flags &= ~SMBT2_MOREDATA;
error = smb_rq_reply(rqp);
if (rqp->sr_flags & SMBR_MOREDATA)
t2p->t2_flags |= SMBT2_MOREDATA;
t2p->t2_sr_errclass = rqp->sr_errclass;
t2p->t2_sr_serror = rqp->sr_serror;
t2p->t2_sr_error = rqp->sr_error;
t2p->t2_sr_rpflags2 = rqp->sr_rpflags2;
if (error && !(rqp->sr_flags & SMBR_MOREDATA))
return error;
totpgot = totdgot = 0;
totpcount = totdcount = 0xffff;
mdp = &rqp->sr_rp;
for (;;) {
m_dumpm(mdp->md_top);
if ((error2 = md_get_uint8(mdp, &wc)) != 0)
break;
if (wc < 10) {
error2 = ENOENT;
break;
}
if ((error2 = md_get_uint16le(mdp, &tmp)) != 0)
break;
if (totpcount > tmp)
totpcount = tmp;
md_get_uint16le(mdp, &tmp);
if (totdcount > tmp)
totdcount = tmp;
if ((error2 = md_get_uint16le(mdp, &tmp)) != 0 ||
(error2 = md_get_uint16le(mdp, &pcount)) != 0 ||
(error2 = md_get_uint16le(mdp, &poff)) != 0 ||
(error2 = md_get_uint16le(mdp, &pdisp)) != 0)
break;
if (pcount != 0 && pdisp != totpgot) {
SMBERROR("Can't handle misordered parameters %d:%d\n",
pdisp, totpgot);
error2 = EINVAL;
break;
}
if ((error2 = md_get_uint16le(mdp, &dcount)) != 0 ||
(error2 = md_get_uint16le(mdp, &doff)) != 0 ||
(error2 = md_get_uint16le(mdp, &ddisp)) != 0)
break;
if (dcount != 0 && ddisp != totdgot) {
SMBERROR("Can't handle misordered data\n");
error2 = EINVAL;
break;
}
md_get_uint8(mdp, &wc);
md_get_uint8(mdp, NULL);
tmp = wc;
while (tmp--)
md_get_uint16(mdp, NULL);
if ((error2 = md_get_uint16le(mdp, &bc)) != 0)
break;
if (dcount) {
error2 = smb_t2_placedata(mdp->md_top, doff, dcount,
&t2p->t2_rdata);
if (error2)
break;
}
if (pcount) {
error2 = smb_t2_placedata(mdp->md_top, poff, pcount,
&t2p->t2_rparam);
if (error2)
break;
}
totpgot += pcount;
totdgot += dcount;
if (totpgot >= totpcount && totdgot >= totdcount) {
error2 = 0;
t2p->t2_flags |= SMBT2_ALLRECV;
break;
}
SMBRQ_SLOCK(rqp);
md_next_record(&rqp->sr_rp);
SMBRQ_SUNLOCK(rqp);
error2 = smb_rq_reply(rqp);
if (rqp->sr_flags & SMBR_MOREDATA)
t2p->t2_flags |= SMBT2_MOREDATA;
if (!error2)
continue;
t2p->t2_sr_errclass = rqp->sr_errclass;
t2p->t2_sr_serror = rqp->sr_serror;
t2p->t2_sr_error = rqp->sr_error;
t2p->t2_sr_rpflags2 = rqp->sr_rpflags2;
error = error2;
if (!(rqp->sr_flags & SMBR_MOREDATA))
break;
}
return (error ? error : error2);
}
int smb_nt_reply(struct smb_ntrq *ntp)
{
struct mdchain *mdp;
struct smb_rq *rqp = ntp->nt_rq;
int error, error2;
u_int32_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp;
u_int32_t tmp, dcount, totpgot, totdgot;
u_int16_t bc;
u_int8_t wc;
ntp->nt_flags &= ~SMBT2_MOREDATA;
error = smb_rq_reply(rqp);
if (rqp->sr_flags & SMBR_MOREDATA)
ntp->nt_flags |= SMBT2_MOREDATA;
ntp->nt_sr_error = rqp->sr_error;
ntp->nt_sr_rpflags2 = rqp->sr_rpflags2;
if (error && !(rqp->sr_flags & SMBR_MOREDATA))
return error;
totpgot = totdgot = 0;
totpcount = totdcount = 0xffffffff;
mdp = &rqp->sr_rp;
for (;;) {
m_dumpm(mdp->md_top);
if ((error2 = md_get_uint8(mdp, &wc)) != 0)
break;
if (wc < 18) {
error2 = ENOENT;
break;
}
md_get_mem(mdp, NULL, 3, MB_MSYSTEM);
if ((error2 = md_get_uint32le(mdp, &tmp)) != 0)
break;
if (totpcount > tmp)
totpcount = tmp;
if ((error2 = md_get_uint32le(mdp, &tmp)) != 0)
break;
if (totdcount > tmp)
totdcount = tmp;
if ((error2 = md_get_uint32le(mdp, &pcount)) != 0 ||
(error2 = md_get_uint32le(mdp, &poff)) != 0 ||
(error2 = md_get_uint32le(mdp, &pdisp)) != 0)
break;
if (pcount != 0 && pdisp != totpgot) {
SMBERROR("Can't handle misordered parameters %d:%d\n",
pdisp, totpgot);
error2 = EINVAL;
break;
}
if ((error2 = md_get_uint32le(mdp, &dcount)) != 0 ||
(error2 = md_get_uint32le(mdp, &doff)) != 0 ||
(error2 = md_get_uint32le(mdp, &ddisp)) != 0)
break;
if (dcount != 0 && ddisp != totdgot) {
SMBERROR("Can't handle misordered data\n");
error2 = EINVAL;
break;
}
md_get_uint8(mdp, &wc);
tmp = wc;
while (tmp--)
md_get_uint16(mdp, NULL);
if ((error2 = md_get_uint16le(mdp, &bc)) != 0)
break;
if (dcount) {
error2 = smb_t2_placedata(mdp->md_top, doff, dcount,
&ntp->nt_rdata);
if (error2)
break;
}
if (pcount) {
error2 = smb_t2_placedata(mdp->md_top, poff, pcount,
&ntp->nt_rparam);
if (error2)
break;
}
totpgot += pcount;
totdgot += dcount;
if (totpgot >= totpcount && totdgot >= totdcount) {
error2 = 0;
ntp->nt_flags |= SMBT2_ALLRECV;
break;
}
DBG_ASSERT((rqp->sr_flags & SMBR_ASYNC) != SMBR_ASYNC)
if (!(rqp->sr_flags & SMBR_MULTIPACKET)) {
SMBWARNING("Not doing multiple message, yet we didn't get all the data?\n");
error2 = EINVAL;
break;
}
SMBRQ_SLOCK(rqp);
md_next_record(&rqp->sr_rp);
SMBRQ_SUNLOCK(rqp);
error2 = smb_rq_reply(rqp);
if (rqp->sr_flags & SMBR_MOREDATA)
ntp->nt_flags |= SMBT2_MOREDATA;
if (!error2)
continue;
ntp->nt_sr_error = rqp->sr_error;
ntp->nt_sr_rpflags2 = rqp->sr_rpflags2;
error = error2;
if (!(rqp->sr_flags & SMBR_MOREDATA))
break;
}
return (error ? error : error2);
}
static int
smb_t2_request_int(struct smb_t2rq *t2p)
{
u_int16_t * txpcountp = NULL;
u_int16_t * txpoffsetp = NULL;
u_int16_t * txdcountp = NULL;
u_int16_t * txdoffsetp = NULL;
struct smb_vc *vcp = t2p->t2_vc;
struct mbchain *mbp;
struct mdchain *mdp, mbparam, mbdata;
mbuf_t m;
struct smb_rq *rqp;
u_int16_t ii;
int error;
size_t check_len;
u_int16_t totpcount, totdcount, leftpcount, leftdcount;
u_int16_t doff, poff, len, txdcount, txpcount, txmax;
m = t2p->t2_tparam.mb_top;
if (m) {
md_initm(&mbparam, m);
check_len = m_fixhdr(m);
if (check_len > 0xffff)
return EINVAL;
totpcount = (u_int16_t)check_len;
} else
totpcount = 0;
m = t2p->t2_tdata.mb_top;
if (m) {
md_initm(&mbdata, m);
check_len = m_fixhdr(m);
if (check_len > 0xffff)
return EINVAL;
totdcount = (u_int16_t)check_len;
} else
totdcount = 0;
leftdcount = totdcount;
leftpcount = totpcount;
if (vcp->vc_txmax > 0xffff)
txmax = 0xffff;
else
txmax = (u_int16_t)vcp->vc_txmax;
error = smb_rq_alloc(t2p->t2_source, t2p->t_name ?
SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, t2p->t2_context, &rqp);
if (error)
return error;
rqp->sr_timo = vcp->vc_timo;
rqp->sr_flags |= SMBR_MULTIPACKET;
t2p->t2_rq = rqp;
rqp->sr_t2 = t2p;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, totpcount);
mb_put_uint16le(mbp, totdcount);
mb_put_uint16le(mbp, t2p->t2_maxpcount);
mb_put_uint16le(mbp, t2p->t2_maxdcount);
mb_put_uint8(mbp, t2p->t2_maxscount);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, 0);
mb_put_uint16le(mbp, 0);
txpcountp = (u_int16_t *)mb_reserve(mbp, sizeof(u_int16_t));
txpoffsetp = (u_int16_t *)mb_reserve(mbp, sizeof(u_int16_t));
txdcountp = (u_int16_t *)mb_reserve(mbp, sizeof(u_int16_t));
txdoffsetp = (u_int16_t *)mb_reserve(mbp, sizeof(u_int16_t));
mb_put_uint8(mbp, t2p->t2_setupcount);
mb_put_uint8(mbp, 0);
for (ii = 0; ii < t2p->t2_setupcount; ii++)
mb_put_uint16le(mbp, t2p->t2_setupdata[ii]);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
if (t2p->t_name)
smb_put_dstring(mbp, vcp, t2p->t_name, PATH_MAX, NO_SFM_CONVERSIONS);
else
mb_put_uint8(mbp, 0);
len = mb_fixhdr(mbp);
mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
poff = len = mb_fixhdr(mbp);
if (len + leftpcount > txmax) {
txpcount = MIN(leftpcount, txmax - len);
txdcount = 0;
} else {
txpcount = leftpcount;
len = ALIGN4(len + txpcount);
txdcount = MIN(leftdcount, txmax - len);
}
leftpcount -= txpcount;
leftdcount -= txdcount;
*txpcountp = htoles(txpcount);
*txdcountp = htoles(txdcount);
*txpoffsetp = htoles(poff);
doff = poff + txpcount;
if (txpcount) {
error = md_get_mbuf(&mbparam, txpcount, &m);
if (error)
goto freerq;
mb_put_mbuf(mbp, m);
if (txdcount) {
len = mb_fixhdr(mbp);
mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
doff = mb_fixhdr(mbp);
}
}
*txdoffsetp = htoles(doff);
if (txdcount) {
error = md_get_mbuf(&mbdata, txdcount, &m);
if (error)
goto freerq;
mb_put_mbuf(mbp, m);
}
smb_rq_bend(rqp);
error = smb_iod_rq_enqueue(rqp);
if (error)
goto freerq;
if (leftpcount || leftdcount) {
error = smb_rq_reply(rqp);
if (error)
goto bad;
SMBRQ_SLOCK(rqp);
md_next_record(&rqp->sr_rp);
SMBRQ_SUNLOCK(rqp);
}
while (leftpcount || leftdcount) {
t2p->t2_flags |= SMBT2_SECONDARY;
error = smb_rq_new(rqp, t2p->t_name ?
SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY);
if (error)
goto bad;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, totpcount);
mb_put_uint16le(mbp, totdcount);
len = mb_fixhdr(mbp);
len = ALIGN4(len + 6 * 2 + 2);
if (t2p->t_name == NULL)
len += 2;
if (len + leftpcount > txmax) {
txpcount = MIN(leftpcount, txmax - len);
poff = len;
txdcount = 0;
doff = 0;
} else {
txpcount = leftpcount;
poff = txpcount ? len : 0;
len = ALIGN4(len + txpcount);
txdcount = MIN(leftdcount, txmax - len);
doff = txdcount ? len : 0;
}
mb_put_uint16le(mbp, txpcount);
mb_put_uint16le(mbp, poff);
mb_put_uint16le(mbp, totpcount - leftpcount);
mb_put_uint16le(mbp, txdcount);
mb_put_uint16le(mbp, doff);
mb_put_uint16le(mbp, totdcount - leftdcount);
leftpcount -= txpcount;
leftdcount -= txdcount;
if (t2p->t_name == NULL)
mb_put_uint16le(mbp, t2p->t2_fid);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, 0);
len = mb_fixhdr(mbp);
if (txpcount) {
mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
error = md_get_mbuf(&mbparam, txpcount, &m);
if (error)
goto bad;
mb_put_mbuf(mbp, m);
}
len = mb_fixhdr(mbp);
if (txdcount) {
mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
error = md_get_mbuf(&mbdata, txdcount, &m);
if (error)
goto bad;
mb_put_mbuf(mbp, m);
}
smb_rq_bend(rqp);
rqp->sr_state = SMBRQ_NOTSENT;
error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_NEWRQ, NULL);
if (error)
goto bad;
}
error = smb_t2_reply(t2p);
if (error && !(t2p->t2_flags & SMBT2_MOREDATA))
goto bad;
mdp = &t2p->t2_rdata;
if (mdp->md_top) {
m_fixhdr(mdp->md_top);
md_initm(mdp, mdp->md_top);
}
mdp = &t2p->t2_rparam;
if (mdp->md_top) {
m_fixhdr(mdp->md_top);
md_initm(mdp, mdp->md_top);
}
bad:
smb_iod_removerq(rqp);
freerq:
smb_rq_done(rqp);
if (error && !(t2p->t2_flags & SMBT2_MOREDATA)) {
md_done(&t2p->t2_rparam);
md_done(&t2p->t2_rdata);
}
return error;
}
static int
smb_nt_request_int(struct smb_ntrq *ntp)
{
struct smb_vc *vcp = ntp->nt_vc;
struct mbchain *mbp;
struct mdchain *mdp, mbsetup, mbparam, mbdata;
mbuf_t m;
struct smb_rq *rqp;
int error;
size_t check_len;
u_int32_t doff, poff, len, txdcount, txpcount;
u_int32_t totscount, totpcount, totdcount;
u_int32_t leftdcount, leftpcount;
m = ntp->nt_tsetup.mb_top;
if (m) {
md_initm(&mbsetup, m);
check_len = m_fixhdr(m);
if (check_len > 2 * 0xff)
return EINVAL;
totscount = (u_int32_t)check_len;
} else
totscount = 0;
m = ntp->nt_tparam.mb_top;
if (m) {
md_initm(&mbparam, m);
check_len = m_fixhdr(m);
if (check_len > 0x7fffffff)
return EINVAL;
totpcount = (u_int32_t)check_len;
} else
totpcount = 0;
m = ntp->nt_tdata.mb_top;
if (m) {
md_initm(&mbdata, m);
check_len = m_fixhdr(m);
if (check_len > 0x7fffffff)
return EINVAL;
totdcount = (u_int32_t)check_len;
} else
totdcount = 0;
leftdcount = totdcount;
leftpcount = totpcount;
error = smb_rq_alloc(ntp->nt_source, SMB_COM_NT_TRANSACT, ntp->nt_context, &rqp);
if (error)
return error;
rqp->sr_timo = vcp->vc_timo;
rqp->sr_flags |= SMBR_MULTIPACKET;
ntp->nt_rq = rqp;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint8(mbp, ntp->nt_maxscount);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, totpcount);
mb_put_uint32le(mbp, totdcount);
mb_put_uint32le(mbp, ntp->nt_maxpcount);
mb_put_uint32le(mbp, ntp->nt_maxdcount);
check_len = mb_fixhdr(mbp);
if (check_len > 0x7fffffff) {
error = EINVAL;
goto freerq;
}
len = (u_int32_t)check_len;
len = ALIGN4(len + 4 * 4 + 1 + 2 + ((totscount+1)&~1) + 2);
if (len + leftpcount > vcp->vc_txmax) {
txpcount = MIN(leftpcount, vcp->vc_txmax - len);
poff = len;
txdcount = 0;
doff = 0;
} else {
txpcount = leftpcount;
poff = txpcount ? (u_int32_t)len : 0;
len = ALIGN4(len + txpcount);
txdcount = MIN(leftdcount, vcp->vc_txmax - len);
doff = txdcount ? len : 0;
}
leftpcount -= txpcount;
leftdcount -= txdcount;
mb_put_uint32le(mbp, txpcount);
mb_put_uint32le(mbp, poff);
mb_put_uint32le(mbp, txdcount);
mb_put_uint32le(mbp, doff);
mb_put_uint8(mbp, (u_int8_t)(totscount+1)/2);
mb_put_uint16le(mbp, ntp->nt_function);
if (totscount) {
error = md_get_mbuf(&mbsetup, totscount, &m);
SMBSDEBUG("%d:%d:%d\n", error, totscount, vcp->vc_txmax);
if (error)
goto freerq;
mb_put_mbuf(mbp, m);
if (totscount & 1)
mb_put_uint8(mbp, 0);
}
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
check_len = mb_fixhdr(mbp);
if (txpcount) {
mb_put_mem(mbp, NULL, ALIGN4(check_len) - check_len, MB_MZERO);
error = md_get_mbuf(&mbparam, txpcount, &m);
SMBSDEBUG("%d:%d:%d\n", error, txpcount, vcp->vc_txmax);
if (error)
goto freerq;
mb_put_mbuf(mbp, m);
}
check_len = mb_fixhdr(mbp);
if (txdcount) {
mb_put_mem(mbp, NULL, ALIGN4(check_len) - check_len, MB_MZERO);
error = md_get_mbuf(&mbdata, txdcount, &m);
if (error)
goto freerq;
mb_put_mbuf(mbp, m);
}
smb_rq_bend(rqp);
error = smb_iod_rq_enqueue(rqp);
if (error)
goto freerq;
if (leftpcount || leftdcount) {
error = smb_rq_reply(rqp);
if (error)
goto bad;
SMBRQ_SLOCK(rqp);
md_next_record(&rqp->sr_rp);
SMBRQ_SUNLOCK(rqp);
}
while (leftpcount || leftdcount) {
error = smb_rq_new(rqp, SMB_COM_NT_TRANSACT_SECONDARY);
if (error)
goto bad;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_mem(mbp, NULL, 3, MB_MZERO);
mb_put_uint32le(mbp, totpcount);
mb_put_uint32le(mbp, totdcount);
check_len = mb_fixhdr(mbp);
check_len = ALIGN4(check_len + 6 * 4 + 2);
if (check_len > 0x7fffffff) {
error = EINVAL;
goto bad;
}
len = (u_int32_t)check_len;
if (len + leftpcount > vcp->vc_txmax) {
txpcount = (u_int32_t)MIN(leftpcount, vcp->vc_txmax - len);
poff = (u_int32_t)len;
txdcount = 0;
doff = 0;
} else {
txpcount = leftpcount;
poff = txpcount ? len : 0;
len = ALIGN4(len + txpcount);
txdcount = MIN(leftdcount, vcp->vc_txmax - len);
doff = txdcount ? len : 0;
}
mb_put_uint32le(mbp, txpcount);
mb_put_uint32le(mbp, poff);
mb_put_uint32le(mbp, (u_int32_t)(totpcount - leftpcount));
mb_put_uint32le(mbp, txdcount);
mb_put_uint32le(mbp, doff);
mb_put_uint32le(mbp, (u_int32_t)(totdcount - leftdcount));
leftpcount -= txpcount;
leftdcount -= txdcount;
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
check_len = mb_fixhdr(mbp);
if (txpcount) {
mb_put_mem(mbp, NULL, ALIGN4(check_len) - check_len, MB_MZERO);
error = md_get_mbuf(&mbparam, txpcount, &m);
if (error)
goto bad;
mb_put_mbuf(mbp, m);
}
check_len = mb_fixhdr(mbp);
if (txdcount) {
mb_put_mem(mbp, NULL, ALIGN4(check_len) - check_len, MB_MZERO);
error = md_get_mbuf(&mbdata, txdcount, &m);
if (error)
goto bad;
mb_put_mbuf(mbp, m);
}
smb_rq_bend(rqp);
rqp->sr_state = SMBRQ_NOTSENT;
error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_NEWRQ, NULL);
if (error)
goto bad;
}
error = smb_nt_reply(ntp);
if (error && !(ntp->nt_flags & SMBT2_MOREDATA))
goto bad;
mdp = &ntp->nt_rdata;
if (mdp->md_top) {
m_fixhdr(mdp->md_top);
md_initm(mdp, mdp->md_top);
}
mdp = &ntp->nt_rparam;
if (mdp->md_top) {
m_fixhdr(mdp->md_top);
md_initm(mdp, mdp->md_top);
}
bad:
smb_iod_removerq(rqp);
freerq:
smb_rq_done(rqp);
if (error && !(ntp->nt_flags & SMBT2_MOREDATA)) {
md_done(&ntp->nt_rparam);
md_done(&ntp->nt_rdata);
}
return error;
}
int
smb_t2_request(struct smb_t2rq *t2p)
{
int error = 0;
if (t2p->t2_share)
error = smb_rq_isforce(t2p->t2_share);
if (! error)
error = smb_t2_request_int(t2p);
return error;
}
int
smb_nt_request(struct smb_ntrq *ntp)
{
int error = 0;
if (ntp->nt_share)
error = smb_rq_isforce(ntp->nt_share);
if (! error)
error = smb_nt_request_int(ntp);
return error;
}
int smb_nt_async_request(struct smb_ntrq *ntp, void *nt_callback, void *nt_callback_args)
{
struct smb_vc *vcp = ntp->nt_vc;
struct mbchain *mbp;
struct mdchain mbsetup, mbparam, mbdata;
mbuf_t m;
struct smb_rq *rqp;
int error;
size_t check_len;
u_int32_t doff, poff, len, txdcount, txpcount;
u_int32_t totscount, totpcount, totdcount;
m = ntp->nt_tsetup.mb_top;
if (m) {
md_initm(&mbsetup, m);
check_len = m_fixhdr(m);
if (check_len > 2 * 0xff)
return EINVAL;
totscount = (u_int32_t)check_len;
} else
totscount = 0;
m = ntp->nt_tparam.mb_top;
if (m) {
md_initm(&mbparam, m);
check_len = m_fixhdr(m);
if (check_len > 0x7fffffff)
return EINVAL;
totpcount = (u_int32_t)check_len;
} else
totpcount = 0;
m = ntp->nt_tdata.mb_top;
if (m) {
md_initm(&mbdata, m);
check_len = m_fixhdr(m);
if (check_len > 0x7fffffff)
return EINVAL;
totdcount = (u_int32_t)check_len;
} else
totdcount = 0;
error = smb_rq_alloc(ntp->nt_source, SMB_COM_NT_TRANSACT, ntp->nt_context, &rqp);
if (error)
return error;
rqp->sr_timo = vcp->vc_timo;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint8(mbp, ntp->nt_maxscount);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, totpcount);
mb_put_uint32le(mbp, totdcount);
mb_put_uint32le(mbp, ntp->nt_maxpcount);
mb_put_uint32le(mbp, ntp->nt_maxdcount);
check_len = mb_fixhdr(mbp);
if (check_len > 0x7fffffff) {
error = EINVAL;
goto free_rqp;
}
len = (u_int32_t)check_len;
len = ALIGN4(len + 4 * 4 + 1 + 2 + ((totscount+1)&~1) + 2);
if ((len + totpcount + totdcount) > vcp->vc_txmax) {
error = EINVAL;
goto free_rqp;
}
txpcount = totpcount;
poff = txpcount ? (u_int32_t)len : 0;
len = ALIGN4(len + txpcount);
txdcount = totdcount;
doff = txdcount ? len : 0;
mb_put_uint32le(mbp, txpcount);
mb_put_uint32le(mbp, poff);
mb_put_uint32le(mbp, txdcount);
mb_put_uint32le(mbp, doff);
mb_put_uint8(mbp, (u_int8_t)(totscount+1)/2);
mb_put_uint16le(mbp, ntp->nt_function);
if (totscount) {
error = md_get_mbuf(&mbsetup, totscount, &m);
SMBSDEBUG("%d:%d:%d\n", error, totscount, vcp->vc_txmax);
if (error)
goto free_rqp;
mb_put_mbuf(mbp, m);
if (totscount & 1)
mb_put_uint8(mbp, 0);
}
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
check_len = mb_fixhdr(mbp);
if (txpcount) {
mb_put_mem(mbp, NULL, ALIGN4(check_len) - check_len, MB_MZERO);
error = md_get_mbuf(&mbparam, txpcount, &m);
SMBSDEBUG("%d:%d:%d\n", error, txpcount, vcp->vc_txmax);
if (error)
goto free_rqp;
mb_put_mbuf(mbp, m);
}
check_len = mb_fixhdr(mbp);
if (txdcount) {
mb_put_mem(mbp, NULL, ALIGN4(check_len) - check_len, MB_MZERO);
error = md_get_mbuf(&mbdata, txdcount, &m);
if (error)
goto free_rqp;
mb_put_mbuf(mbp, m);
}
smb_rq_bend(rqp);
rqp->sr_flags |= SMBR_ASYNC;
rqp->sr_callback_args = nt_callback_args;
rqp->sr_callback = nt_callback;
ntp->nt_rq = rqp;
error = smb_iod_rq_enqueue(rqp);
if (!error)
return 0;
free_rqp:
ntp->nt_rq = NULL;
smb_rq_done(rqp);
return error;
}