1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
22bbf6f00cSJordan Brown  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24b819cea2SGordon Ross  *
25*25a9a7aaSGordon Ross  * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
26da6c28aaSamw  */
27da6c28aaSamw 
28da6c28aaSamw /*
29da6c28aaSamw  * Msgbuf buffer management implementation. The smb_msgbuf interface is
30da6c28aaSamw  * typically used to encode or decode SMB data using sprintf/scanf
31da6c28aaSamw  * style operations. It contains special handling for the SMB header.
32da6c28aaSamw  * It can also be used for general purpose encoding and decoding.
33da6c28aaSamw  */
34da6c28aaSamw 
35da6c28aaSamw #include <sys/types.h>
36da6c28aaSamw #include <sys/varargs.h>
37da6c28aaSamw #include <sys/byteorder.h>
38b819cea2SGordon Ross #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
39da6c28aaSamw #include <stdlib.h>
40da6c28aaSamw #include <syslog.h>
41da6c28aaSamw #include <string.h>
42da6c28aaSamw #include <strings.h>
43da6c28aaSamw #else
44da6c28aaSamw #include <sys/sunddi.h>
45da6c28aaSamw #include <sys/kmem.h>
46da6c28aaSamw #endif
47da6c28aaSamw #include <smbsrv/string.h>
48da6c28aaSamw #include <smbsrv/msgbuf.h>
49da6c28aaSamw #include <smbsrv/smb.h>
50da6c28aaSamw 
51da6c28aaSamw static int buf_decode(smb_msgbuf_t *, char *, va_list ap);
52da6c28aaSamw static int buf_encode(smb_msgbuf_t *, char *, va_list ap);
53da6c28aaSamw static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
54da6c28aaSamw static int smb_msgbuf_chkerc(char *text, int erc);
55da6c28aaSamw 
567d1ffc32SGordon Ross static int msgbuf_get_oem_string(smb_msgbuf_t *, char **, int);
577d1ffc32SGordon Ross static int msgbuf_get_unicode_string(smb_msgbuf_t *, char **, int);
587d1ffc32SGordon Ross static int msgbuf_put_oem_string(smb_msgbuf_t *, char *, int);
597d1ffc32SGordon Ross static int msgbuf_put_unicode_string(smb_msgbuf_t *, char *, int);
607d1ffc32SGordon Ross 
617d1ffc32SGordon Ross 
62da6c28aaSamw /*
63da6c28aaSamw  * Returns the offset or number of bytes used within the buffer.
64da6c28aaSamw  */
65da6c28aaSamw size_t
smb_msgbuf_used(smb_msgbuf_t * mb)66da6c28aaSamw smb_msgbuf_used(smb_msgbuf_t *mb)
67da6c28aaSamw {
68da6c28aaSamw 	/*LINTED E_PTRDIFF_OVERFLOW*/
69da6c28aaSamw 	return (mb->scan - mb->base);
70da6c28aaSamw }
71da6c28aaSamw 
72da6c28aaSamw /*
73da6c28aaSamw  * Returns the actual buffer size.
74da6c28aaSamw  */
75da6c28aaSamw size_t
smb_msgbuf_size(smb_msgbuf_t * mb)76da6c28aaSamw smb_msgbuf_size(smb_msgbuf_t *mb)
77da6c28aaSamw {
78da6c28aaSamw 	return (mb->max);
79da6c28aaSamw }
80da6c28aaSamw 
81da6c28aaSamw uint8_t *
smb_msgbuf_base(smb_msgbuf_t * mb)82da6c28aaSamw smb_msgbuf_base(smb_msgbuf_t *mb)
83da6c28aaSamw {
84da6c28aaSamw 	return (mb->base);
85da6c28aaSamw }
86da6c28aaSamw 
87da6c28aaSamw /*
88da6c28aaSamw  * Ensure that the scan is aligned on a word (16-bit) boundary.
89da6c28aaSamw  */
90da6c28aaSamw void
smb_msgbuf_word_align(smb_msgbuf_t * mb)91da6c28aaSamw smb_msgbuf_word_align(smb_msgbuf_t *mb)
92da6c28aaSamw {
93da6c28aaSamw 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1);
94da6c28aaSamw }
95da6c28aaSamw 
96da6c28aaSamw /*
97da6c28aaSamw  * Ensure that the scan is aligned on a dword (32-bit) boundary.
98da6c28aaSamw  */
99da6c28aaSamw void
smb_msgbuf_dword_align(smb_msgbuf_t * mb)100da6c28aaSamw smb_msgbuf_dword_align(smb_msgbuf_t *mb)
101da6c28aaSamw {
102da6c28aaSamw 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3);
103da6c28aaSamw }
104da6c28aaSamw 
105da6c28aaSamw /*
106da6c28aaSamw  * Checks whether or not the buffer has space for the amount of data
107da6c28aaSamw  * specified. Returns 1 if there is space, otherwise returns 0.
108da6c28aaSamw  */
109da6c28aaSamw int
smb_msgbuf_has_space(smb_msgbuf_t * mb,size_t size)110da6c28aaSamw smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size)
111da6c28aaSamw {
112da6c28aaSamw 	if (size > mb->max || (mb->scan + size) > mb->end)
113da6c28aaSamw 		return (0);
114da6c28aaSamw 
115da6c28aaSamw 	return (1);
116da6c28aaSamw }
117da6c28aaSamw 
118da6c28aaSamw /*
119da6c28aaSamw  * Set flags the smb_msgbuf.
120da6c28aaSamw  */
121da6c28aaSamw void
smb_msgbuf_fset(smb_msgbuf_t * mb,uint32_t flags)122da6c28aaSamw smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags)
123da6c28aaSamw {
124da6c28aaSamw 	mb->flags |= flags;
125da6c28aaSamw }
126da6c28aaSamw 
127da6c28aaSamw /*
128da6c28aaSamw  * Clear flags the smb_msgbuf.
129da6c28aaSamw  */
130da6c28aaSamw void
smb_msgbuf_fclear(smb_msgbuf_t * mb,uint32_t flags)131da6c28aaSamw smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags)
132da6c28aaSamw {
133da6c28aaSamw 	mb->flags &= ~flags;
134da6c28aaSamw }
135da6c28aaSamw 
136da6c28aaSamw /*
137da6c28aaSamw  * smb_msgbuf_init
138da6c28aaSamw  *
139da6c28aaSamw  * Initialize a smb_msgbuf_t structure based on the buffer and size
140da6c28aaSamw  * specified. Both scan and base initially point to the beginning
141da6c28aaSamw  * of the buffer and end points to the limit of the buffer. As
142da6c28aaSamw  * data is added scan should be incremented to point to the next
143da6c28aaSamw  * offset at which data will be written. Max and count are set
144da6c28aaSamw  * to the actual buffer size.
145da6c28aaSamw  */
146da6c28aaSamw void
smb_msgbuf_init(smb_msgbuf_t * mb,uint8_t * buf,size_t size,uint32_t flags)147da6c28aaSamw smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags)
148da6c28aaSamw {
149da6c28aaSamw 	mb->scan = mb->base = buf;
150da6c28aaSamw 	mb->max = mb->count = size;
151da6c28aaSamw 	mb->end = &buf[size];
152da6c28aaSamw 	mb->flags = flags;
153da6c28aaSamw 	mb->mlist.next = 0;
154da6c28aaSamw }
155da6c28aaSamw 
156da6c28aaSamw 
157da6c28aaSamw /*
158da6c28aaSamw  * smb_msgbuf_term
159da6c28aaSamw  *
160da6c28aaSamw  * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist.
161da6c28aaSamw  */
162da6c28aaSamw void
smb_msgbuf_term(smb_msgbuf_t * mb)163da6c28aaSamw smb_msgbuf_term(smb_msgbuf_t *mb)
164da6c28aaSamw {
165da6c28aaSamw 	smb_msgbuf_mlist_t *item = mb->mlist.next;
166da6c28aaSamw 	smb_msgbuf_mlist_t *tmp;
167da6c28aaSamw 
168da6c28aaSamw 	while (item) {
169da6c28aaSamw 		tmp = item;
170da6c28aaSamw 		item = item->next;
171b819cea2SGordon Ross #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
172da6c28aaSamw 		free(tmp);
173da6c28aaSamw #else
174da6c28aaSamw 		kmem_free(tmp, tmp->size);
175da6c28aaSamw #endif
176da6c28aaSamw 	}
177da6c28aaSamw }
178da6c28aaSamw 
179da6c28aaSamw 
180da6c28aaSamw /*
181da6c28aaSamw  * smb_msgbuf_decode
182da6c28aaSamw  *
183da6c28aaSamw  * Decode a smb_msgbuf buffer as indicated by the format string into
184da6c28aaSamw  * the variable arg list. This is similar to a scanf operation.
185da6c28aaSamw  *
1867d1ffc32SGordon Ross  * On success, returns the number of bytes decoded. Otherwise
187da6c28aaSamw  * returns a -ve error code.
188da6c28aaSamw  */
189da6c28aaSamw int
smb_msgbuf_decode(smb_msgbuf_t * mb,char * fmt,...)190da6c28aaSamw smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
191da6c28aaSamw {
192da6c28aaSamw 	int rc;
193da6c28aaSamw 	uint8_t *orig_scan;
194da6c28aaSamw 	va_list ap;
195da6c28aaSamw 
196da6c28aaSamw 	va_start(ap, fmt);
197da6c28aaSamw 	orig_scan = mb->scan;
198da6c28aaSamw 	rc = buf_decode(mb, fmt, ap);
199da6c28aaSamw 	va_end(ap);
200da6c28aaSamw 
201da6c28aaSamw 	if (rc != SMB_MSGBUF_SUCCESS) {
202da6c28aaSamw 		(void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc);
203da6c28aaSamw 		mb->scan = orig_scan;
204da6c28aaSamw 		return (rc);
205da6c28aaSamw 	}
206da6c28aaSamw 
207da6c28aaSamw 	/*LINTED E_PTRDIFF_OVERFLOW*/
208da6c28aaSamw 	return (mb->scan - orig_scan);
209da6c28aaSamw }
210da6c28aaSamw 
211da6c28aaSamw 
212da6c28aaSamw /*
213da6c28aaSamw  * buf_decode
214da6c28aaSamw  *
215da6c28aaSamw  * Private decode function, where the real work of decoding the smb_msgbuf
216da6c28aaSamw  * is done. This function should only be called via smb_msgbuf_decode to
217da6c28aaSamw  * ensure correct behaviour and error handling.
218da6c28aaSamw  */
219da6c28aaSamw static int
buf_decode(smb_msgbuf_t * mb,char * fmt,va_list ap)220da6c28aaSamw buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
221da6c28aaSamw {
222da6c28aaSamw 	uint8_t c;
22312b65585SGordon Ross 	uint8_t *bvalp;
224da6c28aaSamw 	uint16_t *wvalp;
225da6c28aaSamw 	uint32_t *lvalp;
226da6c28aaSamw 	uint64_t *llvalp;
22712b65585SGordon Ross 	char **cvalpp;
22812b65585SGordon Ross 	boolean_t repc_specified;
229da6c28aaSamw 	int repc;
230da6c28aaSamw 	int rc;
231da6c28aaSamw 
232da6c28aaSamw 	while ((c = *fmt++) != 0) {
23312b65585SGordon Ross 		repc_specified = B_FALSE;
234da6c28aaSamw 		repc = 1;
235da6c28aaSamw 
236da6c28aaSamw 		if (c == ' ' || c == '\t')
237da6c28aaSamw 			continue;
238da6c28aaSamw 
239da6c28aaSamw 		if (c == '(') {
240da6c28aaSamw 			while (((c = *fmt++) != 0) && c != ')')
241da6c28aaSamw 				;
242da6c28aaSamw 
243da6c28aaSamw 			if (!c)
244da6c28aaSamw 				return (SMB_MSGBUF_SUCCESS);
245da6c28aaSamw 
246da6c28aaSamw 			continue;
247da6c28aaSamw 		}
248da6c28aaSamw 
249da6c28aaSamw 		if ('0' <= c && c <= '9') {
250da6c28aaSamw 			repc = 0;
251da6c28aaSamw 			do {
252da6c28aaSamw 				repc = repc * 10 + c - '0';
253da6c28aaSamw 				c = *fmt++;
254da6c28aaSamw 			} while ('0' <= c && c <= '9');
25512b65585SGordon Ross 			repc_specified = B_TRUE;
256da6c28aaSamw 		} else if (c == '#') {
257da6c28aaSamw 			repc = va_arg(ap, int);
258da6c28aaSamw 			c = *fmt++;
25912b65585SGordon Ross 			repc_specified = B_TRUE;
260da6c28aaSamw 		}
261da6c28aaSamw 
262da6c28aaSamw 		switch (c) {
263da6c28aaSamw 		case '.':
264da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
265da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
266da6c28aaSamw 
267da6c28aaSamw 			mb->scan += repc;
268da6c28aaSamw 			break;
269da6c28aaSamw 
27012b65585SGordon Ross 		case 'c': /* get char */
271da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
272da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
273da6c28aaSamw 
27412b65585SGordon Ross 			bvalp = va_arg(ap, uint8_t *);
27512b65585SGordon Ross 			bcopy(mb->scan, bvalp, repc);
276da6c28aaSamw 			mb->scan += repc;
277da6c28aaSamw 			break;
278da6c28aaSamw 
27912b65585SGordon Ross 		case 'b': /* get byte */
280da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
281da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
282da6c28aaSamw 
28312b65585SGordon Ross 			bvalp = va_arg(ap, uint8_t *);
284da6c28aaSamw 			while (repc-- > 0) {
28512b65585SGordon Ross 				*bvalp++ = *mb->scan++;
286da6c28aaSamw 			}
287da6c28aaSamw 			break;
288da6c28aaSamw 
28912b65585SGordon Ross 		case 'w': /* get word */
290da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
291da6c28aaSamw 			if (rc == 0)
292da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
293da6c28aaSamw 
294da6c28aaSamw 			wvalp = va_arg(ap, uint16_t *);
295da6c28aaSamw 			while (repc-- > 0) {
296da6c28aaSamw 				*wvalp++ = LE_IN16(mb->scan);
297da6c28aaSamw 				mb->scan += sizeof (uint16_t);
298da6c28aaSamw 			}
299da6c28aaSamw 			break;
300da6c28aaSamw 
30112b65585SGordon Ross 		case 'l': /* get long */
302da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
303da6c28aaSamw 			if (rc == 0)
304da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
305da6c28aaSamw 
306da6c28aaSamw 			lvalp = va_arg(ap, uint32_t *);
307da6c28aaSamw 			while (repc-- > 0) {
308da6c28aaSamw 				*lvalp++ = LE_IN32(mb->scan);
309da6c28aaSamw 				mb->scan += sizeof (int32_t);
310da6c28aaSamw 			}
311da6c28aaSamw 			break;
312da6c28aaSamw 
31312b65585SGordon Ross 		case 'q': /* get quad */
314da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
315da6c28aaSamw 			if (rc == 0)
316da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
317da6c28aaSamw 
318da6c28aaSamw 			llvalp = va_arg(ap, uint64_t *);
319da6c28aaSamw 			while (repc-- > 0) {
320da6c28aaSamw 				*llvalp++ = LE_IN64(mb->scan);
321da6c28aaSamw 				mb->scan += sizeof (int64_t);
322da6c28aaSamw 			}
323da6c28aaSamw 			break;
324da6c28aaSamw 
325da6c28aaSamw 		case 'u': /* Convert from unicode if flags are set */
326da6c28aaSamw 			if (mb->flags & SMB_MSGBUF_UNICODE)
327da6c28aaSamw 				goto unicode_translation;
328da6c28aaSamw 			/*FALLTHROUGH*/
329da6c28aaSamw 
3307d1ffc32SGordon Ross 		case 's': /* get OEM string */
33112b65585SGordon Ross 			cvalpp = va_arg(ap, char **);
3327d1ffc32SGordon Ross 			if (!repc_specified)
3337d1ffc32SGordon Ross 				repc = 0;
3347d1ffc32SGordon Ross 			rc = msgbuf_get_oem_string(mb, cvalpp, repc);
3357d1ffc32SGordon Ross 			if (rc != 0)
3367d1ffc32SGordon Ross 				return (rc);
337da6c28aaSamw 			break;
338da6c28aaSamw 
3397d1ffc32SGordon Ross 		case 'U': /* get UTF-16 string */
340da6c28aaSamw unicode_translation:
34112b65585SGordon Ross 			cvalpp = va_arg(ap, char **);
3427d1ffc32SGordon Ross 			if (!repc_specified)
3437d1ffc32SGordon Ross 				repc = 0;
3447d1ffc32SGordon Ross 			rc = msgbuf_get_unicode_string(mb, cvalpp, repc);
3457d1ffc32SGordon Ross 			if (rc != 0)
3467d1ffc32SGordon Ross 				return (rc);
347da6c28aaSamw 			break;
348da6c28aaSamw 
349da6c28aaSamw 		case 'M':
350da6c28aaSamw 			if (smb_msgbuf_has_space(mb, 4) == 0)
351da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
352da6c28aaSamw 
353da6c28aaSamw 			if (mb->scan[0] != 0xFF ||
354da6c28aaSamw 			    mb->scan[1] != 'S' ||
355da6c28aaSamw 			    mb->scan[2] != 'M' ||
356da6c28aaSamw 			    mb->scan[3] != 'B') {
357da6c28aaSamw 				return (SMB_MSGBUF_INVALID_HEADER);
358da6c28aaSamw 			}
359da6c28aaSamw 			mb->scan += 4;
360da6c28aaSamw 			break;
361da6c28aaSamw 
362da6c28aaSamw 		default:
363da6c28aaSamw 			return (SMB_MSGBUF_INVALID_FORMAT);
364da6c28aaSamw 		}
365da6c28aaSamw 	}
366da6c28aaSamw 
367da6c28aaSamw 	return (SMB_MSGBUF_SUCCESS);
368da6c28aaSamw }
369da6c28aaSamw 
3707d1ffc32SGordon Ross /*
3717d1ffc32SGordon Ross  * msgbuf_get_oem_string
3727d1ffc32SGordon Ross  *
3737d1ffc32SGordon Ross  * Decode an OEM string, returning its UTF-8 form in strpp,
3747d1ffc32SGordon Ross  * allocated using smb_msgbuf_malloc (automatically freed).
3757d1ffc32SGordon Ross  * If max_bytes != 0, consume at most max_bytes of the mb.
3767d1ffc32SGordon Ross  * See also: mbc_marshal_get_oem_string
3777d1ffc32SGordon Ross  */
3787d1ffc32SGordon Ross static int
msgbuf_get_oem_string(smb_msgbuf_t * mb,char ** strpp,int max_bytes)3797d1ffc32SGordon Ross msgbuf_get_oem_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
3807d1ffc32SGordon Ross {
3817d1ffc32SGordon Ross 	char		*mbs;
3827d1ffc32SGordon Ross 	uint8_t		*oembuf = NULL;
3837d1ffc32SGordon Ross 	int		oemlen;		// len of OEM string, w/o null
3847d1ffc32SGordon Ross 	int		datalen;	// OtW data len
3857d1ffc32SGordon Ross 	int		mbsmax;		// max len of ret str
3867d1ffc32SGordon Ross 	int		rlen;
3877d1ffc32SGordon Ross 
3887d1ffc32SGordon Ross 	if (max_bytes == 0)
3897d1ffc32SGordon Ross 		max_bytes = 0xffff;
3907d1ffc32SGordon Ross 
3917d1ffc32SGordon Ross 	/*
3927d1ffc32SGordon Ross 	 * Determine the OtW data length and OEM string length
3937d1ffc32SGordon Ross 	 * Note: oemlen is the string length (w/o null) and
3947d1ffc32SGordon Ross 	 * datalen is how much we move mb->scan
3957d1ffc32SGordon Ross 	 */
3967d1ffc32SGordon Ross 	datalen = 0;
3977d1ffc32SGordon Ross 	oemlen = 0;
3987d1ffc32SGordon Ross 	for (;;) {
3997d1ffc32SGordon Ross 		if (datalen >= max_bytes)
4007d1ffc32SGordon Ross 			break;
4017d1ffc32SGordon Ross 		/* in-line smb_msgbuf_has_space */
4027d1ffc32SGordon Ross 		if ((mb->scan + datalen) >= mb->end)
4037d1ffc32SGordon Ross 			return (SMB_MSGBUF_UNDERFLOW);
4047d1ffc32SGordon Ross 		datalen++;
4057d1ffc32SGordon Ross 		if (mb->scan[datalen - 1] == 0)
4067d1ffc32SGordon Ross 			break;
4077d1ffc32SGordon Ross 		oemlen++;
4087d1ffc32SGordon Ross 	}
4097d1ffc32SGordon Ross 
4107d1ffc32SGordon Ross 	/*
4117d1ffc32SGordon Ross 	 * Get datalen bytes into a temp buffer
4127d1ffc32SGordon Ross 	 * sized with room to add a null.
4137d1ffc32SGordon Ross 	 * Free oembuf in smb_msgbuf_term
4147d1ffc32SGordon Ross 	 */
4157d1ffc32SGordon Ross 	oembuf = smb_msgbuf_malloc(mb, datalen + 1);
4167d1ffc32SGordon Ross 	if (oembuf == NULL)
4177d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
4187d1ffc32SGordon Ross 	bcopy(mb->scan, oembuf, datalen);
4197d1ffc32SGordon Ross 	mb->scan += datalen;
4207d1ffc32SGordon Ross 	oembuf[oemlen] = '\0';
4217d1ffc32SGordon Ross 
4227d1ffc32SGordon Ross 	/*
4237d1ffc32SGordon Ross 	 * Get the buffer we'll return and convert to UTF-8.
4247d1ffc32SGordon Ross 	 * May take as much as double the space.
4257d1ffc32SGordon Ross 	 */
4267d1ffc32SGordon Ross 	mbsmax = oemlen * 2;
4277d1ffc32SGordon Ross 	mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
4287d1ffc32SGordon Ross 	if (mbs == NULL)
4297d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
4307d1ffc32SGordon Ross 	rlen = smb_oemtombs(mbs, oembuf, mbsmax);
4317d1ffc32SGordon Ross 	if (rlen < 0)
4327d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
4337d1ffc32SGordon Ross 	if (rlen > mbsmax)
4347d1ffc32SGordon Ross 		rlen = mbsmax;
4357d1ffc32SGordon Ross 	mbs[rlen] = '\0';
4367d1ffc32SGordon Ross 	*strpp = mbs;
4377d1ffc32SGordon Ross 	return (0);
4387d1ffc32SGordon Ross }
4397d1ffc32SGordon Ross 
4407d1ffc32SGordon Ross /*
4417d1ffc32SGordon Ross  * msgbuf_get_unicode_string
4427d1ffc32SGordon Ross  *
4437d1ffc32SGordon Ross  * Decode a UTF-16 string, returning its UTF-8 form in strpp,
4447d1ffc32SGordon Ross  * allocated using smb_msgbuf_malloc (automatically freed).
4457d1ffc32SGordon Ross  * If max_bytes != 0, consume at most max_bytes of the mb.
4467d1ffc32SGordon Ross  * See also: mbc_marshal_get_unicode_string
4477d1ffc32SGordon Ross  */
4487d1ffc32SGordon Ross static int
msgbuf_get_unicode_string(smb_msgbuf_t * mb,char ** strpp,int max_bytes)4497d1ffc32SGordon Ross msgbuf_get_unicode_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
4507d1ffc32SGordon Ross {
4517d1ffc32SGordon Ross 	char		*mbs;
4527d1ffc32SGordon Ross 	uint16_t	*wcsbuf = NULL;
4537d1ffc32SGordon Ross 	int		wcslen;		// wchar count
4547d1ffc32SGordon Ross 	int		datalen;	// OtW data len
4557d1ffc32SGordon Ross 	size_t		mbsmax;		// max len of ret str
4567d1ffc32SGordon Ross 	size_t		rlen;
4577d1ffc32SGordon Ross 
4587d1ffc32SGordon Ross 	if (max_bytes == 0)
4597d1ffc32SGordon Ross 		max_bytes = 0xffff;
4607d1ffc32SGordon Ross 
4617d1ffc32SGordon Ross 	/*
4627d1ffc32SGordon Ross 	 * Unicode strings are always word aligned.
4637d1ffc32SGordon Ross 	 */
4647d1ffc32SGordon Ross 	smb_msgbuf_word_align(mb);
4657d1ffc32SGordon Ross 
4667d1ffc32SGordon Ross 	/*
4677d1ffc32SGordon Ross 	 * Determine the OtW data length and (WC) string length
4687d1ffc32SGordon Ross 	 * Note: wcslen counts 16-bit wide_chars (w/o null),
4697d1ffc32SGordon Ross 	 * and datalen is how much we move mb->scan
4707d1ffc32SGordon Ross 	 */
4717d1ffc32SGordon Ross 	datalen = 0;
4727d1ffc32SGordon Ross 	wcslen = 0;
4737d1ffc32SGordon Ross 	for (;;) {
4747d1ffc32SGordon Ross 		if (datalen >= max_bytes)
4757d1ffc32SGordon Ross 			break;
4767d1ffc32SGordon Ross 		/* in-line smb_msgbuf_has_space */
4777d1ffc32SGordon Ross 		if ((mb->scan + datalen) >= mb->end)
4787d1ffc32SGordon Ross 			return (SMB_MSGBUF_UNDERFLOW);
4797d1ffc32SGordon Ross 		datalen += 2;
4807d1ffc32SGordon Ross 		if (mb->scan[datalen - 2] == 0 &&
4817d1ffc32SGordon Ross 		    mb->scan[datalen - 1] == 0)
4827d1ffc32SGordon Ross 			break;
4837d1ffc32SGordon Ross 		wcslen++;
4847d1ffc32SGordon Ross 	}
4857d1ffc32SGordon Ross 
4867d1ffc32SGordon Ross 	/*
4877d1ffc32SGordon Ross 	 * Get datalen bytes into a temp buffer
4887d1ffc32SGordon Ross 	 * sized with room to add a (WC) null.
4897d1ffc32SGordon Ross 	 * Note: wcsbuf has little-endian order
4907d1ffc32SGordon Ross 	 */
4917d1ffc32SGordon Ross 	wcsbuf = smb_msgbuf_malloc(mb, datalen + 2);
4927d1ffc32SGordon Ross 	if (wcsbuf == NULL)
4937d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
4947d1ffc32SGordon Ross 	bcopy(mb->scan, wcsbuf, datalen);
4957d1ffc32SGordon Ross 	mb->scan += datalen;
4967d1ffc32SGordon Ross 	wcsbuf[wcslen] = 0;
4977d1ffc32SGordon Ross 
4987d1ffc32SGordon Ross 	/*
4997d1ffc32SGordon Ross 	 * Get the buffer we'll return and convert to UTF-8.
5007d1ffc32SGordon Ross 	 * May take as much 4X number of wide chars.
5017d1ffc32SGordon Ross 	 */
5027d1ffc32SGordon Ross 	mbsmax = wcslen * MTS_MB_CUR_MAX;
5037d1ffc32SGordon Ross 	mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
5047d1ffc32SGordon Ross 	if (mbs == NULL)
5057d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
5067d1ffc32SGordon Ross 	rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
5077d1ffc32SGordon Ross 	if (rlen == (size_t)-1)
5087d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
5097d1ffc32SGordon Ross 	if (rlen > mbsmax)
5107d1ffc32SGordon Ross 		rlen = mbsmax;
5117d1ffc32SGordon Ross 	mbs[rlen] = '\0';
5127d1ffc32SGordon Ross 	*strpp = mbs;
5137d1ffc32SGordon Ross 	return (0);
5147d1ffc32SGordon Ross }
515da6c28aaSamw 
516da6c28aaSamw /*
517da6c28aaSamw  * smb_msgbuf_encode
518da6c28aaSamw  *
519da6c28aaSamw  * Encode a smb_msgbuf buffer as indicated by the format string using
520da6c28aaSamw  * the variable arg list. This is similar to a sprintf operation.
521da6c28aaSamw  *
522da6c28aaSamw  * On success, returns the number of bytes encoded. Otherwise
523da6c28aaSamw  * returns a -ve error code.
524da6c28aaSamw  */
525da6c28aaSamw int
smb_msgbuf_encode(smb_msgbuf_t * mb,char * fmt,...)526da6c28aaSamw smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...)
527da6c28aaSamw {
528da6c28aaSamw 	int rc;
529da6c28aaSamw 	uint8_t *orig_scan;
530da6c28aaSamw 	va_list ap;
531da6c28aaSamw 
532da6c28aaSamw 	va_start(ap, fmt);
533da6c28aaSamw 	orig_scan = mb->scan;
534da6c28aaSamw 	rc = buf_encode(mb, fmt, ap);
535da6c28aaSamw 	va_end(ap);
536da6c28aaSamw 
537da6c28aaSamw 	if (rc != SMB_MSGBUF_SUCCESS) {
538da6c28aaSamw 		(void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc);
539da6c28aaSamw 		mb->scan = orig_scan;
540da6c28aaSamw 		return (rc);
541da6c28aaSamw 	}
542da6c28aaSamw 
543da6c28aaSamw 	/*LINTED E_PTRDIFF_OVERFLOW*/
544da6c28aaSamw 	return (mb->scan - orig_scan);
545da6c28aaSamw }
546da6c28aaSamw 
547da6c28aaSamw 
548da6c28aaSamw /*
549da6c28aaSamw  * buf_encode
550da6c28aaSamw  *
551da6c28aaSamw  * Private encode function, where the real work of encoding the smb_msgbuf
552da6c28aaSamw  * is done. This function should only be called via smb_msgbuf_encode to
553da6c28aaSamw  * ensure correct behaviour and error handling.
554da6c28aaSamw  */
555da6c28aaSamw static int
buf_encode(smb_msgbuf_t * mb,char * fmt,va_list ap)556da6c28aaSamw buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
557da6c28aaSamw {
558da6c28aaSamw 	uint8_t cval;
559da6c28aaSamw 	uint16_t wval;
560da6c28aaSamw 	uint32_t lval;
561da6c28aaSamw 	uint64_t llval;
56212b65585SGordon Ross 	uint8_t *bvalp;
56312b65585SGordon Ross 	char *cvalp;
564da6c28aaSamw 	uint8_t c;
56512b65585SGordon Ross 	boolean_t repc_specified;
56612b65585SGordon Ross 	int repc;
567da6c28aaSamw 	int rc;
568da6c28aaSamw 
569da6c28aaSamw 	while ((c = *fmt++) != 0) {
57012b65585SGordon Ross 		repc_specified = B_FALSE;
571da6c28aaSamw 		repc = 1;
572da6c28aaSamw 
573da6c28aaSamw 		if (c == ' ' || c == '\t')
574da6c28aaSamw 			continue;
575da6c28aaSamw 
576da6c28aaSamw 		if (c == '(') {
577da6c28aaSamw 			while (((c = *fmt++) != 0) && c != ')')
578da6c28aaSamw 				;
579da6c28aaSamw 
580da6c28aaSamw 			if (!c)
581da6c28aaSamw 				return (SMB_MSGBUF_SUCCESS);
582da6c28aaSamw 
583da6c28aaSamw 			continue;
584da6c28aaSamw 		}
585da6c28aaSamw 
586da6c28aaSamw 		if ('0' <= c && c <= '9') {
587da6c28aaSamw 			repc = 0;
588da6c28aaSamw 			do {
589da6c28aaSamw 				repc = repc * 10 + c - '0';
590da6c28aaSamw 				c = *fmt++;
591da6c28aaSamw 			} while ('0' <= c && c <= '9');
59212b65585SGordon Ross 			repc_specified = B_TRUE;
593da6c28aaSamw 		} else if (c == '#') {
594da6c28aaSamw 			repc = va_arg(ap, int);
595da6c28aaSamw 			c = *fmt++;
59612b65585SGordon Ross 			repc_specified = B_TRUE;
597da6c28aaSamw 		}
598da6c28aaSamw 
599da6c28aaSamw 		switch (c) {
600da6c28aaSamw 		case '.':
601da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
602da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
603da6c28aaSamw 
604da6c28aaSamw 			while (repc-- > 0)
605da6c28aaSamw 				*mb->scan++ = 0;
606da6c28aaSamw 			break;
607da6c28aaSamw 
60812b65585SGordon Ross 		case 'c': /* put char */
609da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
610da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
611da6c28aaSamw 
61212b65585SGordon Ross 			bvalp = va_arg(ap, uint8_t *);
61312b65585SGordon Ross 			bcopy(bvalp, mb->scan, repc);
614da6c28aaSamw 			mb->scan += repc;
615da6c28aaSamw 			break;
616da6c28aaSamw 
61712b65585SGordon Ross 		case 'b': /* put byte */
618da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
619da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
620da6c28aaSamw 
621da6c28aaSamw 			while (repc-- > 0) {
622da6c28aaSamw 				cval = va_arg(ap, int);
623da6c28aaSamw 				*mb->scan++ = cval;
624da6c28aaSamw 			}
625da6c28aaSamw 			break;
626da6c28aaSamw 
62712b65585SGordon Ross 		case 'w': /* put word */
628da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
629da6c28aaSamw 			if (rc == 0)
630da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
631da6c28aaSamw 
632da6c28aaSamw 			while (repc-- > 0) {
633da6c28aaSamw 				wval = va_arg(ap, int);
634da6c28aaSamw 				LE_OUT16(mb->scan, wval);
635da6c28aaSamw 				mb->scan += sizeof (uint16_t);
636da6c28aaSamw 			}
637da6c28aaSamw 			break;
638da6c28aaSamw 
63912b65585SGordon Ross 		case 'l': /* put long */
640da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
641da6c28aaSamw 			if (rc == 0)
642da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
643da6c28aaSamw 
644da6c28aaSamw 			while (repc-- > 0) {
645da6c28aaSamw 				lval = va_arg(ap, uint32_t);
646da6c28aaSamw 				LE_OUT32(mb->scan, lval);
647da6c28aaSamw 				mb->scan += sizeof (int32_t);
648da6c28aaSamw 			}
649da6c28aaSamw 			break;
650da6c28aaSamw 
65112b65585SGordon Ross 		case 'q': /* put quad */
652da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
653da6c28aaSamw 			if (rc == 0)
654da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
655da6c28aaSamw 
656da6c28aaSamw 			while (repc-- > 0) {
657da6c28aaSamw 				llval = va_arg(ap, uint64_t);
658da6c28aaSamw 				LE_OUT64(mb->scan, llval);
659da6c28aaSamw 				mb->scan += sizeof (uint64_t);
660da6c28aaSamw 			}
661da6c28aaSamw 			break;
662da6c28aaSamw 
663da6c28aaSamw 		case 'u': /* conditional unicode */
664da6c28aaSamw 			if (mb->flags & SMB_MSGBUF_UNICODE)
665da6c28aaSamw 				goto unicode_translation;
666da6c28aaSamw 			/* FALLTHROUGH */
667da6c28aaSamw 
6687d1ffc32SGordon Ross 		case 's': /* put OEM string */
66912b65585SGordon Ross 			cvalp = va_arg(ap, char *);
6707d1ffc32SGordon Ross 			if (!repc_specified)
6717d1ffc32SGordon Ross 				repc = 0;
6727d1ffc32SGordon Ross 			rc = msgbuf_put_oem_string(mb, cvalp, repc);
6737d1ffc32SGordon Ross 			if (rc != 0)
6747d1ffc32SGordon Ross 				return (rc);
675da6c28aaSamw 			break;
676da6c28aaSamw 
6777d1ffc32SGordon Ross 		case 'U': /* put UTF-16 string */
678da6c28aaSamw unicode_translation:
67912b65585SGordon Ross 			cvalp = va_arg(ap, char *);
6807d1ffc32SGordon Ross 			if (!repc_specified)
6817d1ffc32SGordon Ross 				repc = 0;
6827d1ffc32SGordon Ross 			rc = msgbuf_put_unicode_string(mb, cvalp, repc);
6837d1ffc32SGordon Ross 			if (rc != 0)
6847d1ffc32SGordon Ross 				return (rc);
685da6c28aaSamw 			break;
686da6c28aaSamw 
687da6c28aaSamw 		case 'M':
688da6c28aaSamw 			if (smb_msgbuf_has_space(mb, 4) == 0)
689da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
690da6c28aaSamw 
691da6c28aaSamw 			*mb->scan++ = 0xFF;
692da6c28aaSamw 			*mb->scan++ = 'S';
693da6c28aaSamw 			*mb->scan++ = 'M';
694da6c28aaSamw 			*mb->scan++ = 'B';
695da6c28aaSamw 			break;
696da6c28aaSamw 
697da6c28aaSamw 		default:
698da6c28aaSamw 			return (SMB_MSGBUF_INVALID_FORMAT);
699da6c28aaSamw 		}
700da6c28aaSamw 	}
701da6c28aaSamw 
702da6c28aaSamw 	return (SMB_MSGBUF_SUCCESS);
703da6c28aaSamw }
704da6c28aaSamw 
7057d1ffc32SGordon Ross /*
7067d1ffc32SGordon Ross  * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
7077d1ffc32SGordon Ross  * Also write a null unless the repc count limits the length we put.
7087d1ffc32SGordon Ross  * When (repc > 0) the length we marshal must be exactly repc, and
7097d1ffc32SGordon Ross  * truncate or pad the mb data as necessary.
7107d1ffc32SGordon Ross  * See also: mbc_marshal_put_oem_string
7117d1ffc32SGordon Ross  */
7127d1ffc32SGordon Ross static int
msgbuf_put_oem_string(smb_msgbuf_t * mb,char * mbs,int repc)7137d1ffc32SGordon Ross msgbuf_put_oem_string(smb_msgbuf_t *mb, char *mbs, int repc)
7147d1ffc32SGordon Ross {
7157d1ffc32SGordon Ross 	uint8_t		*oembuf = NULL;
7167d1ffc32SGordon Ross 	uint8_t		*s;
7177d1ffc32SGordon Ross 	int		oemlen;
7187d1ffc32SGordon Ross 	int		rlen;
7197d1ffc32SGordon Ross 
7207d1ffc32SGordon Ross 	/*
7217d1ffc32SGordon Ross 	 * Compute length of converted OEM string,
7227d1ffc32SGordon Ross 	 * NOT including null terminator
7237d1ffc32SGordon Ross 	 */
7247d1ffc32SGordon Ross 	if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
7257d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
7267d1ffc32SGordon Ross 
7277d1ffc32SGordon Ross 	/*
7287d1ffc32SGordon Ross 	 * If repc not specified, put whole string + NULL,
7297d1ffc32SGordon Ross 	 * otherwise will truncate or pad as needed.
7307d1ffc32SGordon Ross 	 */
7317d1ffc32SGordon Ross 	if (repc <= 0) {
7327d1ffc32SGordon Ross 		repc = oemlen;
7337d1ffc32SGordon Ross 		if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
7347d1ffc32SGordon Ross 			repc += sizeof (char);
7357d1ffc32SGordon Ross 	}
7367d1ffc32SGordon Ross 
7377d1ffc32SGordon Ross 	/*
7387d1ffc32SGordon Ross 	 * Convert into a temporary buffer
7397d1ffc32SGordon Ross 	 * Free oembuf in smb_msgbuf_term.
7407d1ffc32SGordon Ross 	 */
7417d1ffc32SGordon Ross 	oembuf = smb_msgbuf_malloc(mb, oemlen + 1);
7427d1ffc32SGordon Ross 	if (oembuf == NULL)
7437d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
7447d1ffc32SGordon Ross 	rlen = smb_mbstooem(oembuf, mbs, oemlen);
7457d1ffc32SGordon Ross 	if (rlen < 0)
7467d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
7477d1ffc32SGordon Ross 	if (rlen > oemlen)
7487d1ffc32SGordon Ross 		rlen = oemlen;
7497d1ffc32SGordon Ross 	oembuf[rlen] = '\0';
7507d1ffc32SGordon Ross 
7517d1ffc32SGordon Ross 	/*
7527d1ffc32SGordon Ross 	 * Copy the converted string into the message,
7537d1ffc32SGordon Ross 	 * truncated or paded as required.
7547d1ffc32SGordon Ross 	 */
7557d1ffc32SGordon Ross 	s = oembuf;
7567d1ffc32SGordon Ross 	while (repc > 0) {
757*25a9a7aaSGordon Ross 		if (smb_msgbuf_has_space(mb, 1) == 0)
758*25a9a7aaSGordon Ross 			return (SMB_MSGBUF_OVERFLOW);
7597d1ffc32SGordon Ross 		*mb->scan++ = *s;
7607d1ffc32SGordon Ross 		if (*s != '\0')
7617d1ffc32SGordon Ross 			s++;
7627d1ffc32SGordon Ross 		repc--;
7637d1ffc32SGordon Ross 	}
7647d1ffc32SGordon Ross 
7657d1ffc32SGordon Ross 	return (0);
7667d1ffc32SGordon Ross }
7677d1ffc32SGordon Ross 
7687d1ffc32SGordon Ross /*
7697d1ffc32SGordon Ross  * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
7707d1ffc32SGordon Ross  * Also write a null unless the repc count limits the length.
7717d1ffc32SGordon Ross  * When (repc > 0) the length we marshal must be exactly repc,
7727d1ffc32SGordon Ross  * and truncate or pad the mb data as necessary.
7737d1ffc32SGordon Ross  * See also: mbc_marshal_put_unicode_string
7747d1ffc32SGordon Ross  */
7757d1ffc32SGordon Ross static int
msgbuf_put_unicode_string(smb_msgbuf_t * mb,char * mbs,int repc)7767d1ffc32SGordon Ross msgbuf_put_unicode_string(smb_msgbuf_t *mb, char *mbs, int repc)
7777d1ffc32SGordon Ross {
7787d1ffc32SGordon Ross 	smb_wchar_t	*wcsbuf = NULL;
7797d1ffc32SGordon Ross 	smb_wchar_t	*wp;
780*25a9a7aaSGordon Ross 	smb_wchar_t	wchar;
7817d1ffc32SGordon Ross 	size_t		wcslen, wcsbytes;
7827d1ffc32SGordon Ross 	size_t		rlen;
7837d1ffc32SGordon Ross 
7847d1ffc32SGordon Ross 	/* align to word boundary */
7857d1ffc32SGordon Ross 	smb_msgbuf_word_align(mb);
7867d1ffc32SGordon Ross 
7877d1ffc32SGordon Ross 	/*
7887d1ffc32SGordon Ross 	 * Compute length of converted UTF-16 string,
7897d1ffc32SGordon Ross 	 * NOT including null terminator (in bytes).
7907d1ffc32SGordon Ross 	 */
7917d1ffc32SGordon Ross 	wcsbytes = smb_wcequiv_strlen(mbs);
7927d1ffc32SGordon Ross 	if (wcsbytes == (size_t)-1)
7937d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
7947d1ffc32SGordon Ross 
7957d1ffc32SGordon Ross 	/*
7967d1ffc32SGordon Ross 	 * If repc not specified, put whole string + NULL,
7977d1ffc32SGordon Ross 	 * otherwise will truncate or pad as needed.
7987d1ffc32SGordon Ross 	 */
7997d1ffc32SGordon Ross 	if (repc <= 0) {
8007d1ffc32SGordon Ross 		repc = (int)wcsbytes;
8017d1ffc32SGordon Ross 		if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
8027d1ffc32SGordon Ross 			repc += sizeof (smb_wchar_t);
8037d1ffc32SGordon Ross 	}
8047d1ffc32SGordon Ross 
8057d1ffc32SGordon Ross 	/*
8067d1ffc32SGordon Ross 	 * Convert into a temporary buffer
8077d1ffc32SGordon Ross 	 * Free wcsbuf in smb_msgbuf_term
8087d1ffc32SGordon Ross 	 */
8097d1ffc32SGordon Ross 	wcslen = wcsbytes / 2;
8107d1ffc32SGordon Ross 	wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2);
8117d1ffc32SGordon Ross 	if (wcsbuf == NULL)
8127d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
8137d1ffc32SGordon Ross 	rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
8147d1ffc32SGordon Ross 	if (rlen == (size_t)-1)
8157d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
8167d1ffc32SGordon Ross 	if (rlen > wcslen)
8177d1ffc32SGordon Ross 		rlen = wcslen;
8187d1ffc32SGordon Ross 	wcsbuf[rlen] = 0;
8197d1ffc32SGordon Ross 
8207d1ffc32SGordon Ross 	/*
8217d1ffc32SGordon Ross 	 * Copy the converted string into the message,
8227d1ffc32SGordon Ross 	 * truncated or paded as required.  Preserve
8237d1ffc32SGordon Ross 	 * little-endian order while copying.
8247d1ffc32SGordon Ross 	 */
8257d1ffc32SGordon Ross 	wp = wcsbuf;
826*25a9a7aaSGordon Ross 	while (repc >= sizeof (smb_wchar_t)) {
827*25a9a7aaSGordon Ross 		if (smb_msgbuf_has_space(mb, sizeof (smb_wchar_t)) == 0)
828*25a9a7aaSGordon Ross 			return (SMB_MSGBUF_OVERFLOW);
829*25a9a7aaSGordon Ross 		wchar = LE_IN16(wp);
8307d1ffc32SGordon Ross 		LE_OUT16(mb->scan, wchar);
8317d1ffc32SGordon Ross 		mb->scan += 2;
8327d1ffc32SGordon Ross 		if (wchar != 0)
8337d1ffc32SGordon Ross 			wp++;
8347d1ffc32SGordon Ross 		repc -= sizeof (smb_wchar_t);
8357d1ffc32SGordon Ross 	}
836*25a9a7aaSGordon Ross 	if (repc > 0) {
837*25a9a7aaSGordon Ross 		if (smb_msgbuf_has_space(mb, 1) == 0)
838*25a9a7aaSGordon Ross 			return (SMB_MSGBUF_OVERFLOW);
8397d1ffc32SGordon Ross 		*mb->scan++ = '\0';
840*25a9a7aaSGordon Ross 	}
8417d1ffc32SGordon Ross 
8427d1ffc32SGordon Ross 	return (0);
8437d1ffc32SGordon Ross }
844da6c28aaSamw 
845da6c28aaSamw /*
846da6c28aaSamw  * smb_msgbuf_malloc
847da6c28aaSamw  *
848da6c28aaSamw  * Allocate some memory for use with this smb_msgbuf. We increase the
849da6c28aaSamw  * requested size to hold the list pointer and return a pointer
850da6c28aaSamw  * to the area for use by the caller.
851da6c28aaSamw  */
852da6c28aaSamw static void *
smb_msgbuf_malloc(smb_msgbuf_t * mb,size_t size)853da6c28aaSamw smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
854da6c28aaSamw {
855da6c28aaSamw 	smb_msgbuf_mlist_t *item;
856da6c28aaSamw 
857da6c28aaSamw 	size += sizeof (smb_msgbuf_mlist_t);
858da6c28aaSamw 
859b819cea2SGordon Ross #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
860da6c28aaSamw 	if ((item = malloc(size)) == NULL)
861da6c28aaSamw 		return (NULL);
862da6c28aaSamw #else
863da6c28aaSamw 	item = kmem_alloc(size, KM_SLEEP);
864da6c28aaSamw #endif
865da6c28aaSamw 	item->next = mb->mlist.next;
866da6c28aaSamw 	item->size = size;
867da6c28aaSamw 	mb->mlist.next = item;
868da6c28aaSamw 
869da6c28aaSamw 	/*
870da6c28aaSamw 	 * The caller gets a pointer to the address
871da6c28aaSamw 	 * immediately after the smb_msgbuf_mlist_t.
872da6c28aaSamw 	 */
873da6c28aaSamw 	return ((void *)(item + 1));
874da6c28aaSamw }
875da6c28aaSamw 
876da6c28aaSamw 
877da6c28aaSamw /*
878da6c28aaSamw  * smb_msgbuf_chkerc
879da6c28aaSamw  *
880da6c28aaSamw  * Diagnostic function to write an appropriate message to the system log.
881da6c28aaSamw  */
882da6c28aaSamw static int
smb_msgbuf_chkerc(char * text,int erc)883da6c28aaSamw smb_msgbuf_chkerc(char *text, int erc)
884da6c28aaSamw {
885da6c28aaSamw 	static struct {
886da6c28aaSamw 		int erc;
887da6c28aaSamw 		char *name;
888da6c28aaSamw 	} etable[] = {
889da6c28aaSamw 		{ SMB_MSGBUF_SUCCESS,		"success" },
890da6c28aaSamw 		{ SMB_MSGBUF_UNDERFLOW,		"overflow/underflow" },
891da6c28aaSamw 		{ SMB_MSGBUF_INVALID_FORMAT,	"invalid format" },
892da6c28aaSamw 		{ SMB_MSGBUF_INVALID_HEADER,	"invalid header" },
893da6c28aaSamw 		{ SMB_MSGBUF_DATA_ERROR,	"data error" }
894da6c28aaSamw 	};
895da6c28aaSamw 
896da6c28aaSamw 	int i;
897da6c28aaSamw 
898da6c28aaSamw 	for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) {
899da6c28aaSamw 		if (etable[i].erc == erc) {
900da6c28aaSamw 			if (text == 0)
901da6c28aaSamw 				text = "smb_msgbuf_chkerc";
902da6c28aaSamw 			break;
903da6c28aaSamw 		}
904da6c28aaSamw 	}
905da6c28aaSamw 	return (erc);
906da6c28aaSamw }
907