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