Home | History | Annotate | Download | only in smbsrv
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 /*
     29  * Msgbuf buffer management implementation. The smb_msgbuf interface is
     30  * typically used to encode or decode SMB data using sprintf/scanf
     31  * style operations. It contains special handling for the SMB header.
     32  * It can also be used for general purpose encoding and decoding.
     33  */
     34 
     35 #include <sys/types.h>
     36 #include <sys/varargs.h>
     37 #include <sys/byteorder.h>
     38 #ifndef _KERNEL
     39 #include <stdlib.h>
     40 #include <syslog.h>
     41 #include <string.h>
     42 #include <strings.h>
     43 #else
     44 #include <sys/sunddi.h>
     45 #include <sys/kmem.h>
     46 #endif
     47 #include <smbsrv/string.h>
     48 #include <smbsrv/msgbuf.h>
     49 #include <smbsrv/smb.h>
     50 
     51 static int buf_decode(smb_msgbuf_t *, char *, va_list ap);
     52 static int buf_encode(smb_msgbuf_t *, char *, va_list ap);
     53 static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
     54 static int smb_msgbuf_chkerc(char *text, int erc);
     55 static void buf_decode_wcs(mts_wchar_t *, mts_wchar_t *, int wcstrlen);
     56 
     57 /*
     58  * Returns the offset or number of bytes used within the buffer.
     59  */
     60 size_t
     61 smb_msgbuf_used(smb_msgbuf_t *mb)
     62 {
     63 	/*LINTED E_PTRDIFF_OVERFLOW*/
     64 	return (mb->scan - mb->base);
     65 }
     66 
     67 /*
     68  * Returns the actual buffer size.
     69  */
     70 size_t
     71 smb_msgbuf_size(smb_msgbuf_t *mb)
     72 {
     73 	return (mb->max);
     74 }
     75 
     76 uint8_t *
     77 smb_msgbuf_base(smb_msgbuf_t *mb)
     78 {
     79 	return (mb->base);
     80 }
     81 
     82 /*
     83  * Ensure that the scan is aligned on a word (16-bit) boundary.
     84  */
     85 void
     86 smb_msgbuf_word_align(smb_msgbuf_t *mb)
     87 {
     88 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 1) & ~1);
     89 }
     90 
     91 /*
     92  * Ensure that the scan is aligned on a dword (32-bit) boundary.
     93  */
     94 void
     95 smb_msgbuf_dword_align(smb_msgbuf_t *mb)
     96 {
     97 	mb->scan = (uint8_t *)((uintptr_t)(mb->scan + 3) & ~3);
     98 }
     99 
    100 /*
    101  * Checks whether or not the buffer has space for the amount of data
    102  * specified. Returns 1 if there is space, otherwise returns 0.
    103  */
    104 int
    105 smb_msgbuf_has_space(smb_msgbuf_t *mb, size_t size)
    106 {
    107 	if (size > mb->max || (mb->scan + size) > mb->end)
    108 		return (0);
    109 
    110 	return (1);
    111 }
    112 
    113 /*
    114  * Set flags the smb_msgbuf.
    115  */
    116 void
    117 smb_msgbuf_fset(smb_msgbuf_t *mb, uint32_t flags)
    118 {
    119 	mb->flags |= flags;
    120 }
    121 
    122 /*
    123  * Clear flags the smb_msgbuf.
    124  */
    125 void
    126 smb_msgbuf_fclear(smb_msgbuf_t *mb, uint32_t flags)
    127 {
    128 	mb->flags &= ~flags;
    129 }
    130 
    131 /*
    132  * smb_msgbuf_init
    133  *
    134  * Initialize a smb_msgbuf_t structure based on the buffer and size
    135  * specified. Both scan and base initially point to the beginning
    136  * of the buffer and end points to the limit of the buffer. As
    137  * data is added scan should be incremented to point to the next
    138  * offset at which data will be written. Max and count are set
    139  * to the actual buffer size.
    140  */
    141 void
    142 smb_msgbuf_init(smb_msgbuf_t *mb, uint8_t *buf, size_t size, uint32_t flags)
    143 {
    144 	mb->scan = mb->base = buf;
    145 	mb->max = mb->count = size;
    146 	mb->end = &buf[size];
    147 	mb->flags = flags;
    148 	mb->mlist.next = 0;
    149 }
    150 
    151 
    152 /*
    153  * smb_msgbuf_term
    154  *
    155  * Destruct a smb_msgbuf_t. Free any memory hanging off the mlist.
    156  */
    157 void
    158 smb_msgbuf_term(smb_msgbuf_t *mb)
    159 {
    160 	smb_msgbuf_mlist_t *item = mb->mlist.next;
    161 	smb_msgbuf_mlist_t *tmp;
    162 
    163 	while (item) {
    164 		tmp = item;
    165 		item = item->next;
    166 #ifndef _KERNEL
    167 		free(tmp);
    168 #else
    169 		kmem_free(tmp, tmp->size);
    170 #endif
    171 	}
    172 }
    173 
    174 
    175 /*
    176  * smb_msgbuf_decode
    177  *
    178  * Decode a smb_msgbuf buffer as indicated by the format string into
    179  * the variable arg list. This is similar to a scanf operation.
    180  *
    181  * On success, returns the number of bytes encoded. Otherwise
    182  * returns a -ve error code.
    183  */
    184 int
    185 smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
    186 {
    187 	int rc;
    188 	uint8_t *orig_scan;
    189 	va_list ap;
    190 
    191 	va_start(ap, fmt);
    192 	orig_scan = mb->scan;
    193 	rc = buf_decode(mb, fmt, ap);
    194 	va_end(ap);
    195 
    196 	if (rc != SMB_MSGBUF_SUCCESS) {
    197 		(void) smb_msgbuf_chkerc("smb_msgbuf_decode", rc);
    198 		mb->scan = orig_scan;
    199 		return (rc);
    200 	}
    201 
    202 	/*LINTED E_PTRDIFF_OVERFLOW*/
    203 	return (mb->scan - orig_scan);
    204 }
    205 
    206 
    207 /*
    208  * buf_decode
    209  *
    210  * Private decode function, where the real work of decoding the smb_msgbuf
    211  * is done. This function should only be called via smb_msgbuf_decode to
    212  * ensure correct behaviour and error handling.
    213  */
    214 static int
    215 buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
    216 {
    217 	uint32_t ival;
    218 	uint8_t c;
    219 	uint8_t *cvalp;
    220 	uint8_t **cvalpp;
    221 	uint16_t *wvalp;
    222 	uint32_t *lvalp;
    223 	uint64_t *llvalp;
    224 	mts_wchar_t *wcs;
    225 	int repc;
    226 	int rc;
    227 
    228 	while ((c = *fmt++) != 0) {
    229 		repc = 1;
    230 
    231 		if (c == ' ' || c == '\t')
    232 			continue;
    233 
    234 		if (c == '(') {
    235 			while (((c = *fmt++) != 0) && c != ')')
    236 				;
    237 
    238 			if (!c)
    239 				return (SMB_MSGBUF_SUCCESS);
    240 
    241 			continue;
    242 		}
    243 
    244 		if ('0' <= c && c <= '9') {
    245 			repc = 0;
    246 			do {
    247 				repc = repc * 10 + c - '0';
    248 				c = *fmt++;
    249 			} while ('0' <= c && c <= '9');
    250 		} else if (c == '#') {
    251 			repc = va_arg(ap, int);
    252 			c = *fmt++;
    253 		}
    254 
    255 		switch (c) {
    256 		case '.':
    257 			if (smb_msgbuf_has_space(mb, repc) == 0)
    258 				return (SMB_MSGBUF_UNDERFLOW);
    259 
    260 			mb->scan += repc;
    261 			break;
    262 
    263 		case 'c':
    264 			if (smb_msgbuf_has_space(mb, repc) == 0)
    265 				return (SMB_MSGBUF_UNDERFLOW);
    266 
    267 			cvalp = va_arg(ap, uint8_t *);
    268 			bcopy(mb->scan, cvalp, repc);
    269 			mb->scan += repc;
    270 			break;
    271 
    272 		case 'b':
    273 			if (smb_msgbuf_has_space(mb, repc) == 0)
    274 				return (SMB_MSGBUF_UNDERFLOW);
    275 
    276 			cvalp = va_arg(ap, uint8_t *);
    277 			while (repc-- > 0) {
    278 				*cvalp++ = *mb->scan++;
    279 			}
    280 			break;
    281 
    282 		case 'w':
    283 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
    284 			if (rc == 0)
    285 				return (SMB_MSGBUF_UNDERFLOW);
    286 
    287 			wvalp = va_arg(ap, uint16_t *);
    288 			while (repc-- > 0) {
    289 				*wvalp++ = LE_IN16(mb->scan);
    290 				mb->scan += sizeof (uint16_t);
    291 			}
    292 			break;
    293 
    294 		case 'l':
    295 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
    296 			if (rc == 0)
    297 				return (SMB_MSGBUF_UNDERFLOW);
    298 
    299 			lvalp = va_arg(ap, uint32_t *);
    300 			while (repc-- > 0) {
    301 				*lvalp++ = LE_IN32(mb->scan);
    302 				mb->scan += sizeof (int32_t);
    303 			}
    304 			break;
    305 
    306 		case 'q':
    307 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
    308 			if (rc == 0)
    309 				return (SMB_MSGBUF_UNDERFLOW);
    310 
    311 			llvalp = va_arg(ap, uint64_t *);
    312 			while (repc-- > 0) {
    313 				*llvalp++ = LE_IN64(mb->scan);
    314 				mb->scan += sizeof (int64_t);
    315 			}
    316 			break;
    317 
    318 		case 'u': /* Convert from unicode if flags are set */
    319 			if (mb->flags & SMB_MSGBUF_UNICODE)
    320 				goto unicode_translation;
    321 			/*FALLTHROUGH*/
    322 
    323 		case 's':
    324 			ival = strlen((const char *)mb->scan) + 1;
    325 			if (smb_msgbuf_has_space(mb, ival) == 0)
    326 				return (SMB_MSGBUF_UNDERFLOW);
    327 
    328 			if ((cvalp = smb_msgbuf_malloc(mb, ival * 2)) == 0)
    329 				return (SMB_MSGBUF_UNDERFLOW);
    330 
    331 			if ((ival = mts_stombs((char *)cvalp,
    332 			    (char *)mb->scan, ival * 2)) ==
    333 			    (uint32_t)-1) {
    334 				return (SMB_MSGBUF_DATA_ERROR);
    335 			}
    336 
    337 			cvalpp = va_arg(ap, uint8_t **);
    338 			*cvalpp = cvalp;
    339 			mb->scan += (ival+1);
    340 			break;
    341 
    342 		case 'U': /* Convert from unicode */
    343 unicode_translation:
    344 			/*
    345 			 * Unicode strings are always word aligned.
    346 			 * The malloc'd area is larger than the
    347 			 * original string because the UTF-8 chars
    348 			 * may be longer than the wide-chars.
    349 			 */
    350 			smb_msgbuf_word_align(mb);
    351 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
    352 			wcs = (mts_wchar_t *)mb->scan;
    353 
    354 			/* count the null wchar */
    355 			repc = sizeof (mts_wchar_t);
    356 			while (*wcs++)
    357 				repc += sizeof (mts_wchar_t);
    358 
    359 			if (smb_msgbuf_has_space(mb, repc) == 0)
    360 				return (SMB_MSGBUF_UNDERFLOW);
    361 
    362 			/* Decode wchar string into host byte-order */
    363 			if ((wcs = smb_msgbuf_malloc(mb, repc)) == 0)
    364 				return (SMB_MSGBUF_UNDERFLOW);
    365 
    366 			/*LINTED E_BAD_PTR_CAST_ALIGN*/
    367 			buf_decode_wcs(wcs, (mts_wchar_t *)mb->scan,
    368 			    repc / sizeof (mts_wchar_t));
    369 
    370 			/* Get space for translated string */
    371 			if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
    372 				return (SMB_MSGBUF_UNDERFLOW);
    373 
    374 			/* Translate string */
    375 			(void) mts_wcstombs((char *)cvalp, wcs, repc * 2);
    376 
    377 			cvalpp = va_arg(ap, uint8_t **);
    378 			*cvalpp = cvalp;
    379 			mb->scan += repc;
    380 			break;
    381 
    382 		case 'M':
    383 			if (smb_msgbuf_has_space(mb, 4) == 0)
    384 				return (SMB_MSGBUF_UNDERFLOW);
    385 
    386 			if (mb->scan[0] != 0xFF ||
    387 			    mb->scan[1] != 'S' ||
    388 			    mb->scan[2] != 'M' ||
    389 			    mb->scan[3] != 'B') {
    390 				return (SMB_MSGBUF_INVALID_HEADER);
    391 			}
    392 			mb->scan += 4;
    393 			break;
    394 
    395 		default:
    396 			return (SMB_MSGBUF_INVALID_FORMAT);
    397 		}
    398 	}
    399 
    400 	return (SMB_MSGBUF_SUCCESS);
    401 }
    402 
    403 
    404 /*
    405  * smb_msgbuf_encode
    406  *
    407  * Encode a smb_msgbuf buffer as indicated by the format string using
    408  * the variable arg list. This is similar to a sprintf operation.
    409  *
    410  * On success, returns the number of bytes encoded. Otherwise
    411  * returns a -ve error code.
    412  */
    413 int
    414 smb_msgbuf_encode(smb_msgbuf_t *mb, char *fmt, ...)
    415 {
    416 	int rc;
    417 	uint8_t *orig_scan;
    418 	va_list ap;
    419 
    420 	va_start(ap, fmt);
    421 	orig_scan = mb->scan;
    422 	rc = buf_encode(mb, fmt, ap);
    423 	va_end(ap);
    424 
    425 	if (rc != SMB_MSGBUF_SUCCESS) {
    426 		(void) smb_msgbuf_chkerc("smb_msgbuf_encode", rc);
    427 		mb->scan = orig_scan;
    428 		return (rc);
    429 	}
    430 
    431 	/*LINTED E_PTRDIFF_OVERFLOW*/
    432 	return (mb->scan - orig_scan);
    433 }
    434 
    435 
    436 /*
    437  * buf_encode
    438  *
    439  * Private encode function, where the real work of encoding the smb_msgbuf
    440  * is done. This function should only be called via smb_msgbuf_encode to
    441  * ensure correct behaviour and error handling.
    442  */
    443 static int
    444 buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
    445 {
    446 	uint8_t cval;
    447 	uint16_t wval;
    448 	uint32_t lval;
    449 	uint64_t llval;
    450 	uint32_t ival;
    451 	uint8_t *cvalp;
    452 	uint8_t c;
    453 	mts_wchar_t wcval;
    454 	int count;
    455 	int repc = 1;
    456 	int rc;
    457 
    458 	while ((c = *fmt++) != 0) {
    459 		repc = 1;
    460 
    461 		if (c == ' ' || c == '\t')
    462 			continue;
    463 
    464 		if (c == '(') {
    465 			while (((c = *fmt++) != 0) && c != ')')
    466 				;
    467 
    468 			if (!c)
    469 				return (SMB_MSGBUF_SUCCESS);
    470 
    471 			continue;
    472 		}
    473 
    474 		if ('0' <= c && c <= '9') {
    475 			repc = 0;
    476 			do {
    477 				repc = repc * 10 + c - '0';
    478 				c = *fmt++;
    479 			} while ('0' <= c && c <= '9');
    480 		} else if (c == '#') {
    481 			repc = va_arg(ap, int);
    482 			c = *fmt++;
    483 		}
    484 
    485 		switch (c) {
    486 		case '.':
    487 			if (smb_msgbuf_has_space(mb, repc) == 0)
    488 				return (SMB_MSGBUF_OVERFLOW);
    489 
    490 			while (repc-- > 0)
    491 				*mb->scan++ = 0;
    492 			break;
    493 
    494 		case 'c':
    495 			if (smb_msgbuf_has_space(mb, repc) == 0)
    496 				return (SMB_MSGBUF_OVERFLOW);
    497 
    498 			cvalp = va_arg(ap, uint8_t *);
    499 			bcopy(cvalp, mb->scan, repc);
    500 			mb->scan += repc;
    501 			break;
    502 
    503 		case 'b':
    504 			if (smb_msgbuf_has_space(mb, repc) == 0)
    505 				return (SMB_MSGBUF_OVERFLOW);
    506 
    507 			while (repc-- > 0) {
    508 				cval = va_arg(ap, int);
    509 				*mb->scan++ = cval;
    510 			}
    511 			break;
    512 
    513 		case 'w':
    514 			rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t));
    515 			if (rc == 0)
    516 				return (SMB_MSGBUF_OVERFLOW);
    517 
    518 			while (repc-- > 0) {
    519 				wval = va_arg(ap, int);
    520 				LE_OUT16(mb->scan, wval);
    521 				mb->scan += sizeof (uint16_t);
    522 			}
    523 			break;
    524 
    525 		case 'l':
    526 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t));
    527 			if (rc == 0)
    528 				return (SMB_MSGBUF_OVERFLOW);
    529 
    530 			while (repc-- > 0) {
    531 				lval = va_arg(ap, uint32_t);
    532 				LE_OUT32(mb->scan, lval);
    533 				mb->scan += sizeof (int32_t);
    534 			}
    535 			break;
    536 
    537 		case 'q':
    538 			rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t));
    539 			if (rc == 0)
    540 				return (SMB_MSGBUF_OVERFLOW);
    541 
    542 			while (repc-- > 0) {
    543 				llval = va_arg(ap, uint64_t);
    544 				LE_OUT64(mb->scan, llval);
    545 				mb->scan += sizeof (uint64_t);
    546 			}
    547 			break;
    548 
    549 		case 'u': /* conditional unicode */
    550 			if (mb->flags & SMB_MSGBUF_UNICODE)
    551 				goto unicode_translation;
    552 			/* FALLTHROUGH */
    553 
    554 		case 's':
    555 			cvalp = va_arg(ap, uint8_t *);
    556 			ival = strlen((const char *)cvalp) + 1;
    557 
    558 			if (smb_msgbuf_has_space(mb, ival) == 0)
    559 				return (SMB_MSGBUF_OVERFLOW);
    560 
    561 			ival =
    562 			    mts_mbstos((char *)mb->scan, (const char *)cvalp);
    563 			mb->scan += ival + 1;
    564 			break;
    565 
    566 		case 'U': /* unicode */
    567 unicode_translation:
    568 			/*
    569 			 * Unicode strings are always word aligned.
    570 			 */
    571 			smb_msgbuf_word_align(mb);
    572 			cvalp = va_arg(ap, uint8_t *);
    573 
    574 			for (;;) {
    575 				rc = smb_msgbuf_has_space(mb,
    576 				    sizeof (mts_wchar_t));
    577 				if (rc == 0)
    578 					return (SMB_MSGBUF_OVERFLOW);
    579 
    580 				count = mts_mbtowc(&wcval, (const char *)cvalp,
    581 				    MTS_MB_CHAR_MAX);
    582 
    583 				if (count < 0) {
    584 					return (SMB_MSGBUF_DATA_ERROR);
    585 				} else if (count == 0) {
    586 					/*
    587 					 * No longer need to do this now that
    588 					 * mbtowc correctly writes the null
    589 					 * before returning zero but paranoia
    590 					 * wins.
    591 					 */
    592 					wcval = 0;
    593 					count = 1;
    594 				}
    595 
    596 				/* Write wchar in wire-format */
    597 				LE_OUT16(mb->scan, wcval);
    598 
    599 				if (*cvalp == 0) {
    600 					/*
    601 					 * End of string. Check to see whether
    602 					 * or not to include the null
    603 					 * terminator.
    604 					 */
    605 					if ((mb->flags & SMB_MSGBUF_NOTERM) ==
    606 					    0)
    607 						mb->scan +=
    608 						    sizeof (mts_wchar_t);
    609 					break;
    610 				}
    611 
    612 				mb->scan += sizeof (mts_wchar_t);
    613 				cvalp += count;
    614 			}
    615 			break;
    616 
    617 		case 'M':
    618 			if (smb_msgbuf_has_space(mb, 4) == 0)
    619 				return (SMB_MSGBUF_OVERFLOW);
    620 
    621 			*mb->scan++ = 0xFF;
    622 			*mb->scan++ = 'S';
    623 			*mb->scan++ = 'M';
    624 			*mb->scan++ = 'B';
    625 			break;
    626 
    627 		default:
    628 			return (SMB_MSGBUF_INVALID_FORMAT);
    629 		}
    630 	}
    631 
    632 	return (SMB_MSGBUF_SUCCESS);
    633 }
    634 
    635 
    636 /*
    637  * smb_msgbuf_malloc
    638  *
    639  * Allocate some memory for use with this smb_msgbuf. We increase the
    640  * requested size to hold the list pointer and return a pointer
    641  * to the area for use by the caller.
    642  */
    643 static void *
    644 smb_msgbuf_malloc(smb_msgbuf_t *mb, size_t size)
    645 {
    646 	smb_msgbuf_mlist_t *item;
    647 
    648 	size += sizeof (smb_msgbuf_mlist_t);
    649 
    650 #ifndef _KERNEL
    651 	if ((item = malloc(size)) == NULL)
    652 		return (NULL);
    653 #else
    654 	item = kmem_alloc(size, KM_SLEEP);
    655 #endif
    656 	item->next = mb->mlist.next;
    657 	item->size = size;
    658 	mb->mlist.next = item;
    659 
    660 	/*
    661 	 * The caller gets a pointer to the address
    662 	 * immediately after the smb_msgbuf_mlist_t.
    663 	 */
    664 	return ((void *)(item + 1));
    665 }
    666 
    667 
    668 /*
    669  * smb_msgbuf_chkerc
    670  *
    671  * Diagnostic function to write an appropriate message to the system log.
    672  */
    673 static int
    674 smb_msgbuf_chkerc(char *text, int erc)
    675 {
    676 	static struct {
    677 		int erc;
    678 		char *name;
    679 	} etable[] = {
    680 		{ SMB_MSGBUF_SUCCESS,		"success" },
    681 		{ SMB_MSGBUF_UNDERFLOW,		"overflow/underflow" },
    682 		{ SMB_MSGBUF_INVALID_FORMAT,	"invalid format" },
    683 		{ SMB_MSGBUF_INVALID_HEADER,	"invalid header" },
    684 		{ SMB_MSGBUF_DATA_ERROR,	"data error" }
    685 	};
    686 
    687 	int i;
    688 
    689 	for (i = 0; i < sizeof (etable)/sizeof (etable[0]); ++i) {
    690 		if (etable[i].erc == erc) {
    691 			if (text == 0)
    692 				text = "smb_msgbuf_chkerc";
    693 			break;
    694 		}
    695 	}
    696 	return (erc);
    697 }
    698 
    699 static void
    700 buf_decode_wcs(mts_wchar_t *dst_wcstr, mts_wchar_t *src_wcstr, int wcstrlen)
    701 {
    702 	int i;
    703 
    704 	for (i = 0; i < wcstrlen; i++) {
    705 		*dst_wcstr = LE_IN16(src_wcstr);
    706 		dst_wcstr++;
    707 		src_wcstr++;
    708 	}
    709 }
    710