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