#include <smbsrv/smb_kproto.h>
#define MALLOC_QUANTUM 80
#define DECODE_NO_ERROR 0
#define DECODE_NO_MORE_DATA 1
#define DECODE_ALLOCATION_ERROR 2
#define DECODE_CONVERSION_ERROR 3
static int mbc_marshal_make_room(mbuf_chain_t *, int32_t);
static void mbc_marshal_store_byte(mbuf_chain_t *, uint8_t);
static int mbc_marshal_put_char(mbuf_chain_t *mbc, uint8_t);
static int mbc_marshal_put_short(mbuf_chain_t *mbc, uint16_t);
static int mbc_marshal_put_long(mbuf_chain_t *mbc, uint32_t);
static int mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t);
static int mbc_marshal_put_oem_string(mbuf_chain_t *, char *, int);
static int mbc_marshal_put_unicode_string(mbuf_chain_t *, char *, int);
static int mbc_marshal_put_uio(mbuf_chain_t *, struct uio *);
static int mbc_marshal_put_mbufs(mbuf_chain_t *mbc, mbuf_t *m);
static int mbc_marshal_put_mbuf_chain(mbuf_chain_t *mbc, mbuf_chain_t *nmbc);
static uint8_t mbc_marshal_fetch_byte(mbuf_chain_t *mbc);
static int mbc_marshal_get_char(mbuf_chain_t *mbc, uint8_t *data);
static int mbc_marshal_get_short(mbuf_chain_t *mbc, uint16_t *data);
static int mbc_marshal_get_long(mbuf_chain_t *mbc, uint32_t *data);
static uint64_t qswap(uint64_t ll);
static int mbc_marshal_get_odd_long_long(mbuf_chain_t *mbc, uint64_t *data);
static int mbc_marshal_get_long_long(mbuf_chain_t *mbc, uint64_t *data);
static int mbc_marshal_get_oem_string(smb_request_t *, mbuf_chain_t *,
char **, int);
static int mbc_marshal_get_unicode_string(smb_request_t *, mbuf_chain_t *,
char **, int);
static int mbc_marshal_get_mbufs(mbuf_chain_t *, int32_t, mbuf_t **);
static int mbc_marshal_get_mbuf_chain(mbuf_chain_t *, int32_t, mbuf_chain_t *);
static int mbc_marshal_get_uio(mbuf_chain_t *, struct uio *);
static int mbc_marshal_get_skip(mbuf_chain_t *, uint_t);
int
smb_mbc_vdecodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
{
uint8_t c;
uint8_t cval;
uint8_t *cvalp;
char **charpp;
uint16_t wval;
uint16_t *wvalp;
uint32_t *lvalp;
uint64_t *llvalp;
smb_vdb_t *vdp;
smb_request_t *sr = NULL;
uint32_t lval;
int unicode = 0;
int repc;
boolean_t repc_specified;
while ((c = *fmt++) != 0) {
repc_specified = B_FALSE;
repc = 1;
if ('0' <= c && c <= '9') {
repc = 0;
do {
repc = repc * 10 + c - '0';
c = *fmt++;
} while ('0' <= c && c <= '9');
repc_specified = B_TRUE;
} else if (c == '#') {
repc = va_arg(ap, int);
c = *fmt++;
repc_specified = B_TRUE;
}
switch (c) {
case '%':
sr = va_arg(ap, struct smb_request *);
if (sr->session->dialect >= SMB_VERS_2_BASE) {
unicode = 1;
break;
}
unicode = sr->smb_flg2 & SMB_FLAGS2_UNICODE;
break;
case 'C':
if (mbc_marshal_get_mbuf_chain(mbc, repc,
va_arg(ap, mbuf_chain_t *)) != 0)
return (-1);
break;
case 'm':
if (mbc_marshal_get_mbufs(mbc, repc,
va_arg(ap, mbuf_t **)) != 0)
return (-1);
break;
case 'M':
if (mbc_marshal_get_long(mbc, &lval) != 0)
return (-1);
if (lval != 0x424D53FF)
return (-1);
break;
case 'N':
if (mbc_marshal_get_long(mbc, &lval) != 0)
return (-1);
if (lval != 0x424D53FE)
return (-1);
break;
case 'b':
case 'c':
cvalp = va_arg(ap, uint8_t *);
if (MBC_ROOM_FOR(mbc, repc) == 0)
return (-1);
while (repc-- > 0)
*cvalp++ = mbc_marshal_fetch_byte(mbc);
break;
case 'w':
wvalp = va_arg(ap, uint16_t *);
while (repc-- > 0)
if (mbc_marshal_get_short(mbc, wvalp++) != 0)
return (-1);
break;
case 'l':
lvalp = va_arg(ap, uint32_t *);
while (repc-- > 0)
if (mbc_marshal_get_long(mbc, lvalp++) != 0)
return (-1);
break;
case 'q':
llvalp = va_arg(ap, uint64_t *);
while (repc-- > 0)
if (mbc_marshal_get_long_long(
mbc, llvalp++) != 0)
return (-1);
break;
case 'Q':
llvalp = va_arg(ap, uint64_t *);
while (repc-- > 0)
if (mbc_marshal_get_odd_long_long(
mbc, llvalp++) != 0)
return (-1);
break;
case 'B':
vdp = va_arg(ap, struct vardata_block *);
vdp->vdb_tag = 0;
vdp->vdb_len = repc;
vdp->vdb_uio.uio_iov = &vdp->vdb_iovec[0];
vdp->vdb_uio.uio_iovcnt = MAX_IOVEC;
vdp->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
vdp->vdb_uio.uio_resid = repc;
if (mbc_marshal_get_uio(mbc, &vdp->vdb_uio) != 0)
return (-1);
break;
case 'D':
case 'V':
vdp = va_arg(ap, struct vardata_block *);
if (mbc_marshal_get_char(mbc, &vdp->vdb_tag) != 0)
return (-1);
if (mbc_marshal_get_short(mbc, &wval) != 0)
return (-1);
vdp->vdb_len = (uint32_t)wval;
vdp->vdb_uio.uio_iov = &vdp->vdb_iovec[0];
vdp->vdb_uio.uio_iovcnt = MAX_IOVEC;
vdp->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
vdp->vdb_uio.uio_resid = vdp->vdb_len;
if (vdp->vdb_len != 0) {
if (mbc_marshal_get_uio(mbc,
&vdp->vdb_uio) != 0)
return (-1);
}
break;
case 'L':
if (mbc_marshal_get_char(mbc, &cval) != 0)
return (-1);
if (cval != 2)
return (-1);
goto oem_conversion;
case 'A':
case 'S':
if (mbc_marshal_get_char(mbc, &cval) != 0)
return (-1);
if (((c == 'A' || c == 'S') && cval != 4) ||
(c == 'L' && cval != 2))
return (-1);
case 'u':
if (unicode)
goto unicode_translation;
case 's':
oem_conversion:
ASSERT(sr != NULL);
charpp = va_arg(ap, char **);
if (!repc_specified)
repc = 0;
if (mbc_marshal_get_oem_string(sr,
mbc, charpp, repc) != 0)
return (-1);
break;
case 'U':
unicode_translation:
ASSERT(sr != 0);
charpp = va_arg(ap, char **);
if (!repc_specified)
repc = 0;
if (mbc_marshal_get_unicode_string(sr,
mbc, charpp, repc) != 0)
return (-1);
break;
case 'Y':
lvalp = va_arg(ap, uint32_t *);
while (repc-- > 0) {
short d, t;
if (mbc_marshal_get_short(mbc,
(uint16_t *)&t) != 0)
return (-1);
if (mbc_marshal_get_short(mbc,
(uint16_t *)&d) != 0)
return (-1);
*lvalp++ = smb_time_dos_to_unix(d, t);
}
break;
case 'y':
lvalp = va_arg(ap, uint32_t *);
while (repc-- > 0) {
short d, t;
if (mbc_marshal_get_short(mbc,
(uint16_t *)&d) != 0)
return (-1);
if (mbc_marshal_get_short(mbc,
(uint16_t *)&t) != 0)
return (-1);
*lvalp++ = smb_time_dos_to_unix(d, t);
}
break;
case ',':
if (unicode)
repc *= 2;
case '.':
if (mbc_marshal_get_skip(mbc, repc) != 0)
return (-1);
break;
default:
ASSERT(0);
return (-1);
}
}
return (0);
}
int
smb_mbc_decodef(mbuf_chain_t *mbc, const char *fmt, ...)
{
int xx;
va_list ap;
va_start(ap, fmt);
xx = smb_mbc_vdecodef(mbc, fmt, ap);
va_end(ap);
return (xx);
}
int
smb_mbc_peek(mbuf_chain_t *mbc, int offset, const char *fmt, ...)
{
mbuf_chain_t tmp;
va_list ap;
int xx;
va_start(ap, fmt);
(void) MBC_SHADOW_CHAIN(&tmp, mbc, offset, mbc->max_bytes - offset);
xx = smb_mbc_vdecodef(&tmp, fmt, ap);
va_end(ap);
return (xx);
}
int
smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
{
char *charp;
uint8_t *cvalp;
timestruc_t *tvp;
smb_vdb_t *vdp;
smb_request_t *sr = NULL;
uint64_t llval;
int64_t nt_time;
uint32_t lval;
uint_t tag;
int unicode = 0;
int repc;
boolean_t repc_specified;
uint16_t wval;
uint8_t cval;
uint8_t c;
while ((c = *fmt++) != 0) {
repc_specified = B_FALSE;
repc = 1;
if ('0' <= c && c <= '9') {
repc = 0;
do {
repc = repc * 10 + c - '0';
c = *fmt++;
} while ('0' <= c && c <= '9');
repc_specified = B_TRUE;
} else if (c == '#') {
repc = va_arg(ap, int);
c = *fmt++;
repc_specified = B_TRUE;
}
switch (c) {
case '%':
sr = va_arg(ap, struct smb_request *);
if (sr->session->dialect >= SMB_VERS_2_BASE) {
unicode = 1;
break;
}
unicode = sr->smb_flg2 & SMB_FLAGS2_UNICODE;
break;
case 'C':
if (mbc_marshal_put_mbuf_chain(mbc,
va_arg(ap, mbuf_chain_t *)) != 0)
return (DECODE_NO_MORE_DATA);
break;
case 'D':
vdp = va_arg(ap, struct vardata_block *);
if (mbc_marshal_put_char(mbc, 1) != 0)
return (DECODE_NO_MORE_DATA);
if (mbc_marshal_put_short(mbc, vdp->vdb_len) != 0)
return (DECODE_NO_MORE_DATA);
if (mbc_marshal_put_uio(mbc, &vdp->vdb_uio) != 0)
return (DECODE_NO_MORE_DATA);
break;
case 'M':
if (mbc_marshal_put_long(mbc, 0x424D53FF))
return (DECODE_NO_MORE_DATA);
break;
case 'N':
if (mbc_marshal_put_long(mbc, 0x424D53FE))
return (DECODE_NO_MORE_DATA);
break;
case 'T':
tvp = va_arg(ap, timestruc_t *);
nt_time = smb_time_unix_to_nt(tvp);
if (mbc_marshal_put_long_long(mbc, nt_time) != 0)
return (DECODE_NO_MORE_DATA);
break;
case 'V':
vdp = va_arg(ap, struct vardata_block *);
if (mbc_marshal_put_char(mbc, 5) != 0)
return (DECODE_NO_MORE_DATA);
if (mbc_marshal_put_short(mbc, vdp->vdb_len) != 0)
return (DECODE_NO_MORE_DATA);
if (mbc_marshal_put_uio(mbc, &vdp->vdb_uio) != 0)
return (DECODE_NO_MORE_DATA);
break;
case 'b':
while (repc-- > 0) {
cval = va_arg(ap, int);
if (mbc_marshal_put_char(mbc, cval) != 0)
return (DECODE_NO_MORE_DATA);
}
break;
case 'm':
if (mbc_marshal_put_mbufs(mbc,
va_arg(ap, mbuf_t *)) != 0)
return (DECODE_NO_MORE_DATA);
break;
case 'c':
cvalp = va_arg(ap, uint8_t *);
while (repc-- > 0) {
if (mbc_marshal_put_char(mbc,
*cvalp++) != 0)
return (DECODE_NO_MORE_DATA);
}
break;
case 'w':
while (repc-- > 0) {
wval = va_arg(ap, int);
if (mbc_marshal_put_short(mbc, wval) != 0)
return (DECODE_NO_MORE_DATA);
}
break;
case 'l':
while (repc-- > 0) {
lval = va_arg(ap, uint32_t);
if (mbc_marshal_put_long(mbc, lval) != 0)
return (DECODE_NO_MORE_DATA);
}
break;
case 'q':
while (repc-- > 0) {
llval = va_arg(ap, uint64_t);
if (mbc_marshal_put_long_long(mbc, llval) != 0)
return (DECODE_NO_MORE_DATA);
}
break;
case 'L':
tag = 2;
goto oem_conversion;
case 'S':
case 'A':
tag = 4;
goto tagged_str;
case 'P':
tag = 3;
goto tagged_str;
tagged_str:
if (mbc_marshal_put_char(mbc, tag) != 0)
return (DECODE_NO_MORE_DATA);
case 'u':
if (unicode)
goto unicode_translation;
case 's':
oem_conversion:
charp = va_arg(ap, char *);
if (!repc_specified)
repc = 0;
if (mbc_marshal_put_oem_string(mbc,
charp, repc) != 0)
return (DECODE_NO_MORE_DATA);
break;
case 'U':
unicode_translation:
charp = va_arg(ap, char *);
if (!repc_specified)
repc = 0;
if (mbc_marshal_put_unicode_string(mbc,
charp, repc) != 0)
return (DECODE_NO_MORE_DATA);
break;
case 'Y':
while (repc-- > 0) {
uint16_t d, t;
lval = va_arg(ap, uint32_t);
smb_time_unix_to_dos(lval,
(short *)&d, (short *)&t);
if (mbc_marshal_put_short(mbc, t) != 0)
return (DECODE_NO_MORE_DATA);
if (mbc_marshal_put_short(mbc, d) != 0)
return (DECODE_NO_MORE_DATA);
}
break;
case 'y':
while (repc-- > 0) {
uint16_t d, t;
lval = va_arg(ap, uint32_t);
smb_time_unix_to_dos(lval,
(short *)&d, (short *)&t);
if (mbc_marshal_put_short(mbc, d) != 0)
return (DECODE_NO_MORE_DATA);
if (mbc_marshal_put_short(mbc, t) != 0)
return (DECODE_NO_MORE_DATA);
}
break;
case ',':
if (unicode)
repc *= 2;
case '.':
while (repc-- > 0)
if (mbc_marshal_put_char(mbc, 0) != 0)
return (DECODE_NO_MORE_DATA);
break;
default:
ASSERT(0);
return (-1);
}
}
return (0);
}
int
smb_mbc_encodef(mbuf_chain_t *mbc, const char *fmt, ...)
{
int rc;
va_list ap;
va_start(ap, fmt);
rc = smb_mbc_vencodef(mbc, fmt, ap);
va_end(ap);
return (rc);
}
int
smb_mbc_poke(mbuf_chain_t *mbc, int offset, const char *fmt, ...)
{
int len, rc;
mbuf_chain_t tmp;
va_list ap;
if ((len = mbc->max_bytes - offset) < 0)
return (DECODE_NO_MORE_DATA);
rc = MBC_SHADOW_CHAIN(&tmp, mbc, offset, len);
if (rc)
return (DECODE_NO_MORE_DATA);
va_start(ap, fmt);
rc = smb_mbc_vencodef(&tmp, fmt, ap);
va_end(ap);
return (rc);
}
int
smb_mbc_copy(mbuf_chain_t *dst_mbc, const mbuf_chain_t *src_mbc,
int copy_offset, int copy_len)
{
mbuf_t *src_m;
int offset, len;
int rc;
if (copy_len <= 0)
return (0);
if (copy_offset < 0)
return (EINVAL);
if ((copy_offset + copy_len) > src_mbc->max_bytes)
return (EMSGSIZE);
offset = copy_offset;
src_m = src_mbc->chain;
while (src_m && offset >= src_m->m_len) {
offset -= src_m->m_len;
src_m = src_m->m_next;
}
if (src_m == NULL)
return (EFAULT);
len = src_m->m_len - offset;
if (len > copy_len)
len = copy_len;
rc = smb_mbc_put_mem(dst_mbc, src_m->m_data + offset, len);
if (rc != 0)
return (rc);
copy_len -= len;
while (copy_len > 0) {
src_m = src_m->m_next;
if (src_m == NULL)
break;
len = src_m->m_len;
if (len > copy_len)
len = copy_len;
rc = smb_mbc_put_mem(dst_mbc, src_m->m_data, len);
copy_len -= len;
}
return (0);
}
int
smb_mbc_put_mem(mbuf_chain_t *mbc, void *vmem, int mem_len)
{
caddr_t mem = vmem;
mbuf_t *m;
int32_t offset, tlen;
int rc;
if (mem_len <= 0)
return (0);
if ((rc = mbc_marshal_make_room(mbc, mem_len)) != 0)
return (rc);
offset = mbc->chain_offset;
m = mbc->chain;
while (offset >= m->m_len) {
ASSERT(m->m_len > 0);
offset -= m->m_len;
m = m->m_next;
}
tlen = m->m_len - offset;
if (tlen > mem_len)
tlen = mem_len;
bcopy(mem, m->m_data + offset, tlen);
mbc->chain_offset += tlen;
mem += tlen;
mem_len -= tlen;
while (mem_len > 0) {
m = m->m_next;
tlen = m->m_len;
if (tlen > mem_len)
tlen = mem_len;
bcopy(mem, m->m_data, tlen);
mbc->chain_offset += tlen;
mem += tlen;
mem_len -= tlen;
}
return (0);
}
int
smb_mbc_put_align(mbuf_chain_t *mbc, int align)
{
int mask = align - 1;
int padsz;
ASSERT(align > 0 && (align & mask) == 0);
if ((mbc->chain_offset & mask) == 0)
return (0);
padsz = align - (mbc->chain_offset & mask);
return (smb_mbc_encodef(mbc, "#.", padsz));
}
static int
mbc_marshal_make_room(mbuf_chain_t *mbc, int32_t bytes_needed)
{
mbuf_t *m;
mbuf_t *last;
int32_t bytes_available;
bytes_needed += mbc->chain_offset;
if (bytes_needed > mbc->max_bytes)
return (EMSGSIZE);
if ((m = mbc->chain) == NULL) {
MGET(m, M_WAIT, MT_DATA);
m->m_len = 0;
MCLGET(m, M_WAIT);
m->m_data += MH_PREPEND_SPACE;
mbc->chain = m;
}
last = NULL;
while ((m != NULL) && (bytes_needed >= m->m_len)) {
last = m;
bytes_needed -= m->m_len;
m = m->m_next;
}
if ((bytes_needed == 0) || (m != NULL)) {
return (0);
}
m = last;
bytes_needed += m->m_len;
bytes_available = M_SIZE(m) - M_LEADINGSPACE(m);
while ((bytes_needed != 0) && (bytes_needed > bytes_available)) {
m->m_len = bytes_available;
bytes_needed -= m->m_len;
MGET(m->m_next, M_WAIT, MT_DATA);
m = m->m_next;
m->m_len = 0;
MCLGET(m, M_WAIT);
ASSERT(M_LEADINGSPACE(m) == 0);
bytes_available = M_SIZE(m);
}
if (m->m_len <= bytes_needed) {
m->m_len = bytes_needed;
}
return (0);
}
static void
mbc_marshal_store_byte(mbuf_chain_t *mbc, uint8_t data)
{
mbuf_t *m = mbc->chain;
int32_t cur_offset = mbc->chain_offset;
while (cur_offset >= m->m_len) {
cur_offset -= m->m_len;
m = m->m_next;
}
((char *)m->m_data)[cur_offset] = data;
mbc->chain_offset++;
}
static int
mbc_marshal_put_char(mbuf_chain_t *mbc, uint8_t data)
{
if (mbc_marshal_make_room(mbc, sizeof (char)) != 0)
return (DECODE_NO_MORE_DATA);
mbc_marshal_store_byte(mbc, data);
return (0);
}
static int
mbc_marshal_put_short(mbuf_chain_t *mbc, uint16_t data)
{
if (mbc_marshal_make_room(mbc, sizeof (short)))
return (DECODE_NO_MORE_DATA);
mbc_marshal_store_byte(mbc, data);
mbc_marshal_store_byte(mbc, data >> 8);
return (0);
}
static int
mbc_marshal_put_long(mbuf_chain_t *mbc, uint32_t data)
{
if (mbc_marshal_make_room(mbc, sizeof (int32_t)))
return (DECODE_NO_MORE_DATA);
mbc_marshal_store_byte(mbc, data);
mbc_marshal_store_byte(mbc, data >> 8);
mbc_marshal_store_byte(mbc, data >> 16);
mbc_marshal_store_byte(mbc, data >> 24);
return (0);
}
static int
mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t data)
{
if (mbc_marshal_make_room(mbc, sizeof (int64_t)))
return (DECODE_NO_MORE_DATA);
mbc_marshal_store_byte(mbc, data);
mbc_marshal_store_byte(mbc, data >> 8);
mbc_marshal_store_byte(mbc, data >> 16);
mbc_marshal_store_byte(mbc, data >> 24);
mbc_marshal_store_byte(mbc, data >> 32);
mbc_marshal_store_byte(mbc, data >> 40);
mbc_marshal_store_byte(mbc, data >> 48);
mbc_marshal_store_byte(mbc, data >> 56);
return (0);
}
static int
mbc_marshal_put_oem_string(mbuf_chain_t *mbc, char *mbs, int repc)
{
uint8_t *oembuf = NULL;
uint8_t *s;
int oemlen;
int rlen;
int rc;
if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
return (DECODE_NO_MORE_DATA);
if (repc <= 0)
repc = oemlen + 1;
oembuf = smb_mem_zalloc(oemlen + 1);
ASSERT(oembuf != NULL);
rlen = smb_mbstooem(oembuf, mbs, oemlen);
if (rlen < 0) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
if (rlen > oemlen)
rlen = oemlen;
oembuf[rlen] = '\0';
s = oembuf;
while (repc > 0) {
if (mbc_marshal_make_room(mbc, 1)) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
mbc_marshal_store_byte(mbc, *s);
if (*s != '\0')
s++;
repc--;
}
rc = 0;
out:
if (oembuf != NULL)
smb_mem_free(oembuf);
return (rc);
}
static int
mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *mbs, int repc)
{
smb_wchar_t *wcsbuf = NULL;
smb_wchar_t *wp;
smb_wchar_t wchar;
size_t wcslen, wcsbytes;
size_t rlen;
int rc;
if (mbc->chain_offset & 1) {
if (mbc_marshal_make_room(mbc, 1))
return (DECODE_NO_MORE_DATA);
mbc_marshal_store_byte(mbc, 0);
}
wcsbytes = smb_wcequiv_strlen(mbs);
if (wcsbytes == (size_t)-1)
return (DECODE_NO_MORE_DATA);
if (repc <= 0)
repc = wcsbytes + 2;
wcslen = wcsbytes / 2;
wcsbuf = smb_mem_zalloc(wcsbytes + 2);
ASSERT(wcsbuf != NULL);
rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
if (rlen == (size_t)-1) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
if (rlen > wcslen)
rlen = wcslen;
wcsbuf[rlen] = 0;
wp = wcsbuf;
while (repc >= sizeof (smb_wchar_t)) {
if (mbc_marshal_make_room(mbc, sizeof (smb_wchar_t))) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
wchar = LE_IN16(wp);
mbc_marshal_store_byte(mbc, wchar);
mbc_marshal_store_byte(mbc, wchar >> 8);
if (wchar != 0)
wp++;
repc -= sizeof (smb_wchar_t);
}
if (repc > 0) {
if (mbc_marshal_make_room(mbc, 1)) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
mbc_marshal_store_byte(mbc, 0);
}
rc = 0;
out:
if (wcsbuf != NULL)
smb_mem_free(wcsbuf);
return (rc);
}
static void
uiorefnoop(mbuf_t *m)
{
}
static int
mbc_marshal_put_uio(mbuf_chain_t *mbc, struct uio *uio)
{
mbuf_t **t;
mbuf_t *m = NULL;
struct iovec *iov = uio->uio_iov;
int32_t i, iov_cnt = uio->uio_iovcnt;
iov = uio->uio_iov;
t = &mbc->chain;
for (i = 0; i < iov_cnt; i++) {
MGET(m, M_WAIT, MT_DATA);
m->m_ext.ext_buf = iov->iov_base;
m->m_ext.ext_free = uiorefnoop;
m->m_data = m->m_ext.ext_buf;
m->m_flags |= M_EXT;
m->m_len = m->m_ext.ext_size = iov->iov_len;
mbc->max_bytes += m->m_len;
m->m_next = 0;
*t = m;
t = &m->m_next;
iov++;
}
return (0);
}
int smb_mbuf_put_copy_threshold = 128;
static int
mbc_marshal_put_mbufs(mbuf_chain_t *mbc, mbuf_t *mbuf)
{
mbuf_t *m;
int bytes, rc;
bytes = 0;
m = mbuf;
while (m != NULL) {
bytes += m->m_len;
m = m->m_next;
}
if (bytes == 0) {
m_freem(mbuf);
return (0);
}
if (!MBC_ROOM_FOR(mbc, bytes)) {
m_freem(mbuf);
return (EMSGSIZE);
}
if (mbc->chain == NULL) {
mbc->chain = mbuf;
mbc->chain_offset = bytes;
return (0);
}
if (bytes <= smb_mbuf_put_copy_threshold) {
m = mbuf;
while (m != NULL) {
rc = smb_mbc_put_mem(mbc, m->m_data, m->m_len);
ASSERT3S(rc, ==, 0);
m = m_free(m);
}
return (0);
}
smb_mbuf_trim(mbc->chain, mbc->chain_offset);
m = mbc->chain;
while (m->m_next != NULL)
m = m->m_next;
m->m_next = mbuf;
mbc->chain_offset += bytes;
return (0);
}
static int
mbc_marshal_put_mbuf_chain(mbuf_chain_t *mbc, mbuf_chain_t *nmbc)
{
int rc = 0;
if (nmbc->chain != NULL) {
rc = mbc_marshal_put_mbufs(mbc, nmbc->chain);
nmbc->chain = NULL;
}
return (rc);
}
static uint8_t
mbc_marshal_fetch_byte(mbuf_chain_t *mbc)
{
uint8_t data;
mbuf_t *m = mbc->chain;
int32_t offset = mbc->chain_offset;
while (offset >= m->m_len) {
offset -= m->m_len;
m = m->m_next;
}
data = ((uint8_t *)m->m_data)[offset];
mbc->chain_offset++;
return (data);
}
static int
mbc_marshal_get_char(mbuf_chain_t *mbc, uint8_t *data)
{
if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0) {
return (DECODE_NO_MORE_DATA);
}
*data = mbc_marshal_fetch_byte(mbc);
return (0);
}
static int
mbc_marshal_get_short(mbuf_chain_t *mbc, uint16_t *data)
{
uint16_t tmp;
mbuf_t *m = mbc->chain;
int32_t offset = mbc->chain_offset;
if (MBC_ROOM_FOR(mbc, sizeof (short)) == 0) {
return (DECODE_NO_MORE_DATA);
}
while (offset >= m->m_len) {
offset -= m->m_len;
m = m->m_next;
}
if ((m->m_len - offset) >= sizeof (short)) {
*data = LE_IN16(m->m_data + offset);
mbc->chain_offset += sizeof (short);
} else {
tmp = (uint16_t)mbc_marshal_fetch_byte(mbc);
tmp |= ((uint16_t)mbc_marshal_fetch_byte(mbc)) << 8;
*data = tmp;
}
return (0);
}
static int
mbc_marshal_get_long(mbuf_chain_t *mbc, uint32_t *data)
{
uint32_t tmp;
mbuf_t *m = mbc->chain;
int32_t offset = mbc->chain_offset;
if (MBC_ROOM_FOR(mbc, sizeof (int32_t)) == 0) {
return (DECODE_NO_MORE_DATA);
}
while (offset >= m->m_len) {
offset -= m->m_len;
m = m->m_next;
}
if ((m->m_len - offset) >= sizeof (int32_t)) {
*data = LE_IN32(m->m_data + offset);
mbc->chain_offset += sizeof (int32_t);
} else {
tmp = (uint32_t)mbc_marshal_fetch_byte(mbc);
tmp |= ((uint32_t)mbc_marshal_fetch_byte(mbc)) << 8;
tmp |= ((uint32_t)mbc_marshal_fetch_byte(mbc)) << 16;
tmp |= ((uint32_t)mbc_marshal_fetch_byte(mbc)) << 24;
*data = tmp;
}
return (0);
}
static uint64_t
qswap(uint64_t ll)
{
uint64_t v;
v = ll >> 32;
v |= ll << 32;
return (v);
}
static int
mbc_marshal_get_odd_long_long(mbuf_chain_t *mbc, uint64_t *data)
{
uint64_t tmp;
mbuf_t *m = mbc->chain;
int32_t offset = mbc->chain_offset;
if (MBC_ROOM_FOR(mbc, sizeof (int64_t)) == 0) {
return (DECODE_NO_MORE_DATA);
}
while (offset >= m->m_len) {
offset -= m->m_len;
m = m->m_next;
}
if ((m->m_len - offset) >= sizeof (int64_t)) {
*data = qswap(LE_IN64(m->m_data + offset));
mbc->chain_offset += sizeof (int64_t);
} else {
tmp = (uint64_t)mbc_marshal_fetch_byte(mbc) << 32;
tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 40;
tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 48;
tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 56;
tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc);
tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 8;
tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 16;
tmp |= (uint64_t)mbc_marshal_fetch_byte(mbc) << 24;
*(uint64_t *)data = tmp;
}
return (0);
}
static int
mbc_marshal_get_long_long(mbuf_chain_t *mbc, uint64_t *data)
{
uint64_t tmp;
mbuf_t *m = mbc->chain;
int32_t offset = mbc->chain_offset;
if (MBC_ROOM_FOR(mbc, sizeof (int64_t)) == 0) {
return (DECODE_NO_MORE_DATA);
}
while (offset >= m->m_len) {
offset -= m->m_len;
m = m->m_next;
}
if ((m->m_len - offset) >= sizeof (int64_t)) {
*data = LE_IN64(m->m_data + offset);
mbc->chain_offset += sizeof (int64_t);
} else {
tmp = (uint32_t)mbc_marshal_fetch_byte(mbc);
tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 8;
tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 16;
tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 24;
tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 32;
tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 40;
tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 48;
tmp |= ((uint64_t)mbc_marshal_fetch_byte(mbc)) << 56;
*(uint64_t *)data = tmp;
}
return (0);
}
static int
mbc_marshal_get_oem_string(smb_request_t *sr,
mbuf_chain_t *mbc, char **strpp, int max_bytes)
{
char *mbs;
uint8_t *oembuf = NULL;
int oemlen, oemmax;
int mbsmax;
int rlen;
int rc;
if (max_bytes == 0)
max_bytes = 0xffff;
oemlen = 0;
oemmax = MALLOC_QUANTUM;
oembuf = smb_mem_alloc(oemmax);
for (;;) {
uint8_t ch;
if (oemlen >= max_bytes)
break;
if ((oemlen + 2) >= oemmax) {
oemmax += MALLOC_QUANTUM;
oembuf = smb_mem_realloc(oembuf, oemmax);
}
if (mbc_marshal_get_char(mbc, &ch) != 0) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
if (ch == 0)
break;
oembuf[oemlen++] = ch;
}
oembuf[oemlen] = '\0';
mbsmax = oemlen * 2;
mbs = smb_srm_zalloc(sr, mbsmax + 1);
ASSERT(mbs != NULL);
rlen = smb_oemtombs(mbs, oembuf, mbsmax);
if (rlen < 0) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
if (rlen > mbsmax)
rlen = mbsmax;
mbs[rlen] = '\0';
*strpp = mbs;
rc = 0;
out:
if (oembuf != NULL)
smb_mem_free(oembuf);
return (rc);
}
static int
mbc_marshal_get_unicode_string(smb_request_t *sr,
mbuf_chain_t *mbc, char **strpp, int max_bytes)
{
char *mbs;
uint16_t *wcsbuf = NULL;
int wcslen;
int wcsmax;
size_t mbsmax;
size_t rlen;
int rc;
if (max_bytes == 0)
max_bytes = 0xffff;
if (mbc->chain_offset & 1) {
if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0)
return (DECODE_NO_MORE_DATA);
mbc->chain_offset++;
}
wcslen = 0;
wcsmax = MALLOC_QUANTUM;
wcsbuf = smb_mem_alloc(wcsmax);
for (;;) {
uint16_t wchar;
if ((wcslen * 2) >= max_bytes)
break;
if (((wcslen * 2) + 4) >= wcsmax) {
wcsmax += MALLOC_QUANTUM;
wcsbuf = smb_mem_realloc(wcsbuf, wcsmax);
}
if (mbc_marshal_get_short(mbc, &wchar) != 0) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
if (wchar == 0)
break;
LE_OUT16(wcsbuf + wcslen, wchar);
wcslen++;
}
wcsbuf[wcslen] = 0;
mbsmax = wcslen * MTS_MB_CUR_MAX;
mbs = smb_srm_zalloc(sr, mbsmax + 1);
ASSERT(mbs != NULL);
rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
if (rlen == (size_t)-1) {
rc = DECODE_NO_MORE_DATA;
goto out;
}
if (rlen > mbsmax)
rlen = mbsmax;
mbs[rlen] = '\0';
*strpp = mbs;
rc = 0;
out:
if (wcsbuf != NULL)
smb_mem_free(wcsbuf);
return (rc);
}
static int
mbc_marshal_get_mbufs(mbuf_chain_t *mbc, int32_t bytes, mbuf_t **m)
{
*m = NULL;
if (MBC_ROOM_FOR(mbc, bytes) == 0) {
return (DECODE_NO_MORE_DATA);
}
return (-1);
}
static int
mbc_marshal_get_mbuf_chain(mbuf_chain_t *mbc, int32_t bytes, mbuf_chain_t *nmbc)
{
int rc;
mbuf_t *m;
if (bytes == 0) {
bytes = mbc->max_bytes - mbc->chain_offset;
}
MBC_SETUP(nmbc, mbc->max_bytes);
if ((rc = mbc_marshal_get_mbufs(mbc, bytes, &m)) != 0) {
if (m)
m_freem(m);
return (rc);
}
nmbc->chain = m;
while (m != 0) {
bytes += m->m_len;
m = m->m_next;
}
nmbc->max_bytes = bytes;
return (0);
}
static int
mbc_marshal_get_uio(mbuf_chain_t *mbc, struct uio *uio)
{
int i, offset;
int32_t bytes = uio->uio_resid;
int32_t remainder;
struct iovec *iov;
mbuf_t *m;
if (bytes != 0) {
iov = uio->uio_iov;
uio->uio_segflg = UIO_SYSSPACE;
uio->uio_extflg = UIO_COPY_DEFAULT;
if (MBC_ROOM_FOR(mbc, bytes) == 0) {
return (DECODE_NO_MORE_DATA);
}
m = mbc->chain;
offset = mbc->chain_offset;
while (offset >= m->m_len) {
offset -= m->m_len;
m = m->m_next;
ASSERT((offset == 0) || (offset && m));
}
for (i = 0; (bytes > 0) && (i < uio->uio_iovcnt); i++) {
iov[i].iov_base = &m->m_data[offset];
remainder = m->m_len - offset;
if (remainder >= bytes) {
iov[i].iov_len = bytes;
mbc->chain_offset += bytes;
uio->uio_iovcnt = i + 1;
return (0);
}
iov[i].iov_len = remainder;
mbc->chain_offset += remainder;
bytes -= remainder;
m = m->m_next;
offset = 0;
}
return (DECODE_NO_MORE_DATA);
}
return (0);
}
static int
mbc_marshal_get_skip(mbuf_chain_t *mbc, uint_t skip)
{
if (MBC_ROOM_FOR(mbc, skip) == 0)
return (DECODE_NO_MORE_DATA);
mbc->chain_offset += skip;
return (0);
}