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