xref: /illumos-gate/usr/src/common/smbsrv/smb_msgbuf.c (revision 7d1ffc32e5e72873791b96934af035e0f051fc14)
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*7d1ffc32SGordon Ross  * Copyright 2018 Nexenta Systems, 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 
56*7d1ffc32SGordon Ross static int msgbuf_get_oem_string(smb_msgbuf_t *, char **, int);
57*7d1ffc32SGordon Ross static int msgbuf_get_unicode_string(smb_msgbuf_t *, char **, int);
58*7d1ffc32SGordon Ross static int msgbuf_put_oem_string(smb_msgbuf_t *, char *, int);
59*7d1ffc32SGordon Ross static int msgbuf_put_unicode_string(smb_msgbuf_t *, char *, int);
60*7d1ffc32SGordon Ross 
61*7d1ffc32SGordon Ross 
62da6c28aaSamw /*
63da6c28aaSamw  * Returns the offset or number of bytes used within the buffer.
64da6c28aaSamw  */
65da6c28aaSamw size_t
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
76da6c28aaSamw smb_msgbuf_size(smb_msgbuf_t *mb)
77da6c28aaSamw {
78da6c28aaSamw 	return (mb->max);
79da6c28aaSamw }
80da6c28aaSamw 
81da6c28aaSamw uint8_t *
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
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
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
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
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
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
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
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  *
186*7d1ffc32SGordon Ross  * On success, returns the number of bytes decoded. Otherwise
187da6c28aaSamw  * returns a -ve error code.
188da6c28aaSamw  */
189da6c28aaSamw int
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
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 
330*7d1ffc32SGordon Ross 		case 's': /* get OEM string */
33112b65585SGordon Ross 			cvalpp = va_arg(ap, char **);
332*7d1ffc32SGordon Ross 			if (!repc_specified)
333*7d1ffc32SGordon Ross 				repc = 0;
334*7d1ffc32SGordon Ross 			rc = msgbuf_get_oem_string(mb, cvalpp, repc);
335*7d1ffc32SGordon Ross 			if (rc != 0)
336*7d1ffc32SGordon Ross 				return (rc);
337da6c28aaSamw 			break;
338da6c28aaSamw 
339*7d1ffc32SGordon Ross 		case 'U': /* get UTF-16 string */
340da6c28aaSamw unicode_translation:
34112b65585SGordon Ross 			cvalpp = va_arg(ap, char **);
342*7d1ffc32SGordon Ross 			if (!repc_specified)
343*7d1ffc32SGordon Ross 				repc = 0;
344*7d1ffc32SGordon Ross 			rc = msgbuf_get_unicode_string(mb, cvalpp, repc);
345*7d1ffc32SGordon Ross 			if (rc != 0)
346*7d1ffc32SGordon 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 
370*7d1ffc32SGordon Ross /*
371*7d1ffc32SGordon Ross  * msgbuf_get_oem_string
372*7d1ffc32SGordon Ross  *
373*7d1ffc32SGordon Ross  * Decode an OEM string, returning its UTF-8 form in strpp,
374*7d1ffc32SGordon Ross  * allocated using smb_msgbuf_malloc (automatically freed).
375*7d1ffc32SGordon Ross  * If max_bytes != 0, consume at most max_bytes of the mb.
376*7d1ffc32SGordon Ross  * See also: mbc_marshal_get_oem_string
377*7d1ffc32SGordon Ross  */
378*7d1ffc32SGordon Ross static int
379*7d1ffc32SGordon Ross msgbuf_get_oem_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
380*7d1ffc32SGordon Ross {
381*7d1ffc32SGordon Ross 	char		*mbs;
382*7d1ffc32SGordon Ross 	uint8_t		*oembuf = NULL;
383*7d1ffc32SGordon Ross 	int		oemlen;		// len of OEM string, w/o null
384*7d1ffc32SGordon Ross 	int		datalen;	// OtW data len
385*7d1ffc32SGordon Ross 	int		mbsmax;		// max len of ret str
386*7d1ffc32SGordon Ross 	int		rlen;
387*7d1ffc32SGordon Ross 
388*7d1ffc32SGordon Ross 	if (max_bytes == 0)
389*7d1ffc32SGordon Ross 		max_bytes = 0xffff;
390*7d1ffc32SGordon Ross 
391*7d1ffc32SGordon Ross 	/*
392*7d1ffc32SGordon Ross 	 * Determine the OtW data length and OEM string length
393*7d1ffc32SGordon Ross 	 * Note: oemlen is the string length (w/o null) and
394*7d1ffc32SGordon Ross 	 * datalen is how much we move mb->scan
395*7d1ffc32SGordon Ross 	 */
396*7d1ffc32SGordon Ross 	datalen = 0;
397*7d1ffc32SGordon Ross 	oemlen = 0;
398*7d1ffc32SGordon Ross 	for (;;) {
399*7d1ffc32SGordon Ross 		if (datalen >= max_bytes)
400*7d1ffc32SGordon Ross 			break;
401*7d1ffc32SGordon Ross 		/* in-line smb_msgbuf_has_space */
402*7d1ffc32SGordon Ross 		if ((mb->scan + datalen) >= mb->end)
403*7d1ffc32SGordon Ross 			return (SMB_MSGBUF_UNDERFLOW);
404*7d1ffc32SGordon Ross 		datalen++;
405*7d1ffc32SGordon Ross 		if (mb->scan[datalen - 1] == 0)
406*7d1ffc32SGordon Ross 			break;
407*7d1ffc32SGordon Ross 		oemlen++;
408*7d1ffc32SGordon Ross 	}
409*7d1ffc32SGordon Ross 
410*7d1ffc32SGordon Ross 	/*
411*7d1ffc32SGordon Ross 	 * Get datalen bytes into a temp buffer
412*7d1ffc32SGordon Ross 	 * sized with room to add a null.
413*7d1ffc32SGordon Ross 	 * Free oembuf in smb_msgbuf_term
414*7d1ffc32SGordon Ross 	 */
415*7d1ffc32SGordon Ross 	oembuf = smb_msgbuf_malloc(mb, datalen + 1);
416*7d1ffc32SGordon Ross 	if (oembuf == NULL)
417*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
418*7d1ffc32SGordon Ross 	bcopy(mb->scan, oembuf, datalen);
419*7d1ffc32SGordon Ross 	mb->scan += datalen;
420*7d1ffc32SGordon Ross 	oembuf[oemlen] = '\0';
421*7d1ffc32SGordon Ross 
422*7d1ffc32SGordon Ross 	/*
423*7d1ffc32SGordon Ross 	 * Get the buffer we'll return and convert to UTF-8.
424*7d1ffc32SGordon Ross 	 * May take as much as double the space.
425*7d1ffc32SGordon Ross 	 */
426*7d1ffc32SGordon Ross 	mbsmax = oemlen * 2;
427*7d1ffc32SGordon Ross 	mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
428*7d1ffc32SGordon Ross 	if (mbs == NULL)
429*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
430*7d1ffc32SGordon Ross 	rlen = smb_oemtombs(mbs, oembuf, mbsmax);
431*7d1ffc32SGordon Ross 	if (rlen < 0)
432*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
433*7d1ffc32SGordon Ross 	if (rlen > mbsmax)
434*7d1ffc32SGordon Ross 		rlen = mbsmax;
435*7d1ffc32SGordon Ross 	mbs[rlen] = '\0';
436*7d1ffc32SGordon Ross 	*strpp = mbs;
437*7d1ffc32SGordon Ross 	return (0);
438*7d1ffc32SGordon Ross }
439*7d1ffc32SGordon Ross 
440*7d1ffc32SGordon Ross /*
441*7d1ffc32SGordon Ross  * msgbuf_get_unicode_string
442*7d1ffc32SGordon Ross  *
443*7d1ffc32SGordon Ross  * Decode a UTF-16 string, returning its UTF-8 form in strpp,
444*7d1ffc32SGordon Ross  * allocated using smb_msgbuf_malloc (automatically freed).
445*7d1ffc32SGordon Ross  * If max_bytes != 0, consume at most max_bytes of the mb.
446*7d1ffc32SGordon Ross  * See also: mbc_marshal_get_unicode_string
447*7d1ffc32SGordon Ross  */
448*7d1ffc32SGordon Ross static int
449*7d1ffc32SGordon Ross msgbuf_get_unicode_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
450*7d1ffc32SGordon Ross {
451*7d1ffc32SGordon Ross 	char		*mbs;
452*7d1ffc32SGordon Ross 	uint16_t	*wcsbuf = NULL;
453*7d1ffc32SGordon Ross 	int		wcslen;		// wchar count
454*7d1ffc32SGordon Ross 	int		datalen;	// OtW data len
455*7d1ffc32SGordon Ross 	size_t		mbsmax;		// max len of ret str
456*7d1ffc32SGordon Ross 	size_t		rlen;
457*7d1ffc32SGordon Ross 
458*7d1ffc32SGordon Ross 	if (max_bytes == 0)
459*7d1ffc32SGordon Ross 		max_bytes = 0xffff;
460*7d1ffc32SGordon Ross 
461*7d1ffc32SGordon Ross 	/*
462*7d1ffc32SGordon Ross 	 * Unicode strings are always word aligned.
463*7d1ffc32SGordon Ross 	 */
464*7d1ffc32SGordon Ross 	smb_msgbuf_word_align(mb);
465*7d1ffc32SGordon Ross 
466*7d1ffc32SGordon Ross 	/*
467*7d1ffc32SGordon Ross 	 * Determine the OtW data length and (WC) string length
468*7d1ffc32SGordon Ross 	 * Note: wcslen counts 16-bit wide_chars (w/o null),
469*7d1ffc32SGordon Ross 	 * and datalen is how much we move mb->scan
470*7d1ffc32SGordon Ross 	 */
471*7d1ffc32SGordon Ross 	datalen = 0;
472*7d1ffc32SGordon Ross 	wcslen = 0;
473*7d1ffc32SGordon Ross 	for (;;) {
474*7d1ffc32SGordon Ross 		if (datalen >= max_bytes)
475*7d1ffc32SGordon Ross 			break;
476*7d1ffc32SGordon Ross 		/* in-line smb_msgbuf_has_space */
477*7d1ffc32SGordon Ross 		if ((mb->scan + datalen) >= mb->end)
478*7d1ffc32SGordon Ross 			return (SMB_MSGBUF_UNDERFLOW);
479*7d1ffc32SGordon Ross 		datalen += 2;
480*7d1ffc32SGordon Ross 		if (mb->scan[datalen - 2] == 0 &&
481*7d1ffc32SGordon Ross 		    mb->scan[datalen - 1] == 0)
482*7d1ffc32SGordon Ross 			break;
483*7d1ffc32SGordon Ross 		wcslen++;
484*7d1ffc32SGordon Ross 	}
485*7d1ffc32SGordon Ross 
486*7d1ffc32SGordon Ross 	/*
487*7d1ffc32SGordon Ross 	 * Get datalen bytes into a temp buffer
488*7d1ffc32SGordon Ross 	 * sized with room to add a (WC) null.
489*7d1ffc32SGordon Ross 	 * Note: wcsbuf has little-endian order
490*7d1ffc32SGordon Ross 	 */
491*7d1ffc32SGordon Ross 	wcsbuf = smb_msgbuf_malloc(mb, datalen + 2);
492*7d1ffc32SGordon Ross 	if (wcsbuf == NULL)
493*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
494*7d1ffc32SGordon Ross 	bcopy(mb->scan, wcsbuf, datalen);
495*7d1ffc32SGordon Ross 	mb->scan += datalen;
496*7d1ffc32SGordon Ross 	wcsbuf[wcslen] = 0;
497*7d1ffc32SGordon Ross 
498*7d1ffc32SGordon Ross 	/*
499*7d1ffc32SGordon Ross 	 * Get the buffer we'll return and convert to UTF-8.
500*7d1ffc32SGordon Ross 	 * May take as much 4X number of wide chars.
501*7d1ffc32SGordon Ross 	 */
502*7d1ffc32SGordon Ross 	mbsmax = wcslen * MTS_MB_CUR_MAX;
503*7d1ffc32SGordon Ross 	mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
504*7d1ffc32SGordon Ross 	if (mbs == NULL)
505*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
506*7d1ffc32SGordon Ross 	rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
507*7d1ffc32SGordon Ross 	if (rlen == (size_t)-1)
508*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
509*7d1ffc32SGordon Ross 	if (rlen > mbsmax)
510*7d1ffc32SGordon Ross 		rlen = mbsmax;
511*7d1ffc32SGordon Ross 	mbs[rlen] = '\0';
512*7d1ffc32SGordon Ross 	*strpp = mbs;
513*7d1ffc32SGordon Ross 	return (0);
514*7d1ffc32SGordon 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
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
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 
668*7d1ffc32SGordon Ross 		case 's': /* put OEM string */
66912b65585SGordon Ross 			cvalp = va_arg(ap, char *);
670*7d1ffc32SGordon Ross 			if (!repc_specified)
671*7d1ffc32SGordon Ross 				repc = 0;
672*7d1ffc32SGordon Ross 			rc = msgbuf_put_oem_string(mb, cvalp, repc);
673*7d1ffc32SGordon Ross 			if (rc != 0)
674*7d1ffc32SGordon Ross 				return (rc);
675da6c28aaSamw 			break;
676da6c28aaSamw 
677*7d1ffc32SGordon Ross 		case 'U': /* put UTF-16 string */
678da6c28aaSamw unicode_translation:
67912b65585SGordon Ross 			cvalp = va_arg(ap, char *);
680*7d1ffc32SGordon Ross 			if (!repc_specified)
681*7d1ffc32SGordon Ross 				repc = 0;
682*7d1ffc32SGordon Ross 			rc = msgbuf_put_unicode_string(mb, cvalp, repc);
683*7d1ffc32SGordon Ross 			if (rc != 0)
684*7d1ffc32SGordon 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 
705*7d1ffc32SGordon Ross /*
706*7d1ffc32SGordon Ross  * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
707*7d1ffc32SGordon Ross  * Also write a null unless the repc count limits the length we put.
708*7d1ffc32SGordon Ross  * When (repc > 0) the length we marshal must be exactly repc, and
709*7d1ffc32SGordon Ross  * truncate or pad the mb data as necessary.
710*7d1ffc32SGordon Ross  * See also: mbc_marshal_put_oem_string
711*7d1ffc32SGordon Ross  */
712*7d1ffc32SGordon Ross static int
713*7d1ffc32SGordon Ross msgbuf_put_oem_string(smb_msgbuf_t *mb, char *mbs, int repc)
714*7d1ffc32SGordon Ross {
715*7d1ffc32SGordon Ross 	uint8_t		*oembuf = NULL;
716*7d1ffc32SGordon Ross 	uint8_t		*s;
717*7d1ffc32SGordon Ross 	int		oemlen;
718*7d1ffc32SGordon Ross 	int		rlen;
719*7d1ffc32SGordon Ross 
720*7d1ffc32SGordon Ross 	/*
721*7d1ffc32SGordon Ross 	 * Compute length of converted OEM string,
722*7d1ffc32SGordon Ross 	 * NOT including null terminator
723*7d1ffc32SGordon Ross 	 */
724*7d1ffc32SGordon Ross 	if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
725*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
726*7d1ffc32SGordon Ross 
727*7d1ffc32SGordon Ross 	/*
728*7d1ffc32SGordon Ross 	 * If repc not specified, put whole string + NULL,
729*7d1ffc32SGordon Ross 	 * otherwise will truncate or pad as needed.
730*7d1ffc32SGordon Ross 	 */
731*7d1ffc32SGordon Ross 	if (repc <= 0) {
732*7d1ffc32SGordon Ross 		repc = oemlen;
733*7d1ffc32SGordon Ross 		if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
734*7d1ffc32SGordon Ross 			repc += sizeof (char);
735*7d1ffc32SGordon Ross 	}
736*7d1ffc32SGordon Ross 	if (smb_msgbuf_has_space(mb, repc) == 0)
737*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_OVERFLOW);
738*7d1ffc32SGordon Ross 
739*7d1ffc32SGordon Ross 	/*
740*7d1ffc32SGordon Ross 	 * Convert into a temporary buffer
741*7d1ffc32SGordon Ross 	 * Free oembuf in smb_msgbuf_term.
742*7d1ffc32SGordon Ross 	 */
743*7d1ffc32SGordon Ross 	oembuf = smb_msgbuf_malloc(mb, oemlen + 1);
744*7d1ffc32SGordon Ross 	if (oembuf == NULL)
745*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
746*7d1ffc32SGordon Ross 	rlen = smb_mbstooem(oembuf, mbs, oemlen);
747*7d1ffc32SGordon Ross 	if (rlen < 0)
748*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
749*7d1ffc32SGordon Ross 	if (rlen > oemlen)
750*7d1ffc32SGordon Ross 		rlen = oemlen;
751*7d1ffc32SGordon Ross 	oembuf[rlen] = '\0';
752*7d1ffc32SGordon Ross 
753*7d1ffc32SGordon Ross 	/*
754*7d1ffc32SGordon Ross 	 * Copy the converted string into the message,
755*7d1ffc32SGordon Ross 	 * truncated or paded as required.
756*7d1ffc32SGordon Ross 	 */
757*7d1ffc32SGordon Ross 	s = oembuf;
758*7d1ffc32SGordon Ross 	while (repc > 0) {
759*7d1ffc32SGordon Ross 		*mb->scan++ = *s;
760*7d1ffc32SGordon Ross 		if (*s != '\0')
761*7d1ffc32SGordon Ross 			s++;
762*7d1ffc32SGordon Ross 		repc--;
763*7d1ffc32SGordon Ross 	}
764*7d1ffc32SGordon Ross 
765*7d1ffc32SGordon Ross 	return (0);
766*7d1ffc32SGordon Ross }
767*7d1ffc32SGordon Ross 
768*7d1ffc32SGordon Ross /*
769*7d1ffc32SGordon Ross  * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
770*7d1ffc32SGordon Ross  * Also write a null unless the repc count limits the length.
771*7d1ffc32SGordon Ross  * When (repc > 0) the length we marshal must be exactly repc,
772*7d1ffc32SGordon Ross  * and truncate or pad the mb data as necessary.
773*7d1ffc32SGordon Ross  * See also: mbc_marshal_put_unicode_string
774*7d1ffc32SGordon Ross  */
775*7d1ffc32SGordon Ross static int
776*7d1ffc32SGordon Ross msgbuf_put_unicode_string(smb_msgbuf_t *mb, char *mbs, int repc)
777*7d1ffc32SGordon Ross {
778*7d1ffc32SGordon Ross 	smb_wchar_t	*wcsbuf = NULL;
779*7d1ffc32SGordon Ross 	smb_wchar_t	*wp;
780*7d1ffc32SGordon Ross 	size_t		wcslen, wcsbytes;
781*7d1ffc32SGordon Ross 	size_t		rlen;
782*7d1ffc32SGordon Ross 
783*7d1ffc32SGordon Ross 	/* align to word boundary */
784*7d1ffc32SGordon Ross 	smb_msgbuf_word_align(mb);
785*7d1ffc32SGordon Ross 
786*7d1ffc32SGordon Ross 	/*
787*7d1ffc32SGordon Ross 	 * Compute length of converted UTF-16 string,
788*7d1ffc32SGordon Ross 	 * NOT including null terminator (in bytes).
789*7d1ffc32SGordon Ross 	 */
790*7d1ffc32SGordon Ross 	wcsbytes = smb_wcequiv_strlen(mbs);
791*7d1ffc32SGordon Ross 	if (wcsbytes == (size_t)-1)
792*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
793*7d1ffc32SGordon Ross 
794*7d1ffc32SGordon Ross 	/*
795*7d1ffc32SGordon Ross 	 * If repc not specified, put whole string + NULL,
796*7d1ffc32SGordon Ross 	 * otherwise will truncate or pad as needed.
797*7d1ffc32SGordon Ross 	 */
798*7d1ffc32SGordon Ross 	if (repc <= 0) {
799*7d1ffc32SGordon Ross 		repc = (int)wcsbytes;
800*7d1ffc32SGordon Ross 		if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
801*7d1ffc32SGordon Ross 			repc += sizeof (smb_wchar_t);
802*7d1ffc32SGordon Ross 	}
803*7d1ffc32SGordon Ross 	if (smb_msgbuf_has_space(mb, repc) == 0)
804*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_OVERFLOW);
805*7d1ffc32SGordon Ross 
806*7d1ffc32SGordon Ross 	/*
807*7d1ffc32SGordon Ross 	 * Convert into a temporary buffer
808*7d1ffc32SGordon Ross 	 * Free wcsbuf in smb_msgbuf_term
809*7d1ffc32SGordon Ross 	 */
810*7d1ffc32SGordon Ross 	wcslen = wcsbytes / 2;
811*7d1ffc32SGordon Ross 	wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2);
812*7d1ffc32SGordon Ross 	if (wcsbuf == NULL)
813*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_UNDERFLOW);
814*7d1ffc32SGordon Ross 	rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
815*7d1ffc32SGordon Ross 	if (rlen == (size_t)-1)
816*7d1ffc32SGordon Ross 		return (SMB_MSGBUF_DATA_ERROR);
817*7d1ffc32SGordon Ross 	if (rlen > wcslen)
818*7d1ffc32SGordon Ross 		rlen = wcslen;
819*7d1ffc32SGordon Ross 	wcsbuf[rlen] = 0;
820*7d1ffc32SGordon Ross 
821*7d1ffc32SGordon Ross 	/*
822*7d1ffc32SGordon Ross 	 * Copy the converted string into the message,
823*7d1ffc32SGordon Ross 	 * truncated or paded as required.  Preserve
824*7d1ffc32SGordon Ross 	 * little-endian order while copying.
825*7d1ffc32SGordon Ross 	 */
826*7d1ffc32SGordon Ross 	wp = wcsbuf;
827*7d1ffc32SGordon Ross 	while (repc > 1) {
828*7d1ffc32SGordon Ross 		smb_wchar_t wchar = LE_IN16(wp);
829*7d1ffc32SGordon Ross 		LE_OUT16(mb->scan, wchar);
830*7d1ffc32SGordon Ross 		mb->scan += 2;
831*7d1ffc32SGordon Ross 		if (wchar != 0)
832*7d1ffc32SGordon Ross 			wp++;
833*7d1ffc32SGordon Ross 		repc -= sizeof (smb_wchar_t);
834*7d1ffc32SGordon Ross 	}
835*7d1ffc32SGordon Ross 	if (repc > 0)
836*7d1ffc32SGordon Ross 		*mb->scan++ = '\0';
837*7d1ffc32SGordon Ross 
838*7d1ffc32SGordon Ross 	return (0);
839*7d1ffc32SGordon Ross }
840da6c28aaSamw 
841da6c28aaSamw /*
842da6c28aaSamw  * smb_msgbuf_malloc
843da6c28aaSamw  *
844da6c28aaSamw  * Allocate some memory for use with this smb_msgbuf. We increase the
845da6c28aaSamw  * requested size to hold the list pointer and return a pointer
846da6c28aaSamw  * to the area for use by the caller.
847da6c28aaSamw  */
848da6c28aaSamw static void *
849da6c28aaSamw smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
850da6c28aaSamw {
851da6c28aaSamw 	smb_msgbuf_mlist_t *item;
852da6c28aaSamw 
853da6c28aaSamw 	size += sizeof (smb_msgbuf_mlist_t);
854da6c28aaSamw 
855b819cea2SGordon Ross #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
856da6c28aaSamw 	if ((item = malloc(size)) == NULL)
857da6c28aaSamw 		return (NULL);
858da6c28aaSamw #else
859da6c28aaSamw 	item = kmem_alloc(size, KM_SLEEP);
860da6c28aaSamw #endif
861da6c28aaSamw 	item->next = mb->mlist.next;
862da6c28aaSamw 	item->size = size;
863da6c28aaSamw 	mb->mlist.next = item;
864da6c28aaSamw 
865da6c28aaSamw 	/*
866da6c28aaSamw 	 * The caller gets a pointer to the address
867da6c28aaSamw 	 * immediately after the smb_msgbuf_mlist_t.
868da6c28aaSamw 	 */
869da6c28aaSamw 	return ((void *)(item + 1));
870da6c28aaSamw }
871da6c28aaSamw 
872da6c28aaSamw 
873da6c28aaSamw /*
874da6c28aaSamw  * smb_msgbuf_chkerc
875da6c28aaSamw  *
876da6c28aaSamw  * Diagnostic function to write an appropriate message to the system log.
877da6c28aaSamw  */
878da6c28aaSamw static int
879da6c28aaSamw smb_msgbuf_chkerc(char *text, int erc)
880da6c28aaSamw {
881da6c28aaSamw 	static struct {
882da6c28aaSamw 		int erc;
883da6c28aaSamw 		char *name;
884da6c28aaSamw 	} etable[] = {
885da6c28aaSamw 		{ SMB_MSGBUF_SUCCESS,		"success" },
886da6c28aaSamw 		{ SMB_MSGBUF_UNDERFLOW,		"overflow/underflow" },
887da6c28aaSamw 		{ SMB_MSGBUF_INVALID_FORMAT,	"invalid format" },
888da6c28aaSamw 		{ SMB_MSGBUF_INVALID_HEADER,	"invalid header" },
889da6c28aaSamw 		{ SMB_MSGBUF_DATA_ERROR,	"data error" }
890da6c28aaSamw 	};
891da6c28aaSamw 
892da6c28aaSamw 	int i;
893da6c28aaSamw 
894da6c28aaSamw 	for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) {
895da6c28aaSamw 		if (etable[i].erc == erc) {
896da6c28aaSamw 			if (text == 0)
897da6c28aaSamw 				text = "smb_msgbuf_chkerc";
898da6c28aaSamw 			break;
899da6c28aaSamw 		}
900da6c28aaSamw 	}
901da6c28aaSamw 	return (erc);
902da6c28aaSamw }
903