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 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 70da6c28aaSamw smb_msgbuf_size(smb_msgbuf_t *mb) 71da6c28aaSamw { 72da6c28aaSamw return (mb->max); 73da6c28aaSamw } 74da6c28aaSamw 75da6c28aaSamw uint8_t * 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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