#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/lock.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/random.h>
#include <sys/note.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <netsmb/smb_osdep.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>
#define STYPE_LEN 8
struct smb_dialect {
int d_id;
const char *d_name;
};
static struct smb_dialect smb_dialects[3] = {
{SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"},
{SMB_DIALECT_NTLM0_12, "NT LM 0.12"},
#define NDIALECT_SMB1 2
{SMB_DIALECT_SMB2_FF, "SMB 2.???"},
#define NDIALECT_SMB2 3
};
static const uint32_t smb_clnt_caps_mask =
SMB_CAP_UNICODE |
SMB_CAP_LARGE_FILES |
SMB_CAP_NT_SMBS |
SMB_CAP_STATUS32 |
SMB_CAP_EXT_SECURITY;
int smb_timo_notice = 15;
int smb_timo_default = 30;
int smb_timo_logon = 45;
int smb_timo_open = 45;
int smb_timo_read = 45;
int smb_timo_write = 60;
int smb_timo_append = 90;
int
smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
{
smb_sopt_t *sv = &vcp->vc_sopt;
smbioc_ssn_work_t *wk = &vcp->vc_work;
struct smb_rq *rqp = NULL;
struct mbchain *mbp = NULL;
struct mdchain *mdp = NULL;
struct smb_dialect *dp;
int err, sblen, tlen;
uint8_t wc, eklen;
uint16_t dindex, bc;
uint16_t ndialects;
boolean_t will_sign = B_FALSE;
vcp->vc_hflags =
SMB_FLAGS_CASELESS |
SMB_FLAGS_CANONICAL_PATHNAMES;
vcp->vc_hflags2 =
SMB_FLAGS2_KNOWS_LONG_NAMES |
SMB_FLAGS2_KNOWS_EAS |
SMB_FLAGS2_IS_LONG_NAME |
SMB_FLAGS2_EXT_SEC |
SMB_FLAGS2_ERR_STATUS |
SMB_FLAGS2_UNICODE;
vcp->vc_smbuid = 0;
bzero(sv, sizeof (*sv));
sv->sv_maxmux = 1;
sv->sv_maxvcs = 1;
sv->sv_maxtx = 1024;
if (vcp->vc_ssn.ssn_maxver >= SMB2_DIALECT_BASE)
ndialects = NDIALECT_SMB2;
else
ndialects = NDIALECT_SMB1;
err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
if (err)
return (err);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
for (dindex = 0; dindex < ndialects; dindex++) {
dp = &smb_dialects[dindex];
mb_put_uint8(mbp, SMB_DT_DIALECT);
tlen = strlen(dp->d_name) + 1;
mb_put_mem(mbp, dp->d_name, tlen, MB_MSYSTEM);
}
smb_rq_bend(rqp);
err = smb_rq_internal(rqp, smb_timo_default);
if (err == EPROTO) {
err = smb2_parse_smb1nego_resp(rqp);
smb_rq_done(rqp);
return (err);
}
if (err) {
SMBSDEBUG("smb_rq_internal, err %d", err);
goto errout;
}
if (rqp->sr_error != NT_STATUS_SUCCESS) {
err = ENOTSUP;
goto errout;
}
smb_rq_getreply(rqp, &mdp);
(void) md_get_uint8(mdp, &wc);
err = md_get_uint16le(mdp, &dindex);
if (err != 0)
goto errout;
if (dindex >= ndialects) {
SMBERROR("Invalid dialect index from server: %s\n",
vcp->vc_srvname);
err = EBADRPC;
goto errout;
}
dp = smb_dialects + dindex;
sv->sv_proto = dp->d_id;
SMBSDEBUG("Dialect %s", dp->d_name);
if (dp->d_id < SMB_DIALECT_NTLM0_12) {
SMBSDEBUG("old dialect %s", dp->d_name);
goto errout;
}
if (wc != 17) {
SMBSDEBUG("bad wc %d", (int)wc);
goto errout;
}
md_get_uint8(mdp, &sv->sv_sm);
md_get_uint16le(mdp, &sv->sv_maxmux);
md_get_uint16le(mdp, &sv->sv_maxvcs);
md_get_uint32le(mdp, &sv->sv_maxtx);
md_get_uint32le(mdp, &sv->sv_maxraw);
md_get_uint32le(mdp, &sv->sv_skey);
md_get_uint32le(mdp, &sv->sv_caps);
md_get_mem(mdp, NULL, 8, MB_MSYSTEM);
md_get_uint16le(mdp, (uint16_t *)&sv->sv_tz);
md_get_uint8(mdp, &eklen);
err = md_get_uint16le(mdp, &bc);
if (err)
goto errout;
if (sv->sv_sm & SMB_SM_SIGS_REQUIRE) {
will_sign = B_TRUE;
} else if (sv->sv_sm & SMB_SM_SIGS) {
if (vcp->vc_vopt & SMBVOPT_SIGNING_ENABLED)
will_sign = B_TRUE;
} else {
if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) {
SMBERROR("Client requires signing "
"but server has it disabled.");
err = EBADRPC;
goto errout;
}
}
if (vcp->vc_vopt & SMBVOPT_ANONYMOUS) {
will_sign = B_FALSE;
}
SMBSDEBUG("Security signatures: %d", (int)will_sign);
if (will_sign) {
vcp->vc_flags |= SMBV_SIGNING;
vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
sv->sv_caps &= ~(SMB_CAP_LARGE_READX | SMB_CAP_LARGE_WRITEX);
}
if ((sv->sv_caps & SMB_CAP_UNICODE) != 0)
vcp->vc_flags |= SMBV_UNICODE;
else
vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
if ((sv->sv_caps & SMB_CAP_STATUS32) == 0) {
vcp->vc_hflags2 &= ~SMB_FLAGS2_ERR_STATUS;
}
if ((sv->sv_caps & SMB_CAP_NT_SMBS) == 0) {
cmn_err(CE_NOTE, "%s does not support SMB_CAP_NT_SMBS",
vcp->vc_srvname);
}
if (sv->sv_caps & SMB_CAP_EXT_SECURITY) {
SMBSDEBUG("Ext.Security: yes");
err = md_get_mem(mdp, NULL, SMB_GUIDLEN, MB_MSYSTEM);
if (err)
goto errout;
sblen = (int)bc - SMB_GUIDLEN;
if (sblen < 0)
goto errout;
} else {
SMBSDEBUG("Ext.Security: no");
err = ENOTSUP;
goto errout;
}
if (wk->wk_u_auth_rlen < sblen) {
SMBSDEBUG("vc_auth_rbuf too small");
wk->wk_u_auth_rlen = sblen;
err = EMSGSIZE;
goto errout;
}
wk->wk_u_auth_rlen = sblen;
err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr, sblen, MB_MUSER);
if (err)
goto errout;
if (sv->sv_maxmux < 1)
sv->sv_maxmux = 1;
if (sv->sv_maxvcs < 1)
sv->sv_maxvcs = 1;
if (sv->sv_maxtx < 1024)
sv->sv_maxtx = 1024;
vcp->vc_txmax = sv->sv_maxtx;
if (vcp->vc_txmax > 0x8000)
vcp->vc_txmax = 0x8000;
tlen = vcp->vc_txmax - 68;
tlen &= 0xFE00;
vcp->vc_rwmax = tlen;
vcp->vc_rxmax = tlen;
vcp->vc_wxmax = tlen;
sv->sv_caps &= smb_clnt_caps_mask;
smb_rq_done(rqp);
return (0);
errout:
smb_rq_done(rqp);
if (err == 0)
err = EBADRPC;
return (err);
}
static const char NativeOS[] = "illumos";
static const char LanMan[] = "NETSMB";
int
smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
{
smb_sopt_t *sv = &vcp->vc_sopt;
smbioc_ssn_work_t *wk = &vcp->vc_work;
struct smb_rq *rqp = NULL;
struct mbchain *mbp = NULL;
struct mdchain *mdp = NULL;
char *sb;
int err, ret;
uint32_t caps;
uint16_t action, bc, sblen;
uint8_t wc;
caps = sv->sv_caps;
sb = wk->wk_u_auth_wbuf.lp_ptr;
sblen = (uint16_t)wk->wk_u_auth_wlen;
err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX,
scred, &rqp);
if (err != 0) {
ret = err;
goto out;
}
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, 0xff);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, sv->sv_maxtx);
mb_put_uint16le(mbp, sv->sv_maxmux);
mb_put_uint16le(mbp, 1);
mb_put_uint32le(mbp, sv->sv_skey);
mb_put_uint16le(mbp, sblen);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, caps);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
err = mb_put_mem(mbp, sb, sblen, MB_MUSER);
if (err != 0) {
ret = err;
goto out;
}
(void) smb_put_dstring(mbp, vcp, NativeOS, SMB_CS_NONE);
(void) smb_put_dstring(mbp, vcp, LanMan, SMB_CS_NONE);
smb_rq_bend(rqp);
ret = smb_rq_internal(rqp, smb_timo_logon);
if (ret != 0)
goto out;
switch (rqp->sr_error) {
case NT_STATUS_SUCCESS:
break;
case NT_STATUS_MORE_PROCESSING_REQUIRED:
ret = EINPROGRESS;
break;
default:
ret = EAUTH;
goto out;
}
if (vcp->vc_smbuid == 0)
vcp->vc_smbuid = rqp->sr_rpuid;
smb_rq_getreply(rqp, &mdp);
err = md_get_uint8(mdp, &wc);
if (err != 0)
wc = 0;
if (wc != 4) {
ret = EBADRPC;
goto out;
}
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, &action);
md_get_uint16le(mdp, &sblen);
md_get_uint16le(mdp, &bc);
if (sblen == 0 || sblen > bc) {
ret = EBADRPC;
goto out;
}
if (sblen > wk->wk_u_auth_rlen) {
ret = EBADRPC;
goto out;
}
sb = wk->wk_u_auth_rbuf.lp_ptr;
err = md_get_mem(mdp, sb, sblen, MB_MUSER);
if (err) {
ret = EBADRPC;
goto out;
}
out:
if (err != 0 && err != EINPROGRESS) {
vcp->vc_smbuid = 0;
}
if (rqp)
smb_rq_done(rqp);
return (ret);
}
int
smb_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred)
{
struct smb_rq *rqp;
struct mbchain *mbp;
int error;
if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
return (0);
error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
if (error)
return (error);
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
rqp->sr_flags |= SMBR_NORECONNECT;
error = smb_rq_internal(rqp, 5);
smb_rq_done(rqp);
return (error);
}
static const char *
smb_share_typename(uint32_t stype)
{
const char *p;
switch (stype) {
case STYPE_DISKTREE:
p = "A:";
break;
case STYPE_PRINTQ:
p = "LPT1:";
break;
case STYPE_DEVICE:
p = "COMM";
break;
case STYPE_IPC:
p = "IPC";
break;
case STYPE_UNKNOWN:
default:
p = "?????";
break;
}
return (p);
}
static uint32_t
smb_share_parsetype(char *name)
{
int stype;
switch (*name) {
case 'A':
stype = STYPE_DISKTREE;
break;
case 'C':
stype = STYPE_DEVICE;
break;
case 'I':
stype = STYPE_IPC;
break;
case 'L':
stype = STYPE_PRINTQ;
break;
default:
stype = STYPE_UNKNOWN;
break;
}
return (stype);
}
int
smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
{
struct smb_vc *vcp;
struct smb_rq *rqp = NULL;
struct mbchain *mbp;
struct mdchain *mdp;
const char *tname;
char *pbuf, *unc_name = NULL;
int error, tlen, plen, unc_len;
uint16_t bcnt, options;
uint8_t wc;
char stype_str[STYPE_LEN];
vcp = SSTOVC(ssp);
error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX,
scred, &rqp);
if (error)
return (error);
unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name);
unc_name = kmem_alloc(unc_len, KM_SLEEP);
(void) snprintf(unc_name, unc_len, "\\\\%s\\%s",
vcp->vc_srvname, ssp->ss_name);
SMBSDEBUG("unc_name: \"%s\"", unc_name);
pbuf = ssp->ss_pass;
plen = strlen(pbuf) + 1;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, plen);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
error = mb_put_mem(mbp, pbuf, plen, MB_MSYSTEM);
if (error)
goto out;
error = smb_put_dstring(mbp, vcp, unc_name, SMB_CS_NONE);
if (error)
goto out;
tname = smb_share_typename(ssp->ss_use);
tlen = strlen(tname) + 1;
error = mb_put_mem(mbp, tname, tlen, MB_MSYSTEM);
if (error)
goto out;
smb_rq_bend(rqp);
rqp->sr_flags |= SMBR_NOINTR_RECV;
error = smb_rq_simple(rqp);
SMBSDEBUG("%d\n", error);
if (error) {
SMBERROR("(%s) failed, status=0x%x",
unc_name, rqp->sr_error);
goto out;
}
smb_rq_getreply(rqp, &mdp);
md_get_uint8(mdp, &wc);
if (wc != 3 && wc != 7) {
error = EBADRPC;
goto out;
}
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, &options);
if (wc == 7) {
md_get_uint32le(mdp, NULL);
md_get_uint32le(mdp, NULL);
}
error = md_get_uint16le(mdp, &bcnt);
if (error)
goto out;
tlen = STYPE_LEN;
bzero(stype_str, tlen--);
if (tlen > bcnt)
tlen = bcnt;
md_get_mem(mdp, stype_str, tlen, MB_MSYSTEM);
stype_str[tlen] = '\0';
ssp->ss_type = smb_share_parsetype(stype_str);
SMB_SS_LOCK(ssp);
ssp->ss_tid = rqp->sr_rptid;
ssp->ss_vcgenid = vcp->vc_genid;
ssp->ss_options = options;
ssp->ss_flags |= SMBS_CONNECTED;
SMB_SS_UNLOCK(ssp);
out:
if (unc_name)
kmem_free(unc_name, unc_len);
smb_rq_done(rqp);
return (error);
}
int
smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
{
struct smb_vc *vcp;
struct smb_rq *rqp;
int error;
if (ssp->ss_tid == SMB_TID_UNKNOWN)
return (0);
vcp = SSTOVC(ssp);
error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
if (error)
return (error);
rqp->sr_share = ssp;
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
error = smb_rq_simple_timed(rqp, 5);
SMBSDEBUG("%d\n", error);
smb_rq_done(rqp);
ssp->ss_tid = SMB_TID_UNKNOWN;
return (error);
}
int
smb1_smb_ntcreate(
struct smb_share *ssp,
struct mbchain *name_mb,
uint32_t cr_flags,
uint32_t req_acc,
uint32_t efa,
uint32_t share_acc,
uint32_t open_disp,
uint32_t createopt,
uint32_t impersonate,
struct smb_cred *scrp,
uint16_t *fidp,
uint32_t *cr_act_p,
struct smbfattr *fap)
{
struct smb_rq rq, *rqp = &rq;
struct smb_vc *vcp = SSTOVC(ssp);
struct mbchain *mbp;
struct mdchain *mdp;
struct smbfattr fa;
uint64_t llongint;
uint32_t longint, createact;
uint16_t fid;
uint8_t wc;
int error;
bzero(&fa, sizeof (fa));
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_NT_CREATE_ANDX, scrp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, name_mb->mb_count);
mb_put_uint32le(mbp, cr_flags);
mb_put_uint32le(mbp, 0);
mb_put_uint32le(mbp, req_acc);
mb_put_uint64le(mbp, 0);
mb_put_uint32le(mbp, efa);
mb_put_uint32le(mbp, share_acc);
mb_put_uint32le(mbp, open_disp);
mb_put_uint32le(mbp, createopt);
mb_put_uint32le(mbp, impersonate);
mb_put_uint8(mbp, 0);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
if (SMB_UNICODE_STRINGS(vcp))
mb_put_padbyte(mbp);
mb_put_mbuf(mbp, name_mb->mb_top);
bzero(name_mb, sizeof (*name_mb));
smb_rq_bend(rqp);
rqp->sr_flags |= SMBR_NOINTR_RECV;
error = smb_rq_simple_timed(rqp, smb_timo_open);
if (error)
goto done;
smb_rq_getreply(rqp, &mdp);
error = md_get_uint8(mdp, &wc);
if (error)
goto done;
if (wc != 26 && wc < 34) {
error = EBADRPC;
goto done;
}
md_get_uint8(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint16le(mdp, &fid);
md_get_uint32le(mdp, &createact);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_createtime);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_atime);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_mtime);
md_get_uint64le(mdp, &llongint);
smb_time_NT2local(llongint, &fa.fa_ctime);
md_get_uint32le(mdp, &longint);
fa.fa_attr = longint;
md_get_uint64le(mdp, &llongint);
fa.fa_allocsz = llongint;
md_get_uint64le(mdp, &llongint);
fa.fa_size = llongint;
error = md_get_uint16le(mdp, NULL);
done:
smb_rq_done(rqp);
if (error)
return (error);
*fidp = fid;
if (cr_act_p)
*cr_act_p = createact;
if (fap)
*fap = fa;
return (0);
}
int
smb1_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
struct smb_cred *scrp)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
long time;
int error;
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scrp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, fid);
if (mtime) {
int sv_tz = SSTOVC(ssp)->vc_sopt.sv_tz;
smb_time_local2server(mtime, sv_tz, &time);
} else {
time = 0;
}
mb_put_uint32le(mbp, time);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
return (error);
}
int
smb_smb_open_prjob(
struct smb_share *ssp,
char *title,
uint16_t setuplen,
uint16_t mode,
struct smb_cred *scrp,
uint16_t *fidp)
{
struct smb_rq rq, *rqp = &rq;
struct smb_vc *vcp = SSTOVC(ssp);
struct mbchain *mbp;
struct mdchain *mdp;
uint16_t fid;
uint8_t wc;
int error;
error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN_PRINT_FILE, scrp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, setuplen);
mb_put_uint16le(mbp, mode);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
error = smb_put_dstring(mbp, vcp, title, SMB_CS_NONE);
smb_rq_bend(rqp);
if (error)
goto done;
rqp->sr_flags |= SMBR_NOINTR_RECV;
error = smb_rq_simple_timed(rqp, smb_timo_open);
if (error)
goto done;
smb_rq_getreply(rqp, &mdp);
error = md_get_uint8(mdp, &wc);
if (error || wc < 1) {
error = EBADRPC;
goto done;
}
error = md_get_uint16le(mdp, &fid);
done:
smb_rq_done(rqp);
if (error)
return (error);
*fidp = fid;
return (0);
}
int
smb_smb_close_prjob(struct smb_share *ssp, uint16_t fid,
struct smb_cred *scrp)
{
struct smb_rq rq, *rqp = &rq;
struct mbchain *mbp;
int error;
error = smb_rq_init(rqp, SSTOCP(ssp),
SMB_COM_CLOSE_PRINT_FILE, scrp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, fid);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
return (error);
}
int
smb_smb_readx(smb_fh_t *fhp, uint32_t *lenp,
uio_t *uiop, smb_cred_t *scred, int timo)
{
struct smb_share *ssp = FHTOSS(fhp);
struct smb_rq *rqp;
struct mbchain *mbp;
struct mdchain *mdp;
int error;
uint32_t offlo, offhi, rlen;
uint16_t lenhi, lenlo, off, doff;
uint8_t wc;
lenhi = (uint16_t)(*lenp >> 16);
lenlo = (uint16_t)*lenp;
offhi = (uint32_t)(uiop->uio_loffset >> 32);
offlo = (uint32_t)uiop->uio_loffset;
error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, fhp->fh_fid1);
mb_put_uint32le(mbp, offlo);
mb_put_uint16le(mbp, lenlo);
mb_put_uint16le(mbp, 1);
mb_put_uint32le(mbp, lenhi);
mb_put_uint16le(mbp, lenlo);
mb_put_uint32le(mbp, offhi);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
if (timo == 0)
timo = smb_timo_read;
error = smb_rq_simple_timed(rqp, timo);
if (error)
goto out;
smb_rq_getreply(rqp, &mdp);
error = md_get_uint8(mdp, &wc);
if (error)
goto out;
if (wc != 12) {
error = EBADRPC;
goto out;
}
md_get_uint8(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, &lenlo);
md_get_uint16le(mdp, &doff);
md_get_uint16le(mdp, &lenhi);
rlen = (lenhi << 16) | lenlo;
md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
error = md_get_uint16le(mdp, NULL);
if (error)
goto out;
off = SMB_HDRLEN + 3 + (12 * 2);
if (doff > off)
md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
if (rlen == 0) {
*lenp = rlen;
goto out;
}
if (rlen > *lenp) {
SMBSDEBUG("bad server! rlen %d, len %d\n",
rlen, *lenp);
rlen = *lenp;
}
error = md_get_uio(mdp, uiop, rlen);
if (error)
goto out;
*lenp = rlen;
out:
smb_rq_done(rqp);
return (error);
}
int
smb_smb_writex(smb_fh_t *fhp, uint32_t *lenp,
uio_t *uiop, smb_cred_t *scred, int timo)
{
struct smb_share *ssp = FHTOSS(fhp);
struct smb_rq *rqp;
struct mbchain *mbp;
struct mdchain *mdp;
int error;
uint32_t offlo, offhi, rlen;
uint16_t lenhi, lenlo;
uint8_t wc;
lenhi = (uint16_t)(*lenp >> 16);
lenlo = (uint16_t)*lenp;
offhi = (uint32_t)(uiop->uio_loffset >> 32);
offlo = (uint32_t)uiop->uio_loffset;
error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
if (error)
return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, fhp->fh_fid1);
mb_put_uint32le(mbp, offlo);
mb_put_uint32le(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint16le(mbp, lenhi);
mb_put_uint16le(mbp, lenlo);
mb_put_uint16le(mbp, 64);
mb_put_uint32le(mbp, offhi);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint8(mbp, 0);
error = mb_put_uio(mbp, uiop, *lenp);
if (error)
goto out;
smb_rq_bend(rqp);
if (timo == 0)
timo = smb_timo_write;
error = smb_rq_simple_timed(rqp, timo);
if (error)
goto out;
smb_rq_getreply(rqp, &mdp);
error = md_get_uint8(mdp, &wc);
if (error)
goto out;
if (wc != 6) {
error = EBADRPC;
goto out;
}
md_get_uint8(mdp, NULL);
md_get_uint8(mdp, NULL);
md_get_uint16le(mdp, NULL);
md_get_uint16le(mdp, &lenlo);
md_get_uint16le(mdp, NULL);
error = md_get_uint16le(mdp, &lenhi);
if (error)
goto out;
rlen = (lenhi << 16) | lenlo;
*lenp = rlen;
out:
smb_rq_done(rqp);
return (error);
}
static u_int32_t smbechoes = 0;
int
smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
{
struct smb_rq *rqp;
struct mbchain *mbp;
int error;
error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
if (error)
return (error);
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, 1);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
mb_put_uint32le(mbp, atomic_inc_32_nv(&smbechoes));
smb_rq_bend(rqp);
rqp->sr_flags |= SMBR_NORECONNECT;
error = smb_rq_internal(rqp, timo);
SMBSDEBUG("%d\n", error);
smb_rq_done(rqp);
return (error);
}