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