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