xref: /illumos-gate/usr/src/lib/libsmbfs/smb/mbuf.c (revision b7b0558ae6cf66a2c72f08f9104c1559d962bc84)
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