xref: /onnv-gate/usr/src/common/smbsrv/smb_msgbuf.c (revision 10966:37e5dcdf36d3)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
22*10966SJordan.Brown@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw /*
275331Samw  * Msgbuf buffer management implementation. The smb_msgbuf interface is
285331Samw  * typically used to encode or decode SMB data using sprintf/scanf
295331Samw  * style operations. It contains special handling for the SMB header.
305331Samw  * It can also be used for general purpose encoding and decoding.
315331Samw  */
325331Samw 
335331Samw #include <sys/types.h>
345331Samw #include <sys/varargs.h>
355331Samw #include <sys/byteorder.h>
365331Samw #ifndef _KERNEL
375331Samw #include <stdlib.h>
385331Samw #include <syslog.h>
395331Samw #include <string.h>
405331Samw #include <strings.h>
415331Samw #else
425331Samw #include <sys/sunddi.h>
435331Samw #include <sys/kmem.h>
445331Samw #endif
455331Samw #include <smbsrv/string.h>
465331Samw #include <smbsrv/msgbuf.h>
475331Samw #include <smbsrv/smb.h>
485331Samw 
495331Samw static int buf_decode(smb_msgbuf_t *, char *, va_list ap);
505331Samw static int buf_encode(smb_msgbuf_t *, char *, va_list ap);
515331Samw static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
525331Samw static int smb_msgbuf_chkerc(char *text, int erc);
53*10966SJordan.Brown@Sun.COM static void buf_decode_wcs(smb_wchar_t *, smb_wchar_t *, int wcstrlen);
545331Samw 
555331Samw /*
565331Samw  * Returns the offset or number of bytes used within the buffer.
575331Samw  */
585331Samw size_t
smb_msgbuf_used(smb_msgbuf_t * mb)595331Samw smb_msgbuf_used(smb_msgbuf_t *mb)
605331Samw {
615331Samw 	/*LINTED E_PTRDIFF_OVERFLOW*/
625331Samw 	return (mb->scan - mb->base);
635331Samw }
645331Samw 
655331Samw /*
665331Samw  * Returns the actual buffer size.
675331Samw  */
685331Samw size_t
smb_msgbuf_size(smb_msgbuf_t * mb)695331Samw smb_msgbuf_size(smb_msgbuf_t *mb)
705331Samw {
715331Samw 	return (mb->max);
725331Samw }
735331Samw 
745331Samw uint8_t *
smb_msgbuf_base(smb_msgbuf_t * mb)755331Samw smb_msgbuf_base(smb_msgbuf_t *mb)
765331Samw {
775331Samw 	return (mb->base);
785331Samw }
795331Samw 
805331Samw /*
815331Samw  * Ensure that the scan is aligned on a word (16-bit) boundary.
825331Samw  */
835331Samw void
smb_msgbuf_word_align(smb_msgbuf_t * mb)845331Samw smb_msgbuf_word_align(smb_msgbuf_t *mb)
855331Samw {
865331Samw 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1);
875331Samw }
885331Samw 
895331Samw /*
905331Samw  * Ensure that the scan is aligned on a dword (32-bit) boundary.
915331Samw  */
925331Samw void
smb_msgbuf_dword_align(smb_msgbuf_t * mb)935331Samw smb_msgbuf_dword_align(smb_msgbuf_t *mb)
945331Samw {
955331Samw 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3);
965331Samw }
975331Samw 
985331Samw /*
995331Samw  * Checks whether or not the buffer has space for the amount of data
1005331Samw  * specified. Returns 1 if there is space, otherwise returns 0.
1015331Samw  */
1025331Samw int
smb_msgbuf_has_space(smb_msgbuf_t * mb,size_t size)1035331Samw smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size)
1045331Samw {
1055331Samw 	if (size > mb->max || (mb->scan + size) > mb->end)
1065331Samw 		return (0);
1075331Samw 
1085331Samw 	return (1);
1095331Samw }
1105331Samw 
1115331Samw /*
1125331Samw  * Set flags the smb_msgbuf.
1135331Samw  */
1145331Samw void
smb_msgbuf_fset(smb_msgbuf_t * mb,uint32_t flags)1155331Samw smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags)
1165331Samw {
1175331Samw 	mb->flags |= flags;
1185331Samw }
1195331Samw 
1205331Samw /*
1215331Samw  * Clear flags the smb_msgbuf.
1225331Samw  */
1235331Samw void
smb_msgbuf_fclear(smb_msgbuf_t * mb,uint32_t flags)1245331Samw smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags)
1255331Samw {
1265331Samw 	mb->flags &= ~flags;
1275331Samw }
1285331Samw 
1295331Samw /*
1305331Samw  * smb_msgbuf_init
1315331Samw  *
1325331Samw  * Initialize a smb_msgbuf_t structure based on the buffer and size
1335331Samw  * specified. Both scan and base initially point to the beginning
1345331Samw  * of the buffer and end points to the limit of the buffer. As
1355331Samw  * data is added scan should be incremented to point to the next
1365331Samw  * offset at which data will be written. Max and count are set
1375331Samw  * to the actual buffer size.
1385331Samw  */
1395331Samw void
smb_msgbuf_init(smb_msgbuf_t * mb,uint8_t * buf,size_t size,uint32_t flags)1405331Samw smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags)
1415331Samw {
1425331Samw 	mb->scan = mb->base = buf;
1435331Samw 	mb->max = mb->count = size;
1445331Samw 	mb->end = &buf[size];
1455331Samw 	mb->flags = flags;
1465331Samw 	mb->mlist.next = 0;
1475331Samw }
1485331Samw 
1495331Samw 
1505331Samw /*
1515331Samw  * smb_msgbuf_term
1525331Samw  *
1535331Samw  * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist.
1545331Samw  */
1555331Samw void
smb_msgbuf_term(smb_msgbuf_t * mb)1565331Samw smb_msgbuf_term(smb_msgbuf_t *mb)
1575331Samw {
1585331Samw 	smb_msgbuf_mlist_t *item = mb->mlist.next;
1595331Samw 	smb_msgbuf_mlist_t *tmp;
1605331Samw 
1615331Samw 	while (item) {
1625331Samw 		tmp = item;
1635331Samw 		item = item->next;
1645331Samw #ifndef _KERNEL
1655331Samw 		free(tmp);
1665331Samw #else
1675331Samw 		kmem_free(tmp, tmp->size);
1685331Samw #endif
1695331Samw 	}
1705331Samw }
1715331Samw 
1725331Samw 
1735331Samw /*
1745331Samw  * smb_msgbuf_decode
1755331Samw  *
1765331Samw  * Decode a smb_msgbuf buffer as indicated by the format string into
1775331Samw  * the variable arg list. This is similar to a scanf operation.
1785331Samw  *
1795331Samw  * On success, returns the number of bytes encoded. Otherwise
1805331Samw  * returns a -ve error code.
1815331Samw  */
1825331Samw int
smb_msgbuf_decode(smb_msgbuf_t * mb,char * fmt,...)1835331Samw smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
1845331Samw {
1855331Samw 	int rc;
1865331Samw 	uint8_t *orig_scan;
1875331Samw 	va_list ap;
1885331Samw 
1895331Samw 	va_start(ap, fmt);
1905331Samw 	orig_scan = mb->scan;
1915331Samw 	rc = buf_decode(mb, fmt, ap);
1925331Samw 	va_end(ap);
1935331Samw 
1945331Samw 	if (rc != SMB_MSGBUF_SUCCESS) {
1955331Samw 		(void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc);
1965331Samw 		mb->scan = orig_scan;
1975331Samw 		return (rc);
1985331Samw 	}
1995331Samw 
2005331Samw 	/*LINTED E_PTRDIFF_OVERFLOW*/
2015331Samw 	return (mb->scan - orig_scan);
2025331Samw }
2035331Samw 
2045331Samw 
2055331Samw /*
2065331Samw  * buf_decode
2075331Samw  *
2085331Samw  * Private decode function, where the real work of decoding the smb_msgbuf
2095331Samw  * is done. This function should only be called via smb_msgbuf_decode to
2105331Samw  * ensure correct behaviour and error handling.
2115331Samw  */
2125331Samw static int
buf_decode(smb_msgbuf_t * mb,char * fmt,va_list ap)2135331Samw buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
2145331Samw {
2155331Samw 	uint32_t ival;
2165331Samw 	uint8_t c;
2175331Samw 	uint8_t *cvalp;
2185331Samw 	uint8_t **cvalpp;
2195331Samw 	uint16_t *wvalp;
2205331Samw 	uint32_t *lvalp;
2215331Samw 	uint64_t *llvalp;
222*10966SJordan.Brown@Sun.COM 	smb_wchar_t *wcs;
2235331Samw 	int repc;
2245331Samw 	int rc;
2255331Samw 
2265331Samw 	while ((c = *fmt++) != 0) {
2275331Samw 		repc = 1;
2285331Samw 
2295331Samw 		if (c == ' ' || c == '\t')
2305331Samw 			continue;
2315331Samw 
2325331Samw 		if (c == '(') {
2335331Samw 			while (((c = *fmt++) != 0) && c != ')')
2345331Samw 				;
2355331Samw 
2365331Samw 			if (!c)
2375331Samw 				return (SMB_MSGBUF_SUCCESS);
2385331Samw 
2395331Samw 			continue;
2405331Samw 		}
2415331Samw 
2425331Samw 		if ('0' <= c && c <= '9') {
2435331Samw 			repc = 0;
2445331Samw 			do {
2455331Samw 				repc = repc * 10 + c - '0';
2465331Samw 				c = *fmt++;
2475331Samw 			} while ('0' <= c && c <= '9');
2485331Samw 		} else if (c == '#') {
2495331Samw 			repc = va_arg(ap, int);
2505331Samw 			c = *fmt++;
2515331Samw 		}
2525331Samw 
2535331Samw 		switch (c) {
2545331Samw 		case '.':
2555331Samw 			if (smb_msgbuf_has_space(mb, repc) == 0)
2565331Samw 				return (SMB_MSGBUF_UNDERFLOW);
2575331Samw 
2585331Samw 			mb->scan += repc;
2595331Samw 			break;
2605331Samw 
2615331Samw 		case 'c':
2625331Samw 			if (smb_msgbuf_has_space(mb, repc) == 0)
2635331Samw 				return (SMB_MSGBUF_UNDERFLOW);
2645331Samw 
2655331Samw 			cvalp = va_arg(ap, uint8_t *);
2665331Samw 			bcopy(mb->scan, cvalp, repc);
2675331Samw 			mb->scan += repc;
2685331Samw 			break;
2695331Samw 
2705331Samw 		case 'b':
2715331Samw 			if (smb_msgbuf_has_space(mb, repc) == 0)
2725331Samw 				return (SMB_MSGBUF_UNDERFLOW);
2735331Samw 
2745331Samw 			cvalp = va_arg(ap, uint8_t *);
2755331Samw 			while (repc-- > 0) {
2765331Samw 				*cvalp++ = *mb->scan++;
2775331Samw 			}
2785331Samw 			break;
2795331Samw 
2805331Samw 		case 'w':
2815331Samw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
2825331Samw 			if (rc == 0)
2835331Samw 				return (SMB_MSGBUF_UNDERFLOW);
2845331Samw 
2855331Samw 			wvalp = va_arg(ap, uint16_t *);
2865331Samw 			while (repc-- > 0) {
2875331Samw 				*wvalp++ = LE_IN16(mb->scan);
2885331Samw 				mb->scan += sizeof (uint16_t);
2895331Samw 			}
2905331Samw 			break;
2915331Samw 
2925331Samw 		case 'l':
2935331Samw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
2945331Samw 			if (rc == 0)
2955331Samw 				return (SMB_MSGBUF_UNDERFLOW);
2965331Samw 
2975331Samw 			lvalp = va_arg(ap, uint32_t *);
2985331Samw 			while (repc-- > 0) {
2995331Samw 				*lvalp++ = LE_IN32(mb->scan);
3005331Samw 				mb->scan += sizeof (int32_t);
3015331Samw 			}
3025331Samw 			break;
3035331Samw 
3045331Samw 		case 'q':
3055331Samw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
3065331Samw 			if (rc == 0)
3075331Samw 				return (SMB_MSGBUF_UNDERFLOW);
3085331Samw 
3095331Samw 			llvalp = va_arg(ap, uint64_t *);
3105331Samw 			while (repc-- > 0) {
3115331Samw 				*llvalp++ = LE_IN64(mb->scan);
3125331Samw 				mb->scan += sizeof (int64_t);
3135331Samw 			}
3145331Samw 			break;
3155331Samw 
3165331Samw 		case 'u': /* Convert from unicode if flags are set */
3175331Samw 			if (mb->flags & SMB_MSGBUF_UNICODE)
3185331Samw 				goto unicode_translation;
3195331Samw 			/*FALLTHROUGH*/
3205331Samw 
3215331Samw 		case 's':
3225331Samw 			ival = strlen((const char *)mb->scan) + 1;
3235331Samw 			if (smb_msgbuf_has_space(mb, ival) == 0)
3245331Samw 				return (SMB_MSGBUF_UNDERFLOW);
3255331Samw 
3265331Samw 			if ((cvalp = smb_msgbuf_malloc(mb, ival * 2)) == 0)
3275331Samw 				return (SMB_MSGBUF_UNDERFLOW);
3285331Samw 
329*10966SJordan.Brown@Sun.COM 			if ((ival = smb_stombs((char *)cvalp,
3305331Samw 			    (char *)mb->scan, ival * 2)) ==
3315331Samw 			    (uint32_t)-1) {
3325331Samw 				return (SMB_MSGBUF_DATA_ERROR);
3335331Samw 			}
3345331Samw 
3355331Samw 			cvalpp = va_arg(ap, uint8_t **);
3365331Samw 			*cvalpp = cvalp;
3375331Samw 			mb->scan += (ival+1);
3385331Samw 			break;
3395331Samw 
3405331Samw 		case 'U': /* Convert from unicode */
3415331Samw unicode_translation:
3425331Samw 			/*
3435331Samw 			 * Unicode strings are always word aligned.
3445331Samw 			 * The malloc'd area is larger than the
3455331Samw 			 * original string because the UTF-8 chars
3465331Samw 			 * may be longer than the wide-chars.
3475331Samw 			 */
3485331Samw 			smb_msgbuf_word_align(mb);
3495331Samw 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
350*10966SJordan.Brown@Sun.COM 			wcs = (smb_wchar_t *)mb->scan;
3515331Samw 
3525331Samw 			/* count the null wchar */
353*10966SJordan.Brown@Sun.COM 			repc = sizeof (smb_wchar_t);
3545331Samw 			while (*wcs++)
355*10966SJordan.Brown@Sun.COM 				repc += sizeof (smb_wchar_t);
3565331Samw 
3575331Samw 			if (smb_msgbuf_has_space(mb, repc) == 0)
3585331Samw 				return (SMB_MSGBUF_UNDERFLOW);
3595331Samw 
3605331Samw 			/* Decode wchar string into host byte-order */
3615331Samw 			if ((wcs = smb_msgbuf_malloc(mb, repc)) == 0)
3625331Samw 				return (SMB_MSGBUF_UNDERFLOW);
3635331Samw 
3645331Samw 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
365*10966SJordan.Brown@Sun.COM 			buf_decode_wcs(wcs, (smb_wchar_t *)mb->scan,
366*10966SJordan.Brown@Sun.COM 			    repc / sizeof (smb_wchar_t));
3675331Samw 
3685331Samw 			/* Get space for translated string */
3695331Samw 			if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
3705331Samw 				return (SMB_MSGBUF_UNDERFLOW);
3715331Samw 
3725331Samw 			/* Translate string */
373*10966SJordan.Brown@Sun.COM 			(void) smb_wcstombs((char *)cvalp, wcs, repc * 2);
3745331Samw 
3755331Samw 			cvalpp = va_arg(ap, uint8_t **);
3765331Samw 			*cvalpp = cvalp;
3775331Samw 			mb->scan += repc;
3785331Samw 			break;
3795331Samw 
3805331Samw 		case 'M':
3815331Samw 			if (smb_msgbuf_has_space(mb, 4) == 0)
3825331Samw 				return (SMB_MSGBUF_UNDERFLOW);
3835331Samw 
3845331Samw 			if (mb->scan[0] != 0xFF ||
3855331Samw 			    mb->scan[1] != 'S' ||
3865331Samw 			    mb->scan[2] != 'M' ||
3875331Samw 			    mb->scan[3] != 'B') {
3885331Samw 				return (SMB_MSGBUF_INVALID_HEADER);
3895331Samw 			}
3905331Samw 			mb->scan += 4;
3915331Samw 			break;
3925331Samw 
3935331Samw 		default:
3945331Samw 			return (SMB_MSGBUF_INVALID_FORMAT);
3955331Samw 		}
3965331Samw 	}
3975331Samw 
3985331Samw 	return (SMB_MSGBUF_SUCCESS);
3995331Samw }
4005331Samw 
4015331Samw 
4025331Samw /*
4035331Samw  * smb_msgbuf_encode
4045331Samw  *
4055331Samw  * Encode a smb_msgbuf buffer as indicated by the format string using
4065331Samw  * the variable arg list. This is similar to a sprintf operation.
4075331Samw  *
4085331Samw  * On success, returns the number of bytes encoded. Otherwise
4095331Samw  * returns a -ve error code.
4105331Samw  */
4115331Samw int
smb_msgbuf_encode(smb_msgbuf_t * mb,char * fmt,...)4125331Samw smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...)
4135331Samw {
4145331Samw 	int rc;
4155331Samw 	uint8_t *orig_scan;
4165331Samw 	va_list ap;
4175331Samw 
4185331Samw 	va_start(ap, fmt);
4195331Samw 	orig_scan = mb->scan;
4205331Samw 	rc = buf_encode(mb, fmt, ap);
4215331Samw 	va_end(ap);
4225331Samw 
4235331Samw 	if (rc != SMB_MSGBUF_SUCCESS) {
4245331Samw 		(void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc);
4255331Samw 		mb->scan = orig_scan;
4265331Samw 		return (rc);
4275331Samw 	}
4285331Samw 
4295331Samw 	/*LINTED E_PTRDIFF_OVERFLOW*/
4305331Samw 	return (mb->scan - orig_scan);
4315331Samw }
4325331Samw 
4335331Samw 
4345331Samw /*
4355331Samw  * buf_encode
4365331Samw  *
4375331Samw  * Private encode function, where the real work of encoding the smb_msgbuf
4385331Samw  * is done. This function should only be called via smb_msgbuf_encode to
4395331Samw  * ensure correct behaviour and error handling.
4405331Samw  */
4415331Samw static int
buf_encode(smb_msgbuf_t * mb,char * fmt,va_list ap)4425331Samw buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
4435331Samw {
4445331Samw 	uint8_t cval;
4455331Samw 	uint16_t wval;
4465331Samw 	uint32_t lval;
4475331Samw 	uint64_t llval;
4485331Samw 	uint32_t ival;
4495331Samw 	uint8_t *cvalp;
4505331Samw 	uint8_t c;
451*10966SJordan.Brown@Sun.COM 	smb_wchar_t wcval;
4525331Samw 	int count;
4535331Samw 	int repc = 1;
4545331Samw 	int rc;
4555331Samw 
4565331Samw 	while ((c = *fmt++) != 0) {
4575331Samw 		repc = 1;
4585331Samw 
4595331Samw 		if (c == ' ' || c == '\t')
4605331Samw 			continue;
4615331Samw 
4625331Samw 		if (c == '(') {
4635331Samw 			while (((c = *fmt++) != 0) && c != ')')
4645331Samw 				;
4655331Samw 
4665331Samw 			if (!c)
4675331Samw 				return (SMB_MSGBUF_SUCCESS);
4685331Samw 
4695331Samw 			continue;
4705331Samw 		}
4715331Samw 
4725331Samw 		if ('0' <= c && c <= '9') {
4735331Samw 			repc = 0;
4745331Samw 			do {
4755331Samw 				repc = repc * 10 + c - '0';
4765331Samw 				c = *fmt++;
4775331Samw 			} while ('0' <= c && c <= '9');
4785331Samw 		} else if (c == '#') {
4795331Samw 			repc = va_arg(ap, int);
4805331Samw 			c = *fmt++;
4815331Samw 		}
4825331Samw 
4835331Samw 		switch (c) {
4845331Samw 		case '.':
4855331Samw 			if (smb_msgbuf_has_space(mb, repc) == 0)
4865331Samw 				return (SMB_MSGBUF_OVERFLOW);
4875331Samw 
4885331Samw 			while (repc-- > 0)
4895331Samw 				*mb->scan++ = 0;
4905331Samw 			break;
4915331Samw 
4925331Samw 		case 'c':
4935331Samw 			if (smb_msgbuf_has_space(mb, repc) == 0)
4945331Samw 				return (SMB_MSGBUF_OVERFLOW);
4955331Samw 
4965331Samw 			cvalp = va_arg(ap, uint8_t *);
4975331Samw 			bcopy(cvalp, mb->scan, repc);
4985331Samw 			mb->scan += repc;
4995331Samw 			break;
5005331Samw 
5015331Samw 		case 'b':
5025331Samw 			if (smb_msgbuf_has_space(mb, repc) == 0)
5035331Samw 				return (SMB_MSGBUF_OVERFLOW);
5045331Samw 
5055331Samw 			while (repc-- > 0) {
5065331Samw 				cval = va_arg(ap, int);
5075331Samw 				*mb->scan++ = cval;
5085331Samw 			}
5095331Samw 			break;
5105331Samw 
5115331Samw 		case 'w':
5125331Samw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
5135331Samw 			if (rc == 0)
5145331Samw 				return (SMB_MSGBUF_OVERFLOW);
5155331Samw 
5165331Samw 			while (repc-- > 0) {
5175331Samw 				wval = va_arg(ap, int);
5185331Samw 				LE_OUT16(mb->scan, wval);
5195331Samw 				mb->scan += sizeof (uint16_t);
5205331Samw 			}
5215331Samw 			break;
5225331Samw 
5235331Samw 		case 'l':
5245331Samw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
5255331Samw 			if (rc == 0)
5265331Samw 				return (SMB_MSGBUF_OVERFLOW);
5275331Samw 
5285331Samw 			while (repc-- > 0) {
5295331Samw 				lval = va_arg(ap, uint32_t);
5305331Samw 				LE_OUT32(mb->scan, lval);
5315331Samw 				mb->scan += sizeof (int32_t);
5325331Samw 			}
5335331Samw 			break;
5345331Samw 
5355331Samw 		case 'q':
5365331Samw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
5375331Samw 			if (rc == 0)
5385331Samw 				return (SMB_MSGBUF_OVERFLOW);
5395331Samw 
5405331Samw 			while (repc-- > 0) {
5415331Samw 				llval = va_arg(ap, uint64_t);
5425331Samw 				LE_OUT64(mb->scan, llval);
5435331Samw 				mb->scan += sizeof (uint64_t);
5445331Samw 			}
5455331Samw 			break;
5465331Samw 
5475331Samw 		case 'u': /* conditional unicode */
5485331Samw 			if (mb->flags & SMB_MSGBUF_UNICODE)
5495331Samw 				goto unicode_translation;
5505331Samw 			/* FALLTHROUGH */
5515331Samw 
5525331Samw 		case 's':
5535331Samw 			cvalp = va_arg(ap, uint8_t *);
5545331Samw 			ival = strlen((const char *)cvalp) + 1;
5555331Samw 
5565331Samw 			if (smb_msgbuf_has_space(mb, ival) == 0)
5575331Samw 				return (SMB_MSGBUF_OVERFLOW);
5585331Samw 
5595331Samw 			ival =
560*10966SJordan.Brown@Sun.COM 			    smb_mbstos((char *)mb->scan, (const char *)cvalp);
5615331Samw 			mb->scan += ival + 1;
5625331Samw 			break;
5635331Samw 
5645331Samw 		case 'U': /* unicode */
5655331Samw unicode_translation:
5665331Samw 			/*
5675331Samw 			 * Unicode strings are always word aligned.
5685331Samw 			 */
5695331Samw 			smb_msgbuf_word_align(mb);
5705331Samw 			cvalp = va_arg(ap, uint8_t *);
5715331Samw 
5725331Samw 			for (;;) {
5735331Samw 				rc = smb_msgbuf_has_space(mb,
574*10966SJordan.Brown@Sun.COM 				    sizeof (smb_wchar_t));
5755331Samw 				if (rc == 0)
5765331Samw 					return (SMB_MSGBUF_OVERFLOW);
5775331Samw 
578*10966SJordan.Brown@Sun.COM 				count = smb_mbtowc(&wcval, (const char *)cvalp,
5795331Samw 				    MTS_MB_CHAR_MAX);
5805331Samw 
5815331Samw 				if (count < 0) {
5825331Samw 					return (SMB_MSGBUF_DATA_ERROR);
5835331Samw 				} else if (count == 0) {
5845331Samw 					/*
5855331Samw 					 * No longer need to do this now that
5865331Samw 					 * mbtowc correctly writes the null
5875331Samw 					 * before returning zero but paranoia
5885331Samw 					 * wins.
5895331Samw 					 */
5905331Samw 					wcval = 0;
5915331Samw 					count = 1;
5925331Samw 				}
5935331Samw 
5945331Samw 				/* Write wchar in wire-format */
5955331Samw 				LE_OUT16(mb->scan, wcval);
5965331Samw 
5975331Samw 				if (*cvalp == 0) {
5985331Samw 					/*
5995331Samw 					 * End of string. Check to see whether
6005331Samw 					 * or not to include the null
6015331Samw 					 * terminator.
6025331Samw 					 */
6035331Samw 					if ((mb->flags & SMB_MSGBUF_NOTERM) ==
6045331Samw 					    0)
6055331Samw 						mb->scan +=
606*10966SJordan.Brown@Sun.COM 						    sizeof (smb_wchar_t);
6075331Samw 					break;
6085331Samw 				}
6095331Samw 
610*10966SJordan.Brown@Sun.COM 				mb->scan += sizeof (smb_wchar_t);
6115331Samw 				cvalp += count;
6125331Samw 			}
6135331Samw 			break;
6145331Samw 
6155331Samw 		case 'M':
6165331Samw 			if (smb_msgbuf_has_space(mb, 4) == 0)
6175331Samw 				return (SMB_MSGBUF_OVERFLOW);
6185331Samw 
6195331Samw 			*mb->scan++ = 0xFF;
6205331Samw 			*mb->scan++ = 'S';
6215331Samw 			*mb->scan++ = 'M';
6225331Samw 			*mb->scan++ = 'B';
6235331Samw 			break;
6245331Samw 
6255331Samw 		default:
6265331Samw 			return (SMB_MSGBUF_INVALID_FORMAT);
6275331Samw 		}
6285331Samw 	}
6295331Samw 
6305331Samw 	return (SMB_MSGBUF_SUCCESS);
6315331Samw }
6325331Samw 
6335331Samw 
6345331Samw /*
6355331Samw  * smb_msgbuf_malloc
6365331Samw  *
6375331Samw  * Allocate some memory for use with this smb_msgbuf. We increase the
6385331Samw  * requested size to hold the list pointer and return a pointer
6395331Samw  * to the area for use by the caller.
6405331Samw  */
6415331Samw static void *
smb_msgbuf_malloc(smb_msgbuf_t * mb,size_t size)6425331Samw smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
6435331Samw {
6445331Samw 	smb_msgbuf_mlist_t *item;
6455331Samw 
6465331Samw 	size += sizeof (smb_msgbuf_mlist_t);
6475331Samw 
6485331Samw #ifndef _KERNEL
6495331Samw 	if ((item = malloc(size)) == NULL)
6505331Samw 		return (NULL);
6515331Samw #else
6525331Samw 	item = kmem_alloc(size, KM_SLEEP);
6535331Samw #endif
6545331Samw 	item->next = mb->mlist.next;
6555331Samw 	item->size = size;
6565331Samw 	mb->mlist.next = item;
6575331Samw 
6585331Samw 	/*
6595331Samw 	 * The caller gets a pointer to the address
6605331Samw 	 * immediately after the smb_msgbuf_mlist_t.
6615331Samw 	 */
6625331Samw 	return ((void *)(item + 1));
6635331Samw }
6645331Samw 
6655331Samw 
6665331Samw /*
6675331Samw  * smb_msgbuf_chkerc
6685331Samw  *
6695331Samw  * Diagnostic function to write an appropriate message to the system log.
6705331Samw  */
6715331Samw static int
smb_msgbuf_chkerc(char * text,int erc)6725331Samw smb_msgbuf_chkerc(char *text, int erc)
6735331Samw {
6745331Samw 	static struct {
6755331Samw 		int erc;
6765331Samw 		char *name;
6775331Samw 	} etable[] = {
6785331Samw 		{ SMB_MSGBUF_SUCCESS,		"success" },
6795331Samw 		{ SMB_MSGBUF_UNDERFLOW,		"overflow/underflow" },
6805331Samw 		{ SMB_MSGBUF_INVALID_FORMAT,	"invalid format" },
6815331Samw 		{ SMB_MSGBUF_INVALID_HEADER,	"invalid header" },
6825331Samw 		{ SMB_MSGBUF_DATA_ERROR,	"data error" }
6835331Samw 	};
6845331Samw 
6855331Samw 	int i;
6865331Samw 
6875331Samw 	for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) {
6885331Samw 		if (etable[i].erc == erc) {
6895331Samw 			if (text == 0)
6905331Samw 				text = "smb_msgbuf_chkerc";
6915331Samw 			break;
6925331Samw 		}
6935331Samw 	}
6945331Samw 	return (erc);
6955331Samw }
6965331Samw 
6975331Samw static void
buf_decode_wcs(smb_wchar_t * dst_wcstr,smb_wchar_t * src_wcstr,int wcstrlen)698*10966SJordan.Brown@Sun.COM buf_decode_wcs(smb_wchar_t *dst_wcstr, smb_wchar_t *src_wcstr, int wcstrlen)
6995331Samw {
7005331Samw 	int i;
7015331Samw 
7025331Samw 	for (i = 0; i < wcstrlen; i++) {
7035331Samw 		*dst_wcstr = LE_IN16(src_wcstr);
7045331Samw 		dst_wcstr++;
7055331Samw 		src_wcstr++;
7065331Samw 	}
7075331Samw }
708