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