xref: /illumos-gate/usr/src/common/smbsrv/smb_msgbuf.c (revision bbf6f00c25b6a2bed23c35eac6d62998ecdb338c)
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 /*
22*bbf6f00cSJordan Brown  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24da6c28aaSamw  */
25da6c28aaSamw 
26da6c28aaSamw /*
27da6c28aaSamw  * Msgbuf buffer management implementation. The smb_msgbuf interface is
28da6c28aaSamw  * typically used to encode or decode SMB data using sprintf/scanf
29da6c28aaSamw  * style operations. It contains special handling for the SMB header.
30da6c28aaSamw  * It can also be used for general purpose encoding and decoding.
31da6c28aaSamw  */
32da6c28aaSamw 
33da6c28aaSamw #include <sys/types.h>
34da6c28aaSamw #include <sys/varargs.h>
35da6c28aaSamw #include <sys/byteorder.h>
36da6c28aaSamw #ifndef _KERNEL
37da6c28aaSamw #include <stdlib.h>
38da6c28aaSamw #include <syslog.h>
39da6c28aaSamw #include <string.h>
40da6c28aaSamw #include <strings.h>
41da6c28aaSamw #else
42da6c28aaSamw #include <sys/sunddi.h>
43da6c28aaSamw #include <sys/kmem.h>
44da6c28aaSamw #endif
45da6c28aaSamw #include <smbsrv/string.h>
46da6c28aaSamw #include <smbsrv/msgbuf.h>
47da6c28aaSamw #include <smbsrv/smb.h>
48da6c28aaSamw 
49da6c28aaSamw static int buf_decode(smb_msgbuf_t *, char *, va_list ap);
50da6c28aaSamw static int buf_encode(smb_msgbuf_t *, char *, va_list ap);
51da6c28aaSamw static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
52da6c28aaSamw static int smb_msgbuf_chkerc(char *text, int erc);
53*bbf6f00cSJordan Brown static void buf_decode_wcs(smb_wchar_t *, smb_wchar_t *, int wcstrlen);
54da6c28aaSamw 
55da6c28aaSamw /*
56da6c28aaSamw  * Returns the offset or number of bytes used within the buffer.
57da6c28aaSamw  */
58da6c28aaSamw size_t
59da6c28aaSamw smb_msgbuf_used(smb_msgbuf_t *mb)
60da6c28aaSamw {
61da6c28aaSamw 	/*LINTED E_PTRDIFF_OVERFLOW*/
62da6c28aaSamw 	return (mb->scan - mb->base);
63da6c28aaSamw }
64da6c28aaSamw 
65da6c28aaSamw /*
66da6c28aaSamw  * Returns the actual buffer size.
67da6c28aaSamw  */
68da6c28aaSamw size_t
69da6c28aaSamw smb_msgbuf_size(smb_msgbuf_t *mb)
70da6c28aaSamw {
71da6c28aaSamw 	return (mb->max);
72da6c28aaSamw }
73da6c28aaSamw 
74da6c28aaSamw uint8_t *
75da6c28aaSamw smb_msgbuf_base(smb_msgbuf_t *mb)
76da6c28aaSamw {
77da6c28aaSamw 	return (mb->base);
78da6c28aaSamw }
79da6c28aaSamw 
80da6c28aaSamw /*
81da6c28aaSamw  * Ensure that the scan is aligned on a word (16-bit) boundary.
82da6c28aaSamw  */
83da6c28aaSamw void
84da6c28aaSamw smb_msgbuf_word_align(smb_msgbuf_t *mb)
85da6c28aaSamw {
86da6c28aaSamw 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1);
87da6c28aaSamw }
88da6c28aaSamw 
89da6c28aaSamw /*
90da6c28aaSamw  * Ensure that the scan is aligned on a dword (32-bit) boundary.
91da6c28aaSamw  */
92da6c28aaSamw void
93da6c28aaSamw smb_msgbuf_dword_align(smb_msgbuf_t *mb)
94da6c28aaSamw {
95da6c28aaSamw 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3);
96da6c28aaSamw }
97da6c28aaSamw 
98da6c28aaSamw /*
99da6c28aaSamw  * Checks whether or not the buffer has space for the amount of data
100da6c28aaSamw  * specified. Returns 1 if there is space, otherwise returns 0.
101da6c28aaSamw  */
102da6c28aaSamw int
103da6c28aaSamw smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size)
104da6c28aaSamw {
105da6c28aaSamw 	if (size > mb->max || (mb->scan + size) > mb->end)
106da6c28aaSamw 		return (0);
107da6c28aaSamw 
108da6c28aaSamw 	return (1);
109da6c28aaSamw }
110da6c28aaSamw 
111da6c28aaSamw /*
112da6c28aaSamw  * Set flags the smb_msgbuf.
113da6c28aaSamw  */
114da6c28aaSamw void
115da6c28aaSamw smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags)
116da6c28aaSamw {
117da6c28aaSamw 	mb->flags |= flags;
118da6c28aaSamw }
119da6c28aaSamw 
120da6c28aaSamw /*
121da6c28aaSamw  * Clear flags the smb_msgbuf.
122da6c28aaSamw  */
123da6c28aaSamw void
124da6c28aaSamw smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags)
125da6c28aaSamw {
126da6c28aaSamw 	mb->flags &= ~flags;
127da6c28aaSamw }
128da6c28aaSamw 
129da6c28aaSamw /*
130da6c28aaSamw  * smb_msgbuf_init
131da6c28aaSamw  *
132da6c28aaSamw  * Initialize a smb_msgbuf_t structure based on the buffer and size
133da6c28aaSamw  * specified. Both scan and base initially point to the beginning
134da6c28aaSamw  * of the buffer and end points to the limit of the buffer. As
135da6c28aaSamw  * data is added scan should be incremented to point to the next
136da6c28aaSamw  * offset at which data will be written. Max and count are set
137da6c28aaSamw  * to the actual buffer size.
138da6c28aaSamw  */
139da6c28aaSamw void
140da6c28aaSamw smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags)
141da6c28aaSamw {
142da6c28aaSamw 	mb->scan = mb->base = buf;
143da6c28aaSamw 	mb->max = mb->count = size;
144da6c28aaSamw 	mb->end = &buf[size];
145da6c28aaSamw 	mb->flags = flags;
146da6c28aaSamw 	mb->mlist.next = 0;
147da6c28aaSamw }
148da6c28aaSamw 
149da6c28aaSamw 
150da6c28aaSamw /*
151da6c28aaSamw  * smb_msgbuf_term
152da6c28aaSamw  *
153da6c28aaSamw  * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist.
154da6c28aaSamw  */
155da6c28aaSamw void
156da6c28aaSamw smb_msgbuf_term(smb_msgbuf_t *mb)
157da6c28aaSamw {
158da6c28aaSamw 	smb_msgbuf_mlist_t *item = mb->mlist.next;
159da6c28aaSamw 	smb_msgbuf_mlist_t *tmp;
160da6c28aaSamw 
161da6c28aaSamw 	while (item) {
162da6c28aaSamw 		tmp = item;
163da6c28aaSamw 		item = item->next;
164da6c28aaSamw #ifndef _KERNEL
165da6c28aaSamw 		free(tmp);
166da6c28aaSamw #else
167da6c28aaSamw 		kmem_free(tmp, tmp->size);
168da6c28aaSamw #endif
169da6c28aaSamw 	}
170da6c28aaSamw }
171da6c28aaSamw 
172da6c28aaSamw 
173da6c28aaSamw /*
174da6c28aaSamw  * smb_msgbuf_decode
175da6c28aaSamw  *
176da6c28aaSamw  * Decode a smb_msgbuf buffer as indicated by the format string into
177da6c28aaSamw  * the variable arg list. This is similar to a scanf operation.
178da6c28aaSamw  *
179da6c28aaSamw  * On success, returns the number of bytes encoded. Otherwise
180da6c28aaSamw  * returns a -ve error code.
181da6c28aaSamw  */
182da6c28aaSamw int
183da6c28aaSamw smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
184da6c28aaSamw {
185da6c28aaSamw 	int rc;
186da6c28aaSamw 	uint8_t *orig_scan;
187da6c28aaSamw 	va_list ap;
188da6c28aaSamw 
189da6c28aaSamw 	va_start(ap, fmt);
190da6c28aaSamw 	orig_scan = mb->scan;
191da6c28aaSamw 	rc = buf_decode(mb, fmt, ap);
192da6c28aaSamw 	va_end(ap);
193da6c28aaSamw 
194da6c28aaSamw 	if (rc != SMB_MSGBUF_SUCCESS) {
195da6c28aaSamw 		(void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc);
196da6c28aaSamw 		mb->scan = orig_scan;
197da6c28aaSamw 		return (rc);
198da6c28aaSamw 	}
199da6c28aaSamw 
200da6c28aaSamw 	/*LINTED E_PTRDIFF_OVERFLOW*/
201da6c28aaSamw 	return (mb->scan - orig_scan);
202da6c28aaSamw }
203da6c28aaSamw 
204da6c28aaSamw 
205da6c28aaSamw /*
206da6c28aaSamw  * buf_decode
207da6c28aaSamw  *
208da6c28aaSamw  * Private decode function, where the real work of decoding the smb_msgbuf
209da6c28aaSamw  * is done. This function should only be called via smb_msgbuf_decode to
210da6c28aaSamw  * ensure correct behaviour and error handling.
211da6c28aaSamw  */
212da6c28aaSamw static int
213da6c28aaSamw buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
214da6c28aaSamw {
215da6c28aaSamw 	uint32_t ival;
216da6c28aaSamw 	uint8_t c;
217da6c28aaSamw 	uint8_t *cvalp;
218da6c28aaSamw 	uint8_t **cvalpp;
219da6c28aaSamw 	uint16_t *wvalp;
220da6c28aaSamw 	uint32_t *lvalp;
221da6c28aaSamw 	uint64_t *llvalp;
222*bbf6f00cSJordan Brown 	smb_wchar_t *wcs;
223da6c28aaSamw 	int repc;
224da6c28aaSamw 	int rc;
225da6c28aaSamw 
226da6c28aaSamw 	while ((c = *fmt++) != 0) {
227da6c28aaSamw 		repc = 1;
228da6c28aaSamw 
229da6c28aaSamw 		if (c == ' ' || c == '\t')
230da6c28aaSamw 			continue;
231da6c28aaSamw 
232da6c28aaSamw 		if (c == '(') {
233da6c28aaSamw 			while (((c = *fmt++) != 0) && c != ')')
234da6c28aaSamw 				;
235da6c28aaSamw 
236da6c28aaSamw 			if (!c)
237da6c28aaSamw 				return (SMB_MSGBUF_SUCCESS);
238da6c28aaSamw 
239da6c28aaSamw 			continue;
240da6c28aaSamw 		}
241da6c28aaSamw 
242da6c28aaSamw 		if ('0' <= c && c <= '9') {
243da6c28aaSamw 			repc = 0;
244da6c28aaSamw 			do {
245da6c28aaSamw 				repc = repc * 10 + c - '0';
246da6c28aaSamw 				c = *fmt++;
247da6c28aaSamw 			} while ('0' <= c && c <= '9');
248da6c28aaSamw 		} else if (c == '#') {
249da6c28aaSamw 			repc = va_arg(ap, int);
250da6c28aaSamw 			c = *fmt++;
251da6c28aaSamw 		}
252da6c28aaSamw 
253da6c28aaSamw 		switch (c) {
254da6c28aaSamw 		case '.':
255da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
256da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
257da6c28aaSamw 
258da6c28aaSamw 			mb->scan += repc;
259da6c28aaSamw 			break;
260da6c28aaSamw 
261da6c28aaSamw 		case 'c':
262da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
263da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
264da6c28aaSamw 
265da6c28aaSamw 			cvalp = va_arg(ap, uint8_t *);
266da6c28aaSamw 			bcopy(mb->scan, cvalp, repc);
267da6c28aaSamw 			mb->scan += repc;
268da6c28aaSamw 			break;
269da6c28aaSamw 
270da6c28aaSamw 		case 'b':
271da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
272da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
273da6c28aaSamw 
274da6c28aaSamw 			cvalp = va_arg(ap, uint8_t *);
275da6c28aaSamw 			while (repc-- > 0) {
276da6c28aaSamw 				*cvalp++ = *mb->scan++;
277da6c28aaSamw 			}
278da6c28aaSamw 			break;
279da6c28aaSamw 
280da6c28aaSamw 		case 'w':
281da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
282da6c28aaSamw 			if (rc == 0)
283da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
284da6c28aaSamw 
285da6c28aaSamw 			wvalp = va_arg(ap, uint16_t *);
286da6c28aaSamw 			while (repc-- > 0) {
287da6c28aaSamw 				*wvalp++ = LE_IN16(mb->scan);
288da6c28aaSamw 				mb->scan += sizeof (uint16_t);
289da6c28aaSamw 			}
290da6c28aaSamw 			break;
291da6c28aaSamw 
292da6c28aaSamw 		case 'l':
293da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
294da6c28aaSamw 			if (rc == 0)
295da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
296da6c28aaSamw 
297da6c28aaSamw 			lvalp = va_arg(ap, uint32_t *);
298da6c28aaSamw 			while (repc-- > 0) {
299da6c28aaSamw 				*lvalp++ = LE_IN32(mb->scan);
300da6c28aaSamw 				mb->scan += sizeof (int32_t);
301da6c28aaSamw 			}
302da6c28aaSamw 			break;
303da6c28aaSamw 
304da6c28aaSamw 		case 'q':
305da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
306da6c28aaSamw 			if (rc == 0)
307da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
308da6c28aaSamw 
309da6c28aaSamw 			llvalp = va_arg(ap, uint64_t *);
310da6c28aaSamw 			while (repc-- > 0) {
311da6c28aaSamw 				*llvalp++ = LE_IN64(mb->scan);
312da6c28aaSamw 				mb->scan += sizeof (int64_t);
313da6c28aaSamw 			}
314da6c28aaSamw 			break;
315da6c28aaSamw 
316da6c28aaSamw 		case 'u': /* Convert from unicode if flags are set */
317da6c28aaSamw 			if (mb->flags & SMB_MSGBUF_UNICODE)
318da6c28aaSamw 				goto unicode_translation;
319da6c28aaSamw 			/*FALLTHROUGH*/
320da6c28aaSamw 
321da6c28aaSamw 		case 's':
322da6c28aaSamw 			ival = strlen((const char *)mb->scan) + 1;
323da6c28aaSamw 			if (smb_msgbuf_has_space(mb, ival) == 0)
324da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
325da6c28aaSamw 
326da6c28aaSamw 			if ((cvalp = smb_msgbuf_malloc(mb, ival * 2)) == 0)
327da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
328da6c28aaSamw 
329*bbf6f00cSJordan Brown 			if ((ival = smb_stombs((char *)cvalp,
330da6c28aaSamw 			    (char *)mb->scan, ival * 2)) ==
331da6c28aaSamw 			    (uint32_t)-1) {
332da6c28aaSamw 				return (SMB_MSGBUF_DATA_ERROR);
333da6c28aaSamw 			}
334da6c28aaSamw 
335da6c28aaSamw 			cvalpp = va_arg(ap, uint8_t **);
336da6c28aaSamw 			*cvalpp = cvalp;
337da6c28aaSamw 			mb->scan += (ival+1);
338da6c28aaSamw 			break;
339da6c28aaSamw 
340da6c28aaSamw 		case 'U': /* Convert from unicode */
341da6c28aaSamw unicode_translation:
342da6c28aaSamw 			/*
343da6c28aaSamw 			 * Unicode strings are always word aligned.
344da6c28aaSamw 			 * The malloc'd area is larger than the
345da6c28aaSamw 			 * original string because the UTF-8 chars
346da6c28aaSamw 			 * may be longer than the wide-chars.
347da6c28aaSamw 			 */
348da6c28aaSamw 			smb_msgbuf_word_align(mb);
349da6c28aaSamw 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
350*bbf6f00cSJordan Brown 			wcs = (smb_wchar_t *)mb->scan;
351da6c28aaSamw 
352da6c28aaSamw 			/* count the null wchar */
353*bbf6f00cSJordan Brown 			repc = sizeof (smb_wchar_t);
354da6c28aaSamw 			while (*wcs++)
355*bbf6f00cSJordan Brown 				repc += sizeof (smb_wchar_t);
356da6c28aaSamw 
357da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
358da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
359da6c28aaSamw 
360da6c28aaSamw 			/* Decode wchar string into host byte-order */
361da6c28aaSamw 			if ((wcs = smb_msgbuf_malloc(mb, repc)) == 0)
362da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
363da6c28aaSamw 
364da6c28aaSamw 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
365*bbf6f00cSJordan Brown 			buf_decode_wcs(wcs, (smb_wchar_t *)mb->scan,
366*bbf6f00cSJordan Brown 			    repc / sizeof (smb_wchar_t));
367da6c28aaSamw 
368da6c28aaSamw 			/* Get space for translated string */
369da6c28aaSamw 			if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
370da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
371da6c28aaSamw 
372da6c28aaSamw 			/* Translate string */
373*bbf6f00cSJordan Brown 			(void) smb_wcstombs((char *)cvalp, wcs, repc * 2);
374da6c28aaSamw 
375da6c28aaSamw 			cvalpp = va_arg(ap, uint8_t **);
376da6c28aaSamw 			*cvalpp = cvalp;
377da6c28aaSamw 			mb->scan += repc;
378da6c28aaSamw 			break;
379da6c28aaSamw 
380da6c28aaSamw 		case 'M':
381da6c28aaSamw 			if (smb_msgbuf_has_space(mb, 4) == 0)
382da6c28aaSamw 				return (SMB_MSGBUF_UNDERFLOW);
383da6c28aaSamw 
384da6c28aaSamw 			if (mb->scan[0] != 0xFF ||
385da6c28aaSamw 			    mb->scan[1] != 'S' ||
386da6c28aaSamw 			    mb->scan[2] != 'M' ||
387da6c28aaSamw 			    mb->scan[3] != 'B') {
388da6c28aaSamw 				return (SMB_MSGBUF_INVALID_HEADER);
389da6c28aaSamw 			}
390da6c28aaSamw 			mb->scan += 4;
391da6c28aaSamw 			break;
392da6c28aaSamw 
393da6c28aaSamw 		default:
394da6c28aaSamw 			return (SMB_MSGBUF_INVALID_FORMAT);
395da6c28aaSamw 		}
396da6c28aaSamw 	}
397da6c28aaSamw 
398da6c28aaSamw 	return (SMB_MSGBUF_SUCCESS);
399da6c28aaSamw }
400da6c28aaSamw 
401da6c28aaSamw 
402da6c28aaSamw /*
403da6c28aaSamw  * smb_msgbuf_encode
404da6c28aaSamw  *
405da6c28aaSamw  * Encode a smb_msgbuf buffer as indicated by the format string using
406da6c28aaSamw  * the variable arg list. This is similar to a sprintf operation.
407da6c28aaSamw  *
408da6c28aaSamw  * On success, returns the number of bytes encoded. Otherwise
409da6c28aaSamw  * returns a -ve error code.
410da6c28aaSamw  */
411da6c28aaSamw int
412da6c28aaSamw smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...)
413da6c28aaSamw {
414da6c28aaSamw 	int rc;
415da6c28aaSamw 	uint8_t *orig_scan;
416da6c28aaSamw 	va_list ap;
417da6c28aaSamw 
418da6c28aaSamw 	va_start(ap, fmt);
419da6c28aaSamw 	orig_scan = mb->scan;
420da6c28aaSamw 	rc = buf_encode(mb, fmt, ap);
421da6c28aaSamw 	va_end(ap);
422da6c28aaSamw 
423da6c28aaSamw 	if (rc != SMB_MSGBUF_SUCCESS) {
424da6c28aaSamw 		(void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc);
425da6c28aaSamw 		mb->scan = orig_scan;
426da6c28aaSamw 		return (rc);
427da6c28aaSamw 	}
428da6c28aaSamw 
429da6c28aaSamw 	/*LINTED E_PTRDIFF_OVERFLOW*/
430da6c28aaSamw 	return (mb->scan - orig_scan);
431da6c28aaSamw }
432da6c28aaSamw 
433da6c28aaSamw 
434da6c28aaSamw /*
435da6c28aaSamw  * buf_encode
436da6c28aaSamw  *
437da6c28aaSamw  * Private encode function, where the real work of encoding the smb_msgbuf
438da6c28aaSamw  * is done. This function should only be called via smb_msgbuf_encode to
439da6c28aaSamw  * ensure correct behaviour and error handling.
440da6c28aaSamw  */
441da6c28aaSamw static int
442da6c28aaSamw buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
443da6c28aaSamw {
444da6c28aaSamw 	uint8_t cval;
445da6c28aaSamw 	uint16_t wval;
446da6c28aaSamw 	uint32_t lval;
447da6c28aaSamw 	uint64_t llval;
448da6c28aaSamw 	uint32_t ival;
449da6c28aaSamw 	uint8_t *cvalp;
450da6c28aaSamw 	uint8_t c;
451*bbf6f00cSJordan Brown 	smb_wchar_t wcval;
452da6c28aaSamw 	int count;
453da6c28aaSamw 	int repc = 1;
454da6c28aaSamw 	int rc;
455da6c28aaSamw 
456da6c28aaSamw 	while ((c = *fmt++) != 0) {
457da6c28aaSamw 		repc = 1;
458da6c28aaSamw 
459da6c28aaSamw 		if (c == ' ' || c == '\t')
460da6c28aaSamw 			continue;
461da6c28aaSamw 
462da6c28aaSamw 		if (c == '(') {
463da6c28aaSamw 			while (((c = *fmt++) != 0) && c != ')')
464da6c28aaSamw 				;
465da6c28aaSamw 
466da6c28aaSamw 			if (!c)
467da6c28aaSamw 				return (SMB_MSGBUF_SUCCESS);
468da6c28aaSamw 
469da6c28aaSamw 			continue;
470da6c28aaSamw 		}
471da6c28aaSamw 
472da6c28aaSamw 		if ('0' <= c && c <= '9') {
473da6c28aaSamw 			repc = 0;
474da6c28aaSamw 			do {
475da6c28aaSamw 				repc = repc * 10 + c - '0';
476da6c28aaSamw 				c = *fmt++;
477da6c28aaSamw 			} while ('0' <= c && c <= '9');
478da6c28aaSamw 		} else if (c == '#') {
479da6c28aaSamw 			repc = va_arg(ap, int);
480da6c28aaSamw 			c = *fmt++;
481da6c28aaSamw 		}
482da6c28aaSamw 
483da6c28aaSamw 		switch (c) {
484da6c28aaSamw 		case '.':
485da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
486da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
487da6c28aaSamw 
488da6c28aaSamw 			while (repc-- > 0)
489da6c28aaSamw 				*mb->scan++ = 0;
490da6c28aaSamw 			break;
491da6c28aaSamw 
492da6c28aaSamw 		case 'c':
493da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
494da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
495da6c28aaSamw 
496da6c28aaSamw 			cvalp = va_arg(ap, uint8_t *);
497da6c28aaSamw 			bcopy(cvalp, mb->scan, repc);
498da6c28aaSamw 			mb->scan += repc;
499da6c28aaSamw 			break;
500da6c28aaSamw 
501da6c28aaSamw 		case 'b':
502da6c28aaSamw 			if (smb_msgbuf_has_space(mb, repc) == 0)
503da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
504da6c28aaSamw 
505da6c28aaSamw 			while (repc-- > 0) {
506da6c28aaSamw 				cval = va_arg(ap, int);
507da6c28aaSamw 				*mb->scan++ = cval;
508da6c28aaSamw 			}
509da6c28aaSamw 			break;
510da6c28aaSamw 
511da6c28aaSamw 		case 'w':
512da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
513da6c28aaSamw 			if (rc == 0)
514da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
515da6c28aaSamw 
516da6c28aaSamw 			while (repc-- > 0) {
517da6c28aaSamw 				wval = va_arg(ap, int);
518da6c28aaSamw 				LE_OUT16(mb->scan, wval);
519da6c28aaSamw 				mb->scan += sizeof (uint16_t);
520da6c28aaSamw 			}
521da6c28aaSamw 			break;
522da6c28aaSamw 
523da6c28aaSamw 		case 'l':
524da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
525da6c28aaSamw 			if (rc == 0)
526da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
527da6c28aaSamw 
528da6c28aaSamw 			while (repc-- > 0) {
529da6c28aaSamw 				lval = va_arg(ap, uint32_t);
530da6c28aaSamw 				LE_OUT32(mb->scan, lval);
531da6c28aaSamw 				mb->scan += sizeof (int32_t);
532da6c28aaSamw 			}
533da6c28aaSamw 			break;
534da6c28aaSamw 
535da6c28aaSamw 		case 'q':
536da6c28aaSamw 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
537da6c28aaSamw 			if (rc == 0)
538da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
539da6c28aaSamw 
540da6c28aaSamw 			while (repc-- > 0) {
541da6c28aaSamw 				llval = va_arg(ap, uint64_t);
542da6c28aaSamw 				LE_OUT64(mb->scan, llval);
543da6c28aaSamw 				mb->scan += sizeof (uint64_t);
544da6c28aaSamw 			}
545da6c28aaSamw 			break;
546da6c28aaSamw 
547da6c28aaSamw 		case 'u': /* conditional unicode */
548da6c28aaSamw 			if (mb->flags & SMB_MSGBUF_UNICODE)
549da6c28aaSamw 				goto unicode_translation;
550da6c28aaSamw 			/* FALLTHROUGH */
551da6c28aaSamw 
552da6c28aaSamw 		case 's':
553da6c28aaSamw 			cvalp = va_arg(ap, uint8_t *);
554da6c28aaSamw 			ival = strlen((const char *)cvalp) + 1;
555da6c28aaSamw 
556da6c28aaSamw 			if (smb_msgbuf_has_space(mb, ival) == 0)
557da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
558da6c28aaSamw 
559da6c28aaSamw 			ival =
560*bbf6f00cSJordan Brown 			    smb_mbstos((char *)mb->scan, (const char *)cvalp);
561da6c28aaSamw 			mb->scan += ival + 1;
562da6c28aaSamw 			break;
563da6c28aaSamw 
564da6c28aaSamw 		case 'U': /* unicode */
565da6c28aaSamw unicode_translation:
566da6c28aaSamw 			/*
567da6c28aaSamw 			 * Unicode strings are always word aligned.
568da6c28aaSamw 			 */
569da6c28aaSamw 			smb_msgbuf_word_align(mb);
570da6c28aaSamw 			cvalp = va_arg(ap, uint8_t *);
571da6c28aaSamw 
572da6c28aaSamw 			for (;;) {
573da6c28aaSamw 				rc = smb_msgbuf_has_space(mb,
574*bbf6f00cSJordan Brown 				    sizeof (smb_wchar_t));
575da6c28aaSamw 				if (rc == 0)
576da6c28aaSamw 					return (SMB_MSGBUF_OVERFLOW);
577da6c28aaSamw 
578*bbf6f00cSJordan Brown 				count = smb_mbtowc(&wcval, (const char *)cvalp,
579da6c28aaSamw 				    MTS_MB_CHAR_MAX);
580da6c28aaSamw 
581da6c28aaSamw 				if (count < 0) {
582da6c28aaSamw 					return (SMB_MSGBUF_DATA_ERROR);
583da6c28aaSamw 				} else if (count == 0) {
584da6c28aaSamw 					/*
585da6c28aaSamw 					 * No longer need to do this now that
586da6c28aaSamw 					 * mbtowc correctly writes the null
587da6c28aaSamw 					 * before returning zero but paranoia
588da6c28aaSamw 					 * wins.
589da6c28aaSamw 					 */
590da6c28aaSamw 					wcval = 0;
591da6c28aaSamw 					count = 1;
592da6c28aaSamw 				}
593da6c28aaSamw 
594da6c28aaSamw 				/* Write wchar in wire-format */
595da6c28aaSamw 				LE_OUT16(mb->scan, wcval);
596da6c28aaSamw 
597da6c28aaSamw 				if (*cvalp == 0) {
598da6c28aaSamw 					/*
599da6c28aaSamw 					 * End of string. Check to see whether
600da6c28aaSamw 					 * or not to include the null
601da6c28aaSamw 					 * terminator.
602da6c28aaSamw 					 */
603da6c28aaSamw 					if ((mb->flags & SMB_MSGBUF_NOTERM) ==
604da6c28aaSamw 					    0)
605da6c28aaSamw 						mb->scan +=
606*bbf6f00cSJordan Brown 						    sizeof (smb_wchar_t);
607da6c28aaSamw 					break;
608da6c28aaSamw 				}
609da6c28aaSamw 
610*bbf6f00cSJordan Brown 				mb->scan += sizeof (smb_wchar_t);
611da6c28aaSamw 				cvalp += count;
612da6c28aaSamw 			}
613da6c28aaSamw 			break;
614da6c28aaSamw 
615da6c28aaSamw 		case 'M':
616da6c28aaSamw 			if (smb_msgbuf_has_space(mb, 4) == 0)
617da6c28aaSamw 				return (SMB_MSGBUF_OVERFLOW);
618da6c28aaSamw 
619da6c28aaSamw 			*mb->scan++ = 0xFF;
620da6c28aaSamw 			*mb->scan++ = 'S';
621da6c28aaSamw 			*mb->scan++ = 'M';
622da6c28aaSamw 			*mb->scan++ = 'B';
623da6c28aaSamw 			break;
624da6c28aaSamw 
625da6c28aaSamw 		default:
626da6c28aaSamw 			return (SMB_MSGBUF_INVALID_FORMAT);
627da6c28aaSamw 		}
628da6c28aaSamw 	}
629da6c28aaSamw 
630da6c28aaSamw 	return (SMB_MSGBUF_SUCCESS);
631da6c28aaSamw }
632da6c28aaSamw 
633da6c28aaSamw 
634da6c28aaSamw /*
635da6c28aaSamw  * smb_msgbuf_malloc
636da6c28aaSamw  *
637da6c28aaSamw  * Allocate some memory for use with this smb_msgbuf. We increase the
638da6c28aaSamw  * requested size to hold the list pointer and return a pointer
639da6c28aaSamw  * to the area for use by the caller.
640da6c28aaSamw  */
641da6c28aaSamw static void *
642da6c28aaSamw smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
643da6c28aaSamw {
644da6c28aaSamw 	smb_msgbuf_mlist_t *item;
645da6c28aaSamw 
646da6c28aaSamw 	size += sizeof (smb_msgbuf_mlist_t);
647da6c28aaSamw 
648da6c28aaSamw #ifndef _KERNEL
649da6c28aaSamw 	if ((item = malloc(size)) == NULL)
650da6c28aaSamw 		return (NULL);
651da6c28aaSamw #else
652da6c28aaSamw 	item = kmem_alloc(size, KM_SLEEP);
653da6c28aaSamw #endif
654da6c28aaSamw 	item->next = mb->mlist.next;
655da6c28aaSamw 	item->size = size;
656da6c28aaSamw 	mb->mlist.next = item;
657da6c28aaSamw 
658da6c28aaSamw 	/*
659da6c28aaSamw 	 * The caller gets a pointer to the address
660da6c28aaSamw 	 * immediately after the smb_msgbuf_mlist_t.
661da6c28aaSamw 	 */
662da6c28aaSamw 	return ((void *)(item + 1));
663da6c28aaSamw }
664da6c28aaSamw 
665da6c28aaSamw 
666da6c28aaSamw /*
667da6c28aaSamw  * smb_msgbuf_chkerc
668da6c28aaSamw  *
669da6c28aaSamw  * Diagnostic function to write an appropriate message to the system log.
670da6c28aaSamw  */
671da6c28aaSamw static int
672da6c28aaSamw smb_msgbuf_chkerc(char *text, int erc)
673da6c28aaSamw {
674da6c28aaSamw 	static struct {
675da6c28aaSamw 		int erc;
676da6c28aaSamw 		char *name;
677da6c28aaSamw 	} etable[] = {
678da6c28aaSamw 		{ SMB_MSGBUF_SUCCESS,		"success" },
679da6c28aaSamw 		{ SMB_MSGBUF_UNDERFLOW,		"overflow/underflow" },
680da6c28aaSamw 		{ SMB_MSGBUF_INVALID_FORMAT,	"invalid format" },
681da6c28aaSamw 		{ SMB_MSGBUF_INVALID_HEADER,	"invalid header" },
682da6c28aaSamw 		{ SMB_MSGBUF_DATA_ERROR,	"data error" }
683da6c28aaSamw 	};
684da6c28aaSamw 
685da6c28aaSamw 	int i;
686da6c28aaSamw 
687da6c28aaSamw 	for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) {
688da6c28aaSamw 		if (etable[i].erc == erc) {
689da6c28aaSamw 			if (text == 0)
690da6c28aaSamw 				text = "smb_msgbuf_chkerc";
691da6c28aaSamw 			break;
692da6c28aaSamw 		}
693da6c28aaSamw 	}
694da6c28aaSamw 	return (erc);
695da6c28aaSamw }
696da6c28aaSamw 
697da6c28aaSamw static void
698*bbf6f00cSJordan Brown buf_decode_wcs(smb_wchar_t *dst_wcstr, smb_wchar_t *src_wcstr, int wcstrlen)
699da6c28aaSamw {
700da6c28aaSamw 	int i;
701da6c28aaSamw 
702da6c28aaSamw 	for (i = 0; i < wcstrlen; i++) {
703da6c28aaSamw 		*dst_wcstr = LE_IN16(src_wcstr);
704da6c28aaSamw 		dst_wcstr++;
705da6c28aaSamw 		src_wcstr++;
706da6c28aaSamw 	}
707da6c28aaSamw }
708