1 /* 2 * Copyright (c) 2000, Boris Popov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: mbuf.c,v 1.3 2004/12/13 00:25:22 lindak Exp $ 33 */ 34 35 /* 36 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 37 * Use is subject to license terms. 38 */ 39 40 #include <sys/types.h> 41 #include <ctype.h> 42 #include <errno.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <strings.h> 47 #include <libintl.h> 48 #include <assert.h> 49 50 #include <netsmb/smb_lib.h> 51 #include <netsmb/mchain.h> 52 53 #include "private.h" 54 #include "charsets.h" 55 56 /* 57 * Note: Leaving a little space (8 bytes) between the 58 * mbuf header and the start of the data so we can 59 * prepend a NetBIOS header in that space. 60 */ 61 #define M_ALIGNFACTOR (sizeof (long)) 62 #define M_ALIGN(len) (((len) + M_ALIGNFACTOR - 1) & ~(M_ALIGNFACTOR - 1)) 63 #define M_BASESIZE (sizeof (struct mbuf) + 8) 64 #define M_MINSIZE (1024 - M_BASESIZE) 65 #define M_TOP(m) ((char *)(m) + M_BASESIZE) 66 #define M_TRAILINGSPACE(m) ((m)->m_maxlen - (m)->m_len) 67 68 int 69 m_get(int len, struct mbuf **mpp) 70 { 71 struct mbuf *m; 72 73 assert(len < 0x100000); /* sanity */ 74 75 len = M_ALIGN(len); 76 if (len < M_MINSIZE) 77 len = M_MINSIZE; 78 m = malloc(M_BASESIZE + len); 79 if (m == NULL) 80 return (ENOMEM); 81 bzero(m, M_BASESIZE + len); 82 m->m_maxlen = len; 83 m->m_data = M_TOP(m); 84 *mpp = m; 85 return (0); 86 } 87 88 static void 89 m_free(struct mbuf *m) 90 { 91 free(m); 92 } 93 94 void 95 m_freem(struct mbuf *m0) 96 { 97 struct mbuf *m; 98 99 while (m0) { 100 m = m0->m_next; 101 m_free(m0); 102 m0 = m; 103 } 104 } 105 106 size_t 107 m_totlen(struct mbuf *m0) 108 { 109 struct mbuf *m = m0; 110 int len = 0; 111 112 while (m) { 113 len += m->m_len; 114 m = m->m_next; 115 } 116 return (len); 117 } 118 119 int 120 m_lineup(struct mbuf *m0, struct mbuf **mpp) 121 { 122 struct mbuf *nm, *m; 123 char *dp; 124 size_t len; 125 int error; 126 127 if (m0->m_next == NULL) { 128 *mpp = m0; 129 return (0); 130 } 131 if ((error = m_get(m_totlen(m0), &nm)) != 0) 132 return (error); 133 dp = mtod(nm, char *); 134 while (m0) { 135 len = m0->m_len; 136 bcopy(m0->m_data, dp, len); 137 dp += len; 138 m = m0->m_next; 139 m_free(m0); 140 m0 = m; 141 } 142 *mpp = nm; 143 return (0); 144 } 145 146 int 147 mb_init(struct mbdata *mbp) 148 { 149 return (mb_init_sz(mbp, M_MINSIZE)); 150 } 151 152 int 153 mb_init_sz(struct mbdata *mbp, int size) 154 { 155 struct mbuf *m; 156 int error; 157 158 if ((error = m_get(size, &m)) != 0) 159 return (error); 160 mb_initm(mbp, m); 161 return (0); 162 } 163 164 void 165 mb_initm(struct mbdata *mbp, struct mbuf *m) 166 { 167 bzero(mbp, sizeof (*mbp)); 168 mbp->mb_top = mbp->mb_cur = m; 169 mbp->mb_pos = mtod(m, char *); 170 } 171 172 void 173 mb_done(struct mbdata *mbp) 174 { 175 if (mbp->mb_top) { 176 m_freem(mbp->mb_top); 177 mbp->mb_top = NULL; 178 } 179 } 180 181 int 182 m_getm(struct mbuf *top, int len, struct mbuf **mpp) 183 { 184 struct mbuf *m, *mp; 185 int error, ts; 186 187 for (mp = top; ; mp = mp->m_next) { 188 ts = M_TRAILINGSPACE(mp); 189 if (len <= ts) 190 goto out; 191 len -= ts; 192 if (mp->m_next == NULL) 193 break; 194 195 } 196 if (len > 0) { 197 if ((error = m_get(len, &m)) != 0) 198 return (error); 199 mp->m_next = m; 200 } 201 out: 202 *mpp = top; 203 return (0); 204 } 205 206 /* 207 * Routines to put data in a buffer 208 */ 209 210 void * 211 mb_reserve(mbchain_t *mbp, int size) 212 { 213 char *p; 214 215 if (mb_fit(mbp, size, &p) != 0) 216 return (NULL); 217 218 return (p); 219 } 220 221 /* 222 * Check if object of size 'size' fit to the current position and 223 * allocate new mbuf if not. Advance pointers and increase length of mbuf(s). 224 * Return pointer to the object placeholder or NULL if any error occured. 225 */ 226 int 227 mb_fit(mbchain_t *mbp, int size, char **pp) 228 { 229 struct mbuf *m, *mn; 230 int error; 231 232 m = mbp->mb_cur; 233 if (M_TRAILINGSPACE(m) < (int)size) { 234 if ((error = m_get(size, &mn)) != 0) 235 return (error); 236 mbp->mb_pos = mtod(mn, char *); 237 mbp->mb_cur = m->m_next = mn; 238 m = mn; 239 } 240 m->m_len += size; 241 *pp = mbp->mb_pos; 242 mbp->mb_pos += size; 243 mbp->mb_count += size; 244 return (0); 245 } 246 247 int 248 mb_put_uint8(mbchain_t *mbp, uint8_t x) 249 { 250 uint8_t y = x; 251 return (mb_put_mem(mbp, &y, sizeof (y), MB_MINLINE)); 252 } 253 254 int 255 mb_put_uint16be(mbchain_t *mbp, uint16_t x) 256 { 257 uint16_t y = htobes(x); 258 return (mb_put_mem(mbp, &y, sizeof (y), MB_MINLINE)); 259 } 260 261 int 262 mb_put_uint16le(mbchain_t *mbp, uint16_t x) 263 { 264 uint16_t y = htoles(x); 265 return (mb_put_mem(mbp, &y, sizeof (y), MB_MINLINE)); 266 } 267 268 int 269 mb_put_uint32be(mbchain_t *mbp, uint32_t x) 270 { 271 uint32_t y = htobel(x); 272 return (mb_put_mem(mbp, &y, sizeof (y), MB_MINLINE)); 273 } 274 275 int 276 mb_put_uint32le(mbchain_t *mbp, uint32_t x) 277 { 278 uint32_t y = htolel(x); 279 return (mb_put_mem(mbp, &y, sizeof (y), MB_MINLINE)); 280 } 281 282 int 283 mb_put_uint64be(mbchain_t *mbp, uint64_t x) 284 { 285 uint64_t y = htobeq(x); 286 return (mb_put_mem(mbp, &y, sizeof (y), MB_MINLINE)); 287 } 288 289 int 290 mb_put_uint64le(mbchain_t *mbp, uint64_t x) 291 { 292 uint64_t y = htoleq(x); 293 return (mb_put_mem(mbp, &y, sizeof (y), MB_MINLINE)); 294 } 295 296 /* ARGSUSED */ 297 int 298 mb_put_mem(mbchain_t *mbp, const void *vmem, int size, int type) 299 { 300 struct mbuf *m; 301 const char *src; 302 char *dst; 303 size_t cplen; 304 int error; 305 306 if (size == 0) 307 return (0); 308 309 src = vmem; 310 m = mbp->mb_cur; 311 if ((error = m_getm(m, size, &m)) != 0) 312 return (error); 313 while (size > 0) { 314 cplen = M_TRAILINGSPACE(m); 315 if (cplen == 0) { 316 m = m->m_next; 317 continue; 318 } 319 if (cplen > size) 320 cplen = size; 321 dst = mtod(m, char *) + m->m_len; 322 if (src) { 323 bcopy(src, dst, cplen); 324 src += cplen; 325 } else 326 bzero(dst, cplen); 327 size -= cplen; 328 m->m_len += cplen; 329 mbp->mb_count += cplen; 330 } 331 mbp->mb_pos = mtod(m, char *) + m->m_len; 332 mbp->mb_cur = m; 333 return (0); 334 } 335 336 /* 337 * Append another mbuf to the mbuf chain. 338 * If what we're appending is smaller than 339 * the current trailing space, just copy. 340 * This always consumes the passed mbuf. 341 */ 342 int 343 mb_put_mbuf(mbchain_t *mbp, struct mbuf *m) 344 { 345 struct mbuf *cm = mbp->mb_cur; 346 int ts = M_TRAILINGSPACE(cm); 347 348 if (m->m_next == NULL && m->m_len <= ts) { 349 /* just copy */ 350 mb_put_mem(mbp, m->m_data, m->m_len, MB_MSYSTEM); 351 m_freem(m); 352 return (0); 353 } 354 355 cm->m_next = m; 356 while (m) { 357 mbp->mb_count += m->m_len; 358 if (m->m_next == NULL) 359 break; 360 m = m->m_next; 361 } 362 mbp->mb_pos = mtod(m, char *) + m->m_len; 363 mbp->mb_cur = m; 364 return (0); 365 } 366 367 /* 368 * Convenience function to put an OEM or Unicode string, 369 * null terminated, and aligned if necessary. 370 */ 371 int 372 mb_put_string(mbchain_t *mbp, const char *s, int uc) 373 { 374 int err; 375 376 if (uc) { 377 /* Put Unicode. align(2) first. */ 378 if (mbp->mb_count & 1) 379 mb_put_uint8(mbp, 0); 380 err = mb_put_ustring(mbp, s); 381 } else { 382 /* Put ASCII (really OEM) */ 383 err = mb_put_astring(mbp, s); 384 } 385 386 return (err); 387 } 388 389 /* 390 * Put an ASCII string (really OEM), given a UTF-8 string. 391 */ 392 int 393 mb_put_astring(mbchain_t *mbp, const char *s) 394 { 395 char *abuf; 396 int err, len; 397 398 abuf = convert_utf8_to_wincs(s); 399 if (abuf == NULL) 400 return (ENOMEM); 401 len = strlen(abuf) + 1; 402 err = mb_put_mem(mbp, abuf, len, MB_MSYSTEM); 403 free(abuf); 404 return (err); 405 } 406 407 /* 408 * Put UCS-2LE, given a UTF-8 string. 409 */ 410 int 411 mb_put_ustring(mbchain_t *mbp, const char *s) 412 { 413 uint16_t *ubuf; 414 int err, len; 415 416 ubuf = convert_utf8_to_leunicode(s); 417 if (ubuf == NULL) 418 return (ENOMEM); 419 len = 2 * (unicode_strlen(ubuf) + 1); 420 err = mb_put_mem(mbp, ubuf, len, MB_MSYSTEM); 421 free(ubuf); 422 return (err); 423 } 424 425 /* 426 * Routines for fetching data from an mbuf chain 427 */ 428 #define mb_left(m, p) (mtod(m, char *) + (m)->m_len - (p)) 429 430 int 431 md_get_uint8(mdchain_t *mbp, uint8_t *x) 432 { 433 return (md_get_mem(mbp, x, 1, MB_MINLINE)); 434 } 435 436 int 437 md_get_uint16le(mdchain_t *mbp, uint16_t *x) 438 { 439 uint16_t v; 440 int err; 441 442 if ((err = md_get_mem(mbp, &v, sizeof (v), MB_MINLINE)) != 0) 443 return (err); 444 if (x != NULL) 445 *x = letohs(v); 446 return (0); 447 } 448 449 int 450 md_get_uint16be(mdchain_t *mbp, uint16_t *x) { 451 uint16_t v; 452 int err; 453 454 if ((err = md_get_mem(mbp, &v, sizeof (v), MB_MINLINE)) != 0) 455 return (err); 456 if (x != NULL) 457 *x = betohs(v); 458 return (0); 459 } 460 461 int 462 md_get_uint32be(mdchain_t *mbp, uint32_t *x) 463 { 464 uint32_t v; 465 int err; 466 467 if ((err = md_get_mem(mbp, &v, sizeof (v), MB_MINLINE)) != 0) 468 return (err); 469 if (x != NULL) 470 *x = betohl(v); 471 return (0); 472 } 473 474 int 475 md_get_uint32le(mdchain_t *mbp, uint32_t *x) 476 { 477 uint32_t v; 478 int err; 479 480 if ((err = md_get_mem(mbp, &v, sizeof (v), MB_MINLINE)) != 0) 481 return (err); 482 if (x != NULL) 483 *x = letohl(v); 484 return (0); 485 } 486 487 int 488 md_get_uint64be(mdchain_t *mbp, uint64_t *x) 489 { 490 uint64_t v; 491 int err; 492 493 if ((err = md_get_mem(mbp, &v, sizeof (v), MB_MINLINE)) != 0) 494 return (err); 495 if (x != NULL) 496 *x = betohq(v); 497 return (0); 498 } 499 500 int 501 md_get_uint64le(mdchain_t *mbp, uint64_t *x) 502 { 503 uint64_t v; 504 int err; 505 506 if ((err = md_get_mem(mbp, &v, sizeof (v), MB_MINLINE)) != 0) 507 return (err); 508 if (x != NULL) 509 *x = letohq(v); 510 return (0); 511 } 512 513 /* ARGSUSED */ 514 int 515 md_get_mem(mdchain_t *mbp, void *vmem, int size, int type) 516 { 517 struct mbuf *m = mbp->mb_cur; 518 char *dst = vmem; 519 uint_t count; 520 521 while (size > 0) { 522 if (m == NULL) { 523 /* DPRINT("incomplete copy"); */ 524 return (EBADRPC); 525 } 526 count = mb_left(m, mbp->mb_pos); 527 if (count == 0) { 528 mbp->mb_cur = m = m->m_next; 529 if (m) 530 mbp->mb_pos = mtod(m, char *); 531 continue; 532 } 533 if (count > size) 534 count = size; 535 size -= count; 536 if (dst) { 537 if (count == 1) { 538 *dst++ = *mbp->mb_pos; 539 } else { 540 bcopy(mbp->mb_pos, dst, count); 541 dst += count; 542 } 543 } 544 mbp->mb_pos += count; 545 } 546 return (0); 547 } 548 549 /* 550 * Get the next SIZE bytes as a separate mblk. 551 * Nothing fancy here - just copy. 552 */ 553 int 554 md_get_mbuf(mdchain_t *mbp, int size, mbuf_t **ret) 555 { 556 mbuf_t *m; 557 int err; 558 559 err = m_get(size, &m); 560 if (err) 561 return (err); 562 563 err = md_get_mem(mbp, m->m_data, size, MB_MSYSTEM); 564 if (err) { 565 m_freem(m); 566 return (err); 567 } 568 m->m_len = size; 569 *ret = m; 570 571 return (0); 572 } 573 574 /* 575 * Get a string from the mbuf chain, 576 * either Unicode or OEM chars. 577 */ 578 int 579 md_get_string(mdchain_t *mbp, char **str_pp, int uc) 580 { 581 int err; 582 583 if (uc) 584 err = md_get_ustring(mbp, str_pp); 585 else 586 err = md_get_astring(mbp, str_pp); 587 return (err); 588 } 589 590 /* 591 * Get an ASCII (really OEM) string from the mbuf chain 592 * and convert it to UTF-8 593 * 594 * Similar to md_get_ustring below. 595 */ 596 int 597 md_get_astring(mdchain_t *real_mbp, char **str_pp) 598 { 599 mdchain_t tmp_mb, *mbp; 600 char *tstr, *ostr; 601 int err, i, slen; 602 uint8_t ch; 603 604 /* 605 * First, figure out the string length. 606 * Use a copy of the real_mbp so we don't 607 * actually consume it here, then search for 608 * the null (or end of data). 609 */ 610 bcopy(real_mbp, &tmp_mb, sizeof (tmp_mb)); 611 mbp = &tmp_mb; 612 slen = 0; 613 for (;;) { 614 err = md_get_uint8(mbp, &ch); 615 if (err) 616 break; 617 if (ch == 0) 618 break; 619 slen++; 620 } 621 622 /* 623 * Now read the (OEM) string for real. 624 * No need to re-check errors. 625 */ 626 tstr = malloc(slen + 1); 627 if (tstr == NULL) 628 return (ENOMEM); 629 mbp = real_mbp; 630 for (i = 0; i < slen; i++) { 631 md_get_uint8(mbp, &ch); 632 tstr[i] = ch; 633 } 634 tstr[i] = 0; 635 md_get_uint8(mbp, NULL); 636 637 /* 638 * Convert OEM to UTF-8 639 */ 640 ostr = convert_wincs_to_utf8(tstr); 641 free(tstr); 642 if (ostr == NULL) 643 return (ENOMEM); 644 645 *str_pp = ostr; 646 return (0); 647 } 648 649 /* 650 * Get a UCS-2LE string from the mbuf chain, and 651 * convert it to UTF-8. 652 * 653 * Similar to md_get_astring above. 654 */ 655 int 656 md_get_ustring(mdchain_t *real_mbp, char **str_pp) 657 { 658 mdchain_t tmp_mb, *mbp; 659 uint16_t *tstr; 660 char *ostr; 661 int err, i, slen; 662 uint16_t ch; 663 664 /* 665 * First, align(2) on the real_mbp 666 */ 667 if (((uintptr_t)real_mbp->mb_pos) & 1) 668 md_get_uint8(real_mbp, NULL); 669 670 /* 671 * Next, figure out the string length. 672 * Use a copy of the real_mbp so we don't 673 * actually consume it here, then search for 674 * the null (or end of data). 675 */ 676 bcopy(real_mbp, &tmp_mb, sizeof (tmp_mb)); 677 mbp = &tmp_mb; 678 slen = 0; 679 for (;;) { 680 err = md_get_uint16le(mbp, &ch); 681 if (err) 682 break; 683 if (ch == 0) 684 break; 685 slen++; 686 } 687 688 /* 689 * Now read the (UCS-2) string for real. 690 * No need to re-check errors. Note: 691 * This puts the UCS-2 in NATIVE order! 692 */ 693 tstr = calloc(slen + 1, 2); 694 if (tstr == NULL) 695 return (ENOMEM); 696 mbp = real_mbp; 697 for (i = 0; i < slen; i++) { 698 md_get_uint16le(mbp, &ch); 699 tstr[i] = ch; 700 } 701 tstr[i] = 0; 702 md_get_uint16le(mbp, NULL); 703 704 /* 705 * Convert UCS-2 (native!) to UTF-8 706 */ 707 ostr = convert_unicode_to_utf8(tstr); 708 free(tstr); 709 if (ostr == NULL) 710 return (ENOMEM); 711 712 *str_pp = ostr; 713 return (0); 714 } 715