4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
29 * Msgbuf buffer management implementation. The smb_msgbuf interface is
30 * typically used to encode or decode SMB data using sprintf/scanf
31 * style operations. It contains special handling for the SMB header.
32 * It can also be used for general purpose encoding and decoding.
35 #include <sys/types.h>
36 #include <sys/varargs.h>
37 #include <sys/byteorder.h>
44 #include <sys/sunddi.h>
47 #include <smbsrv/string.h>
48 #include <smbsrv/msgbuf.h>
49 #include <smbsrv/smb.h>
51 static int buf_decode(smb_msgbuf_t
*, char *, va_list ap
);
52 static int buf_encode(smb_msgbuf_t
*, char *, va_list ap
);
53 static void *smb_msgbuf_malloc(smb_msgbuf_t
*, size_t);
54 static int smb_msgbuf_chkerc(char *text
, int erc
);
57 * Returns the offset or number of bytes used within the buffer.
60 smb_msgbuf_used(smb_msgbuf_t
*mb
)
62 /*LINTED E_PTRDIFF_OVERFLOW*/
63 return (mb
->scan
- mb
->base
);
67 * Returns the actual buffer size.
70 smb_msgbuf_size(smb_msgbuf_t
*mb
)
76 smb_msgbuf_base(smb_msgbuf_t
*mb
)
82 * Ensure that the scan is aligned on a word (16-bit) boundary.
85 smb_msgbuf_word_align(smb_msgbuf_t
*mb
)
87 mb
->scan
= (uint8_t *)((uintptr_t)(mb
->scan
+ 1) & ~1);
91 * Ensure that the scan is aligned on a dword (32-bit) boundary.
94 smb_msgbuf_dword_align(smb_msgbuf_t
*mb
)
96 mb
->scan
= (uint8_t *)((uintptr_t)(mb
->scan
+ 3) & ~3);
100 * Checks whether or not the buffer has space for the amount of data
101 * specified. Returns 1 if there is space, otherwise returns 0.
104 smb_msgbuf_has_space(smb_msgbuf_t
*mb
, size_t size
)
106 if (size
> mb
->max
|| (mb
->scan
+ size
) > mb
->end
)
113 * Set flags the smb_msgbuf.
116 smb_msgbuf_fset(smb_msgbuf_t
*mb
, uint32_t flags
)
122 * Clear flags the smb_msgbuf.
125 smb_msgbuf_fclear(smb_msgbuf_t
*mb
, uint32_t flags
)
133 * Initialize a smb_msgbuf_t structure based on the buffer and size
134 * specified. Both scan and base initially point to the beginning
135 * of the buffer and end points to the limit of the buffer. As
136 * data is added scan should be incremented to point to the next
137 * offset at which data will be written. Max and count are set
138 * to the actual buffer size.
141 smb_msgbuf_init(smb_msgbuf_t
*mb
, uint8_t *buf
, size_t size
, uint32_t flags
)
143 mb
->scan
= mb
->base
= buf
;
144 mb
->max
= mb
->count
= size
;
145 mb
->end
= &buf
[size
];
154 * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist.
157 smb_msgbuf_term(smb_msgbuf_t
*mb
)
159 smb_msgbuf_mlist_t
*item
= mb
->mlist
.next
;
160 smb_msgbuf_mlist_t
*tmp
;
165 #if !defined(_KERNEL)
168 kmem_free(tmp
, tmp
->size
);
177 * Decode a smb_msgbuf buffer as indicated by the format string into
178 * the variable arg list. This is similar to a scanf operation.
180 * On success, returns the number of bytes encoded. Otherwise
181 * returns a -ve error code.
184 smb_msgbuf_decode(smb_msgbuf_t
*mb
, char *fmt
, ...)
191 orig_scan
= mb
->scan
;
192 rc
= buf_decode(mb
, fmt
, ap
);
195 if (rc
!= SMB_MSGBUF_SUCCESS
) {
196 (void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc
);
197 mb
->scan
= orig_scan
;
201 /*LINTED E_PTRDIFF_OVERFLOW*/
202 return (mb
->scan
- orig_scan
);
209 * Private decode function, where the real work of decoding the smb_msgbuf
210 * is done. This function should only be called via smb_msgbuf_decode to
211 * ensure correct behaviour and error handling.
214 buf_decode(smb_msgbuf_t
*mb
, char *fmt
, va_list ap
)
225 boolean_t repc_specified
;
229 while ((c
= *fmt
++) != 0) {
230 repc_specified
= B_FALSE
;
233 if (c
== ' ' || c
== '\t')
237 while (((c
= *fmt
++) != 0) && c
!= ')')
241 return (SMB_MSGBUF_SUCCESS
);
246 if ('0' <= c
&& c
<= '9') {
249 repc
= repc
* 10 + c
- '0';
251 } while ('0' <= c
&& c
<= '9');
252 repc_specified
= B_TRUE
;
253 } else if (c
== '#') {
254 repc
= va_arg(ap
, int);
256 repc_specified
= B_TRUE
;
261 if (smb_msgbuf_has_space(mb
, repc
) == 0)
262 return (SMB_MSGBUF_UNDERFLOW
);
267 case 'c': /* get char */
268 if (smb_msgbuf_has_space(mb
, repc
) == 0)
269 return (SMB_MSGBUF_UNDERFLOW
);
271 bvalp
= va_arg(ap
, uint8_t *);
272 bcopy(mb
->scan
, bvalp
, repc
);
276 case 'b': /* get byte */
277 if (smb_msgbuf_has_space(mb
, repc
) == 0)
278 return (SMB_MSGBUF_UNDERFLOW
);
280 bvalp
= va_arg(ap
, uint8_t *);
282 *bvalp
++ = *mb
->scan
++;
286 case 'w': /* get word */
287 rc
= smb_msgbuf_has_space(mb
, repc
* sizeof (uint16_t));
289 return (SMB_MSGBUF_UNDERFLOW
);
291 wvalp
= va_arg(ap
, uint16_t *);
293 *wvalp
++ = LE_IN16(mb
->scan
);
294 mb
->scan
+= sizeof (uint16_t);
298 case 'l': /* get long */
299 rc
= smb_msgbuf_has_space(mb
, repc
* sizeof (int32_t));
301 return (SMB_MSGBUF_UNDERFLOW
);
303 lvalp
= va_arg(ap
, uint32_t *);
305 *lvalp
++ = LE_IN32(mb
->scan
);
306 mb
->scan
+= sizeof (int32_t);
310 case 'q': /* get quad */
311 rc
= smb_msgbuf_has_space(mb
, repc
* sizeof (int64_t));
313 return (SMB_MSGBUF_UNDERFLOW
);
315 llvalp
= va_arg(ap
, uint64_t *);
317 *llvalp
++ = LE_IN64(mb
->scan
);
318 mb
->scan
+= sizeof (int64_t);
322 case 'u': /* Convert from unicode if flags are set */
323 if (mb
->flags
& SMB_MSGBUF_UNICODE
)
324 goto unicode_translation
;
327 case 's': /* get string */
329 repc
= strlen((const char *)mb
->scan
) + 1;
330 if (smb_msgbuf_has_space(mb
, repc
) == 0)
331 return (SMB_MSGBUF_UNDERFLOW
);
332 if ((cvalp
= smb_msgbuf_malloc(mb
, repc
* 2)) == 0)
333 return (SMB_MSGBUF_UNDERFLOW
);
334 cvalpp
= va_arg(ap
, char **);
336 /* Translate OEM to mbs */
342 ival
= smb_wctomb(cvalp
, wchar
);
350 case 'U': /* get unicode string */
353 * Unicode strings are always word aligned.
354 * The malloc'd area is larger than the
355 * original string because the UTF-8 chars
356 * may be longer than the wide-chars.
358 smb_msgbuf_word_align(mb
);
359 if (!repc_specified
) {
361 * Count bytes, including the null.
363 uint8_t *tmp_scan
= mb
->scan
;
364 repc
= 2; /* the null */
365 while ((wchar
= LE_IN16(tmp_scan
)) != 0) {
370 if (smb_msgbuf_has_space(mb
, repc
) == 0)
371 return (SMB_MSGBUF_UNDERFLOW
);
373 * Get space for translated string
374 * Allocates worst-case size.
376 if ((cvalp
= smb_msgbuf_malloc(mb
, repc
* 2)) == 0)
377 return (SMB_MSGBUF_UNDERFLOW
);
378 cvalpp
= va_arg(ap
, char **);
381 * Translate unicode to mbs, stopping after
382 * null or repc limit.
385 wchar
= LE_IN16(mb
->scan
);
390 ival
= smb_wctomb(cvalp
, wchar
);
399 if (smb_msgbuf_has_space(mb
, 4) == 0)
400 return (SMB_MSGBUF_UNDERFLOW
);
402 if (mb
->scan
[0] != 0xFF ||
403 mb
->scan
[1] != 'S' ||
404 mb
->scan
[2] != 'M' ||
405 mb
->scan
[3] != 'B') {
406 return (SMB_MSGBUF_INVALID_HEADER
);
412 return (SMB_MSGBUF_INVALID_FORMAT
);
416 return (SMB_MSGBUF_SUCCESS
);
423 * Encode a smb_msgbuf buffer as indicated by the format string using
424 * the variable arg list. This is similar to a sprintf operation.
426 * On success, returns the number of bytes encoded. Otherwise
427 * returns a -ve error code.
430 smb_msgbuf_encode(smb_msgbuf_t
*mb
, char *fmt
, ...)
437 orig_scan
= mb
->scan
;
438 rc
= buf_encode(mb
, fmt
, ap
);
441 if (rc
!= SMB_MSGBUF_SUCCESS
) {
442 (void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc
);
443 mb
->scan
= orig_scan
;
447 /*LINTED E_PTRDIFF_OVERFLOW*/
448 return (mb
->scan
- orig_scan
);
455 * Private encode function, where the real work of encoding the smb_msgbuf
456 * is done. This function should only be called via smb_msgbuf_encode to
457 * ensure correct behaviour and error handling.
460 buf_encode(smb_msgbuf_t
*mb
, char *fmt
, va_list ap
)
471 boolean_t repc_specified
;
475 while ((c
= *fmt
++) != 0) {
476 repc_specified
= B_FALSE
;
479 if (c
== ' ' || c
== '\t')
483 while (((c
= *fmt
++) != 0) && c
!= ')')
487 return (SMB_MSGBUF_SUCCESS
);
492 if ('0' <= c
&& c
<= '9') {
495 repc
= repc
* 10 + c
- '0';
497 } while ('0' <= c
&& c
<= '9');
498 repc_specified
= B_TRUE
;
499 } else if (c
== '#') {
500 repc
= va_arg(ap
, int);
502 repc_specified
= B_TRUE
;
507 if (smb_msgbuf_has_space(mb
, repc
) == 0)
508 return (SMB_MSGBUF_OVERFLOW
);
514 case 'c': /* put char */
515 if (smb_msgbuf_has_space(mb
, repc
) == 0)
516 return (SMB_MSGBUF_OVERFLOW
);
518 bvalp
= va_arg(ap
, uint8_t *);
519 bcopy(bvalp
, mb
->scan
, repc
);
523 case 'b': /* put byte */
524 if (smb_msgbuf_has_space(mb
, repc
) == 0)
525 return (SMB_MSGBUF_OVERFLOW
);
528 cval
= va_arg(ap
, int);
533 case 'w': /* put word */
534 rc
= smb_msgbuf_has_space(mb
, repc
* sizeof (uint16_t));
536 return (SMB_MSGBUF_OVERFLOW
);
539 wval
= va_arg(ap
, int);
540 LE_OUT16(mb
->scan
, wval
);
541 mb
->scan
+= sizeof (uint16_t);
545 case 'l': /* put long */
546 rc
= smb_msgbuf_has_space(mb
, repc
* sizeof (int32_t));
548 return (SMB_MSGBUF_OVERFLOW
);
551 lval
= va_arg(ap
, uint32_t);
552 LE_OUT32(mb
->scan
, lval
);
553 mb
->scan
+= sizeof (int32_t);
557 case 'q': /* put quad */
558 rc
= smb_msgbuf_has_space(mb
, repc
* sizeof (int64_t));
560 return (SMB_MSGBUF_OVERFLOW
);
563 llval
= va_arg(ap
, uint64_t);
564 LE_OUT64(mb
->scan
, llval
);
565 mb
->scan
+= sizeof (uint64_t);
569 case 'u': /* conditional unicode */
570 if (mb
->flags
& SMB_MSGBUF_UNICODE
)
571 goto unicode_translation
;
574 case 's': /* put string */
575 cvalp
= va_arg(ap
, char *);
576 if (!repc_specified
) {
577 repc
= smb_sbequiv_strlen(cvalp
);
579 return (SMB_MSGBUF_OVERFLOW
);
580 if (!(mb
->flags
& SMB_MSGBUF_NOTERM
))
583 if (smb_msgbuf_has_space(mb
, repc
) == 0)
584 return (SMB_MSGBUF_OVERFLOW
);
586 count
= smb_mbtowc(&wchar
, cvalp
,
589 return (SMB_MSGBUF_DATA_ERROR
);
593 *mb
->scan
++ = (uint8_t)wchar
;
595 if (wchar
& 0xff00) {
596 *mb
->scan
++ = wchar
>> 8;
600 if (*cvalp
== '\0' && repc
> 0 &&
601 (mb
->flags
& SMB_MSGBUF_NOTERM
) == 0) {
611 case 'U': /* put unicode string */
614 * Unicode strings are always word aligned.
616 smb_msgbuf_word_align(mb
);
617 cvalp
= va_arg(ap
, char *);
618 if (!repc_specified
) {
619 repc
= smb_wcequiv_strlen(cvalp
);
620 if (!(mb
->flags
& SMB_MSGBUF_NOTERM
))
623 if (!smb_msgbuf_has_space(mb
, repc
))
624 return (SMB_MSGBUF_OVERFLOW
);
626 count
= smb_mbtowc(&wchar
, cvalp
,
629 return (SMB_MSGBUF_DATA_ERROR
);
634 LE_OUT16(mb
->scan
, wchar
);
638 if (*cvalp
== '\0' && repc
>= 2 &&
639 (mb
->flags
& SMB_MSGBUF_NOTERM
) == 0) {
640 LE_OUT16(mb
->scan
, 0);
651 if (smb_msgbuf_has_space(mb
, 4) == 0)
652 return (SMB_MSGBUF_OVERFLOW
);
661 return (SMB_MSGBUF_INVALID_FORMAT
);
665 return (SMB_MSGBUF_SUCCESS
);
672 * Allocate some memory for use with this smb_msgbuf. We increase the
673 * requested size to hold the list pointer and return a pointer
674 * to the area for use by the caller.
677 smb_msgbuf_malloc(smb_msgbuf_t
*mb
, size_t size
)
679 smb_msgbuf_mlist_t
*item
;
681 size
+= sizeof (smb_msgbuf_mlist_t
);
683 #if !defined(_KERNEL)
684 if ((item
= malloc(size
)) == NULL
)
687 item
= kmem_alloc(size
, KM_SLEEP
);
689 item
->next
= mb
->mlist
.next
;
691 mb
->mlist
.next
= item
;
694 * The caller gets a pointer to the address
695 * immediately after the smb_msgbuf_mlist_t.
697 return ((void *)(item
+ 1));
704 * Diagnostic function to write an appropriate message to the system log.
707 smb_msgbuf_chkerc(char *text
, int erc
)
713 { SMB_MSGBUF_SUCCESS
, "success" },
714 { SMB_MSGBUF_UNDERFLOW
, "overflow/underflow" },
715 { SMB_MSGBUF_INVALID_FORMAT
, "invalid format" },
716 { SMB_MSGBUF_INVALID_HEADER
, "invalid header" },
717 { SMB_MSGBUF_DATA_ERROR
, "data error" }
722 for (i
= 0; i
< sizeof (etable
)/sizeof (etable
[0]); ++i
) {
723 if (etable
[i
].erc
== erc
) {
725 text
= "smb_msgbuf_chkerc";