summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2018-11-06 01:27:53 -0500
committerGordon Ross <gwr@nexenta.com>2019-06-03 22:09:35 -0400
commit7d1ffc32e5e72873791b96934af035e0f051fc14 (patch)
tree1b712216a61d8178e5df14b9bd4e1bb4a4a99050 /usr/src
parent07a6ae61f8958faa11352bf1b552d85d79e9cbbe (diff)
downloadillumos-joyent-7d1ffc32e5e72873791b96934af035e0f051fc14.tar.gz
7587 SMB should support enhanced Unicode
Reviewed by: Matt Barden <matt.barden@nexenta.com> Reviewed by: Evan Layton <evan.layton@nexenta.com> Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com> Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/smbsrv/Makefile3
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/.dbxrc22
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/Makefile124
-rwxr-xr-xusr/src/cmd/smbsrv/test-msgbuf/Run.sh32
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/test_conv.c342
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/test_defs.h36
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/test_main.c86
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c680
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/test_misc.c301
-rw-r--r--usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c638
-rw-r--r--usr/src/common/smbsrv/smb_match.c2
-rw-r--r--usr/src/common/smbsrv/smb_msgbuf.c452
-rw-r--r--usr/src/common/smbsrv/smb_string.c18
-rw-r--r--usr/src/common/smbsrv/smb_utf8.c251
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/mapfile-vers2
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c378
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_vops.c4
-rw-r--r--usr/src/uts/common/smbsrv/string.h15
18 files changed, 2957 insertions, 429 deletions
diff --git a/usr/src/cmd/smbsrv/Makefile b/usr/src/cmd/smbsrv/Makefile
index 641026e8e9..193ce84c88 100644
--- a/usr/src/cmd/smbsrv/Makefile
+++ b/usr/src/cmd/smbsrv/Makefile
@@ -25,7 +25,8 @@
# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
#
-SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper
+SUBDIRS = smbadm smbd smbstat dtrace fksmbd bind-helper \
+ test-msgbuf
MSGSUBDIRS = smbadm smbstat
include ../Makefile.cmd
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/.dbxrc b/usr/src/cmd/smbsrv/test-msgbuf/.dbxrc
new file mode 100644
index 0000000000..983ebcfb35
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/.dbxrc
@@ -0,0 +1,22 @@
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+#
+
+set -o emacs
+
+setenv ROOT ${CODEMGR_WS}/proto/root_i386
+setenv LD_LIBRARY_PATH ${ROOT}/usr/lib/smbsrv:${ROOT}/usr/lib:${ROOT}/lib
+
+echo debug ${ROOT}/usr/lib/smbsrv/test-msgbuf
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/Makefile b/usr/src/cmd/smbsrv/test-msgbuf/Makefile
new file mode 100644
index 0000000000..96ab0292d1
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/Makefile
@@ -0,0 +1,124 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+#
+
+
+PROG= test-msgbuf
+
+OBJS_LOCAL= test_main.o test_misc.o test_conv.o \
+ test_mbmarshal.o test_msgbuf.o
+OBJS_SMBSRV= smb_mbuf_marshaling.o smb_mbuf_util.o smb_alloc.o
+OBJS_SMBCMN= smb_msgbuf.o smb_string.o smb_utf8.o
+
+OBJS= ${OBJS_LOCAL} ${OBJS_SMBSRV} ${OBJS_SMBCMN}
+
+SMBSRV_SRCDIR=../../../uts/common/fs/smbsrv
+SMBSRV_CMNDIR=../../../common/smbsrv
+SRCS= ${OBJS_LOCAL:.o=.c} \
+ ${OBJS_SMBSRV:%.o=${SMBSRV_SRCDIR}/%.c} \
+ ${OBJS_SMBCMN:%.o=${SMBSRV_CMNDIR}/%.c}
+
+include ../../Makefile.cmd
+include ../../Makefile.ctf
+
+# Note: need our sys includes _before_ ENVCPPFLAGS, proto etc.
+CPPFLAGS.first += -I.
+CPPFLAGS.first += -I../../../lib/libfakekernel/common
+CPPFLAGS.first += -I../../../lib/smbsrv/libfksmbsrv/common
+
+INCS += -I../../../uts/common
+INCS += -I../../../uts/common/smbsrv
+INCS += -I../../../common/smbsrv
+
+CSTD= $(CSTD_GNU99)
+C99LMODE= -Xc99=%all
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+CERRWARN += -_gcc=-Wno-parentheses
+
+CPPFLAGS += -D_REENTRANT
+CPPFLAGS += -DTESTJIG
+CPPFLAGS += -Dsyslog=smb_syslog
+CPPFLAGS += -D_LARGEFILE64_SOURCE=1
+# Always debug here
+CPPFLAGS += -DDEBUG
+CPPFLAGS += $(INCS)
+
+LDFLAGS += $(ZNOLAZYLOAD)
+LDFLAGS += '-R$$ORIGIN/..'
+LDLIBS += -lfakekernel -lcmdutils
+
+LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2
+LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2
+LINTFLAGS += -xerroff=E_INCONS_ARG_DECL2
+LINTFLAGS += -xerroff=E_INCONS_VAL_TYPE_DECL2
+
+ROOTSMBDDIR = $(ROOTLIB)/smbsrv
+ROOTSMBDFILE = $(PROG:%=$(ROOTSMBDDIR)/%)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+clean:
+ -$(RM) $(OBJS)
+
+lint: # lint_SRCS
+
+include ../../Makefile.targ
+
+install: all $(ROOTSMBDFILE)
+
+test_main.o : test_main.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_KMEMUSER -c test_main.c
+ $(POST_PROCESS_O)
+
+test_misc.o : test_misc.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+ -I../../../uts/common/smbsrv \
+ -I../../../common/smbsrv -c test_misc.c
+ $(POST_PROCESS_O)
+
+# OBJS_SMBSRV
+%.o: ../../../uts/common/fs/smbsrv/%.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+ -I../../../uts/common/smbsrv \
+ -I../../../common/smbsrv -c $<
+ $(POST_PROCESS_O)
+
+# OBJS_SMBCMN
+%.o: ../../../common/smbsrv/%.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_FAKE_KERNEL \
+ -I../../../uts/common/smbsrv \
+ -I../../../common/smbsrv -c $<
+ $(POST_PROCESS_O)
+
+$(ROOTSMBDDIR)/%: %
+ $(INS.file)
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/Run.sh b/usr/src/cmd/smbsrv/test-msgbuf/Run.sh
new file mode 100755
index 0000000000..9063607a95
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/Run.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+#
+
+# Helper program to run test-msgbuf (unit test program)
+# using binaries from the proto area.
+
+[ -n "$CODEMGR_WS" ] || {
+ echo "Need a buildenv to set CODEMGR_WS=..."
+ exit 1;
+}
+
+ROOT=${CODEMGR_WS}/proto/root_i386
+LD_LIBRARY_PATH=$ROOT/usr/lib/smbsrv:$ROOT/usr/lib:$ROOT/lib
+export LD_LIBRARY_PATH
+export UMEM_DEBUG=default
+
+# run with the passed options
+exec $ROOT/usr/lib/smbsrv/test-msgbuf "$@"
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_conv.c b/usr/src/cmd/smbsrv/test-msgbuf/test_conv.c
new file mode 100644
index 0000000000..224d0373ec
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_conv.c
@@ -0,0 +1,342 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Test conversion of strings UTF-8 to/from UTF-16 etc.
+ *
+ * This tests both 16-bit unicode symbols (UCS-2) and so called
+ * "enhanced" unicode symbols such as the "poop emoji" that are
+ * above 65535 and encode to four bytes as UTF-8.
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/u8_textprep.h>
+#include <smbsrv/string.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "test_defs.h"
+
+#define U_FW_A 0xff21 // full-width A (A)
+static const char fwA[4] = "\xef\xbc\xa1";
+
+#define U_POOP 0x1f4a9 // poop emoji (💩)
+static const char poop[5] = "\xf0\x9f\x92\xa9";
+
+static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5)
+static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6)
+static smb_wchar_t wcsa[] = { 'A', U_FW_A, '.', 0 }; // (3)
+static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4)
+
+
+static void
+conv_wctomb()
+{
+ char mbs[8];
+ int len;
+
+ len = smb_wctomb(mbs, U_FW_A);
+ if (len != 3) {
+ printf("Fail: conv_wctomb fwA ret=%d\n", len);
+ return;
+ }
+ mbs[len] = '\0';
+ if (strcmp(mbs, fwA)) {
+ printf("Fail: conv_wctomb fwA cmp:\n");
+ hexdump((uchar_t *)mbs, len+1);
+ return;
+ }
+
+ len = smb_wctomb(mbs, U_POOP);
+ if (len != 4) {
+ printf("Fail: conv_wctomb poop ret=%d\n", len);
+ return;
+ }
+ mbs[len] = '\0';
+ if (strcmp(mbs, poop)) {
+ printf("Fail: conv_wctomb poop cmp:\n");
+ hexdump((uchar_t *)mbs, len+1);
+ return;
+ }
+
+ /* null wc to mbs should return 1 and put a null */
+ len = smb_wctomb(mbs, 0);
+ if (len != 1) {
+ printf("Fail: conv_wctomb null ret=%d\n", len);
+ return;
+ }
+ if (mbs[0] != '\0') {
+ printf("Fail: conv_wctomb null cmp:\n");
+ hexdump((uchar_t *)mbs, len+1);
+ return;
+ }
+
+ printf("Pass: conv_wctomb\n");
+}
+
+static void
+conv_mbtowc()
+{
+ uint32_t wch = 0;
+ int len;
+
+ /*
+ * The (void *) cast here is to let this build both
+ * before and after an interface change in smb_mbtowc
+ * (uint16_t vs uint32_t)
+ */
+ len = smb_mbtowc((void *)&wch, fwA, 4);
+ if (len != 3) {
+ printf("Fail: conv_mbtowc fwA ret=%d\n", len);
+ return;
+ }
+ if (wch != U_FW_A) {
+ printf("Fail: conv_mbtowc fwA cmp: 0x%x\n", wch);
+ return;
+ }
+
+ len = smb_mbtowc((void *)&wch, poop, 4); // poop emoji
+ if (len != 4) {
+ printf("Fail: conv_mbtowc poop ret=%d\n", len);
+ return;
+ }
+ if (wch != U_POOP) {
+ printf("Fail: conv_mbtowc poop cmp: 0x%x\n", wch);
+ return;
+ }
+
+ /* null mbs to wc should return 0 (and set wch=0) */
+ len = smb_mbtowc((void *)&wch, "", 4);
+ if (len != 0) {
+ printf("Fail: conv_mbtowc null ret=%d\n", len);
+ return;
+ }
+ if (wch != 0) {
+ printf("Fail: conv_mbtowc null cmp: 0x%x\n", wch);
+ return;
+ }
+
+ printf("Pass: conv_mbtowc\n");
+}
+
+static void
+conv_wcstombs()
+{
+ char tmbs[16];
+ int len;
+
+ len = smb_wcstombs(tmbs, wcsa, sizeof (tmbs));
+ if (len != 5) {
+ printf("Fail: conv_wcstombs A ret=%d\n", len);
+ return;
+ }
+ if (strcmp(tmbs, mbsa)) {
+ printf("Fail: conv_wcstombs A cmp:\n");
+ hexdump((uchar_t *)tmbs, len+2);
+ return;
+ }
+
+ len = smb_wcstombs(tmbs, wcsp, sizeof (tmbs));
+ if (len != 6) {
+ printf("Fail: conv_wcstombs f ret=%d\n", len);
+ return;
+ }
+ if (strcmp(tmbs, mbsp)) {
+ printf("Fail: conv_wcstombs f cmp:\n");
+ hexdump((uchar_t *)tmbs, len+2);
+ return;
+ }
+
+ printf("Pass: conv_wcstombs\n");
+}
+
+static void
+conv_mbstowcs()
+{
+ smb_wchar_t twcs[8];
+ uint32_t wch = 0;
+ int len;
+
+ len = smb_mbstowcs(twcs, mbsa, sizeof (twcs));
+ if (len != 3) {
+ printf("Fail: conv_mbstowcs A ret=%d\n", len);
+ return;
+ }
+ if (memcmp(twcs, wcsa, len+2)) {
+ printf("Fail: conv_mbstowcs A cmp: 0x%x\n", wch);
+ hexdump((uchar_t *)twcs, len+2);
+ return;
+ }
+
+ len = smb_mbstowcs(twcs, mbsp, sizeof (twcs));
+ if (len != 4) {
+ printf("Fail: conv_mbstowcs P ret=%d\n", len);
+ return;
+ }
+ if (memcmp(twcs, wcsp, len+2)) {
+ printf("Fail: conv_mbstowcs P cmp: 0x%x\n", wch);
+ hexdump((uchar_t *)twcs, len+2);
+ return;
+ }
+
+ printf("Pass: conv_mbstowcs\n");
+}
+
+/*
+ * An OEM string that will require iconv.
+ */
+static uchar_t fubar_oem[] = "F\201bar"; // CP850 x81 (ü)
+static char fubar_mbs[] = "F\303\274bar"; // UTF8 xC3 xBC
+
+
+static void
+conv_oemtombs()
+{
+ char tmbs[16];
+ int len;
+
+ len = smb_oemtombs(tmbs, (uchar_t *)"foo", 4);
+ if (len != 3) {
+ printf("Fail: conv_wctomb foo ret=%d\n", len);
+ return;
+ }
+ if (strcmp(tmbs, "foo")) {
+ printf("Fail: conv_wctomb foo cmp:\n");
+ hexdump((uchar_t *)tmbs, len+1);
+ return;
+ }
+
+ len = smb_oemtombs(tmbs, fubar_oem, 7);
+ if (len != 6) {
+ printf("Fail: conv_oemtombs fubar ret=%d\n", len);
+ return;
+ }
+ if (strcmp(tmbs, fubar_mbs)) {
+ printf("Fail: conv_oemtombs fubar cmp:\n");
+ hexdump((uchar_t *)tmbs, len+1);
+ return;
+ }
+
+ printf("Pass: conv_oemtombs\n");
+}
+
+static void
+conv_mbstooem()
+{
+ uint8_t oemcs[8];
+ uint32_t wch = 0;
+ int len;
+
+ len = smb_mbstooem(oemcs, "foo", 8);
+ if (len != 3) {
+ printf("Fail: conv_mbstooem foo ret=%d\n", len);
+ return;
+ }
+ if (memcmp(oemcs, "foo", len+1)) {
+ printf("Fail: conv_mbstooem P cmp: 0x%x\n", wch);
+ hexdump((uchar_t *)oemcs, len+1);
+ return;
+ }
+
+ len = smb_mbstooem(oemcs, fubar_mbs, 8);
+ if (len != 5) {
+ printf("Fail: conv_mbstooem fubar ret=%d\n", len);
+ return;
+ }
+ if (memcmp(oemcs, (char *)fubar_oem, len+1)) {
+ printf("Fail: conv_mbstooem fubar cmp: 0x%x\n", wch);
+ hexdump((uchar_t *)oemcs, len+1);
+ return;
+ }
+
+ len = smb_mbstooem(oemcs, mbsp, 8);
+ if (len != 3) {
+ printf("Fail: conv_mbstooem poop ret=%d\n", len);
+ return;
+ }
+ if (memcmp(oemcs, "P?.", len+1)) {
+ printf("Fail: conv_mbstooem poop cmp: 0x%x\n", wch);
+ hexdump((uchar_t *)oemcs, len+1);
+ return;
+ }
+
+ printf("Pass: conv_mbstooem\n");
+}
+
+static void
+conv_sbequiv_strlen()
+{
+ int len;
+
+ len = (int)smb_sbequiv_strlen("a");
+ if (len != 1) {
+ printf("Fail: conv_sbequiv_strlen (a) len=%d\n", len);
+ return;
+ }
+
+ len = (int)smb_sbequiv_strlen(fubar_mbs);
+ if (len != strlen((char *)fubar_oem)) {
+ printf("Fail: conv_sbequiv_strlen (fubar) len=%d\n", len);
+ return;
+ }
+
+ len = (int)smb_sbequiv_strlen(mbsp);
+ if (len != 3) { // "P?."
+ printf("Fail: conv_sbequiv_strlen (poop) len=%d\n", len);
+ return;
+ }
+
+ printf("Pass: conv_sbequiv_strlen\n");
+}
+
+static void
+conv_wcequiv_strlen()
+{
+ int len;
+
+ len = (int)smb_wcequiv_strlen("a");
+ if (len != 2) {
+ printf("Fail: conv_wcequiv_strlen (a) len=%d\n", len);
+ return;
+ }
+
+ len = (int)smb_wcequiv_strlen(fwA);
+ if (len != 2) {
+ printf("Fail: conv_wcequiv_strlen (fwA) len=%d\n", len);
+ return;
+ }
+
+ len = (int)smb_wcequiv_strlen(poop);
+ if (len != 4) {
+ printf("Fail: conv_wcequiv_strlen (poop) len=%d\n", len);
+ return;
+ }
+
+ printf("Pass: conv_wcequiv_strlen\n");
+}
+
+void
+test_conv()
+{
+ conv_wctomb();
+ conv_mbtowc();
+ conv_wcstombs();
+ conv_mbstowcs();
+ conv_oemtombs();
+ conv_mbstooem();
+ conv_sbequiv_strlen();
+ conv_wcequiv_strlen();
+}
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_defs.h b/usr/src/cmd/smbsrv/test-msgbuf/test_defs.h
new file mode 100644
index 0000000000..c7e6a4eeb1
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_defs.h
@@ -0,0 +1,36 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#ifndef _TEST_DEFS_H
+#define _TEST_DEFS_H
+
+/*
+ * Describe the purpose of the file here.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void hexdump(const uchar_t *buf, int len);
+extern void test_conv(void);
+extern void test_mbmarshal(void);
+extern void test_msgbuf(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TEST_DEFS_H */
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_main.c b/usr/src/cmd/smbsrv/test-msgbuf/test_main.c
new file mode 100644
index 0000000000..e79ccc08da
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_main.c
@@ -0,0 +1,86 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Test & debug program for smb_msgbuf.c and smb_mbuf_marshaling.c
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "test_defs.h"
+
+
+int
+main(int argc, char *argv[])
+{
+
+ test_conv();
+ test_mbmarshal();
+ test_msgbuf();
+
+ return (0);
+}
+
+void
+hexdump(const uchar_t *buf, int len)
+{
+ int idx;
+ char ascii[24];
+ char *pa = ascii;
+
+ memset(ascii, '\0', sizeof (ascii));
+
+ idx = 0;
+ while (len--) {
+ if ((idx & 15) == 0) {
+ printf("%04X: ", idx);
+ pa = ascii;
+ }
+ if (*buf > ' ' && *buf <= '~')
+ *pa++ = *buf;
+ else
+ *pa++ = '.';
+ printf("%02x ", *buf++);
+
+ idx++;
+ if ((idx & 3) == 0) {
+ *pa++ = ' ';
+ putchar(' ');
+ }
+ if ((idx & 15) == 0) {
+ *pa = '\0';
+ printf("%s\n", ascii);
+ }
+ }
+
+ if ((idx & 15) != 0) {
+ *pa = '\0';
+ /* column align the last ascii row */
+ while ((idx & 15) != 0) {
+ if ((idx & 3) == 0)
+ putchar(' ');
+ printf(" ");
+ idx++;
+ }
+ printf("%s\n", ascii);
+ }
+}
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c b/usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c
new file mode 100644
index 0000000000..967915688e
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_mbmarshal.c
@@ -0,0 +1,680 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Test putting/getting unicode strings in mbchains.
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/varargs.h>
+#include <smbsrv/smb_kproto.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "test_defs.h"
+
+static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5)
+static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6)
+static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 }; // (3)
+static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4)
+
+smb_session_t test_ssn;
+smb_request_t test_sr;
+
+/*
+ * Put ASCII string with NULL
+ */
+static void
+mbm_put_a0()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ rc = smb_mbc_encodef(mbc, "sw", "one", 42);
+ if (rc != 0) {
+ printf("Fail: mbm_put_a0 encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 6) {
+ printf("Fail: mbm_put_a0 len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 6)) {
+ printf("Fail: mbm_put_a0 cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 6);
+ return;
+ }
+
+ printf("Pass: mbm_put_a0\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+/*
+ * Put ASCII string, no NULL
+ */
+static void
+mbm_put_a1()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ rc = smb_mbc_encodef(mbc, "4sw", "one.", 42);
+ if (rc != 0) {
+ printf("Fail: mbm_put_a1 encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 6) {
+ printf("Fail: mbm_put_a1 len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 6)) {
+ printf("Fail: mbm_put_a1 cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 6);
+ return;
+ }
+
+ printf("Pass: mbm_put_a1\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_apad()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 0, 0 };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ /* Encode with wire length > strlen */
+ rc = smb_mbc_encodef(mbc, "5s", "one");
+ if (rc != 0) {
+ printf("Fail: mbm_put_apad encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 5) {
+ printf("Fail: mbm_put_apad len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 5)) {
+ printf("Fail: mbm_put_apad cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 5);
+ return;
+ }
+
+ printf("Pass: mbm_put_apad\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_atrunc()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 't', };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ /* Encode with wire length < strlen */
+ rc = smb_mbc_encodef(mbc, "4s", "onetwo");
+ if (rc != 0) {
+ printf("Fail: mbm_put_atrunc encode\n");
+ goto out;
+ }
+ /* Trunc should put exactly 4 */
+ if (mbc->chain->m_len != 4) {
+ printf("Fail: mbm_put_atrunc len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 4)) {
+ printf("Fail: mbm_put_atrunc cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 4);
+ return;
+ }
+
+ printf("Pass: mbm_put_atrunc\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+/*
+ * Put unicode string with NULL
+ */
+static void
+mbm_put_u0()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ rc = smb_mbc_encodef(mbc, "Uw", "one", 42);
+ if (rc != 0) {
+ printf("Fail: mbm_put_u0 encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 10) {
+ printf("Fail: mbm_put_u0 len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 10)) {
+ printf("Fail: mbm_put_u0 cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 10);
+ return;
+ }
+
+ printf("Pass: mbm_put_u0\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+/*
+ * Put unicode string, no NULL
+ */
+static void
+mbm_put_u1()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ rc = smb_mbc_encodef(mbc, "8Uw", "one.", 42);
+ if (rc != 0) {
+ printf("Fail: mbm_put_u1 encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 10) {
+ printf("Fail: mbm_put_u1 len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 10)) {
+ printf("Fail: mbm_put_u1 cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 10);
+ return;
+ }
+
+ printf("Pass: mbm_put_u1\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_u3()
+{
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ rc = smb_mbc_encodef(mbc, "U", mbsa);
+ if (rc != 0) {
+ printf("Fail: mbm_put_u3 encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 8) {
+ printf("Fail: mbm_put_u3 len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wcsa, 8)) {
+ printf("Fail: mbm_put_u3 cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 8);
+ return;
+ }
+
+ printf("Pass: mbm_put_u3\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_u4()
+{
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ rc = smb_mbc_encodef(mbc, "U", mbsp);
+ if (rc != 0) {
+ printf("Fail: mbm_put_u4 encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 10) {
+ printf("Fail: mbm_put_u4 len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wcsp, 10)) {
+ printf("Fail: mbm_put_u4 cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 10);
+ return;
+ }
+
+ printf("Pass: mbm_put_u4\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_upad()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 0, 0 };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ /* Encode with wire length > strlen */
+ rc = smb_mbc_encodef(mbc, "10U", "one");
+ if (rc != 0) {
+ printf("Fail: mbm_put_upad encode\n");
+ goto out;
+ }
+ if (mbc->chain->m_len != 10) {
+ printf("Fail: mbm_put_upad len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 10)) {
+ printf("Fail: mbm_put_upad cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 10);
+ return;
+ }
+
+ printf("Pass: mbm_put_upad\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+static void
+mbm_put_utrunc()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 't' };
+ mbuf_chain_t *mbc;
+ int rc;
+
+ mbc = smb_mbc_alloc(100);
+
+ /* Encode with wire length < strlen */
+ rc = smb_mbc_encodef(mbc, "8U", "onetwo");
+ if (rc != 0) {
+ printf("Fail: mbm_put_utrunc encode\n");
+ goto out;
+ }
+ /* Trunc should put exactly 8 */
+ if (mbc->chain->m_len != 8) {
+ printf("Fail: mbm_put_utrunc len=%d\n",
+ mbc->chain->m_len);
+ return;
+ }
+
+ if (memcmp(mbc->chain->m_data, wire, 8)) {
+ printf("Fail: mbm_put_utrunc cmp:\n");
+ hexdump((uchar_t *)mbc->chain->m_data, 8);
+ return;
+ }
+
+ printf("Pass: mbm_put_utrunc\n");
+
+out:
+ smb_mbc_free(mbc);
+}
+
+/*
+ * Parse an ascii string.
+ */
+static void
+mbm_get_a0()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+ rc = smb_mbc_decodef(&mbc, "%sw", &test_sr, &s, &w);
+ if (rc != 0) {
+ printf("Fail: mbm_get_a0 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: mbm_get_a0 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: mbm_get_a0 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_a0\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+/*
+ * Parse an ascii string, no NULL
+ */
+static void
+mbm_get_a1()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+ rc = smb_mbc_decodef(&mbc, "%3s.w", &test_sr, &s, &w);
+ if (rc != 0) {
+ printf("Fail: mbm_get_a1 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: mbm_get_a1 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: mbm_get_a1 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_a1\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+/* parse exactly to end of data */
+static void
+mbm_get_a2()
+{
+ uint8_t wire[] = { 'o', 'n', 'e' };
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+ rc = smb_mbc_decodef(&mbc, "%3s", &test_sr, &s);
+ if (rc != 0) {
+ printf("Fail: mbm_get_a2 decode\n");
+ goto out;
+ }
+ if (mbc.chain_offset != 3) {
+ printf("Fail: mbm_get_a2 wrong pos\n");
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: mbm_get_a2 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_a2\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+/*
+ * Parse a unicode string.
+ */
+static void
+mbm_get_u0()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+ rc = smb_mbc_decodef(&mbc, "%Uw", &test_sr, &s, &w);
+ if (rc != 0) {
+ printf("Fail: mbm_get_u0 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: mbm_get_u0 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: mbm_get_u0 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_u0\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+/*
+ * Parse a string that's NOT null terminated.
+ */
+static void
+mbm_get_u1()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+ rc = smb_mbc_decodef(&mbc, "%6U..w", &test_sr, &s, &w);
+ if (rc != 0) {
+ printf("Fail: mbm_get_u1 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: mbm_get_u1 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: mbm_get_u1 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_u1\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+/* parse exactly to end of data */
+static void
+mbm_get_u2()
+{
+ uint16_t wire[] = { 't', 'w', 'o' };
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wire, sizeof (wire));
+
+ rc = smb_mbc_decodef(&mbc, "%6U", &test_sr, &s);
+ if (rc != 0) {
+ printf("Fail: mbm_get_u2 decode\n");
+ goto out;
+ }
+ if (mbc.chain_offset != 6) {
+ printf("Fail: mbm_get_u2 wrong pos\n");
+ return;
+ }
+ if (strcmp(s, "two") != 0) {
+ printf("Fail: mbm_get_u2 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_a2\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+static void
+mbm_get_u3()
+{
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsa, sizeof (wcsa));
+
+ rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsa), &s);
+ if (rc != 0) {
+ printf("Fail: mbm_get_u3 decode\n");
+ goto out;
+ }
+ if (strcmp(s, mbsa) != 0) {
+ printf("Fail: mbm_get_u3 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_u3\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+static void
+mbm_get_u4()
+{
+ mbuf_chain_t mbc;
+ char *s;
+ int rc;
+
+ bzero(&mbc, sizeof (mbc));
+ MBC_ATTACH_BUF(&mbc, (uchar_t *)wcsp, sizeof (wcsp));
+
+ rc = smb_mbc_decodef(&mbc, "%#U", &test_sr, sizeof (wcsp), &s);
+ if (rc != 0) {
+ printf("Fail: mbm_get_u4 decode\n");
+ goto out;
+ }
+ if (strcmp(s, mbsp) != 0) {
+ printf("Fail: mbm_get_u4 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: mbm_get_u4\n");
+
+out:
+ MBC_FLUSH(&mbc);
+}
+
+void
+test_mbmarshal()
+{
+
+ smb_mbc_init();
+
+ test_ssn.dialect = 0x210; // SMB 2.1
+ test_sr.session = &test_ssn;
+ test_sr.sr_magic = SMB_REQ_MAGIC;
+ smb_srm_init(&test_sr);
+
+ mbm_put_a0();
+ mbm_put_a1();
+ mbm_put_apad();
+ mbm_put_atrunc();
+
+ mbm_put_u0();
+ mbm_put_u1();
+ mbm_put_u3();
+ mbm_put_u4();
+ mbm_put_upad();
+ mbm_put_utrunc();
+
+ mbm_get_a0();
+ mbm_get_a1();
+ mbm_get_a2();
+ mbm_get_u0();
+ mbm_get_u1();
+ mbm_get_u2();
+ mbm_get_u3();
+ mbm_get_u4();
+
+ smb_srm_fini(&test_sr);
+ smb_mbc_fini();
+}
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c b/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c
new file mode 100644
index 0000000000..c93634ba67
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_misc.c
@@ -0,0 +1,301 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * A few excerpts from smb_kutil.c
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/tzfile.h>
+#include <sys/atomic.h>
+#include <sys/debug.h>
+#include <sys/time.h>
+#include <smbsrv/smb_kproto.h>
+
+time_t tzh_leapcnt = 0;
+
+struct tm
+*smb_gmtime_r(time_t *clock, struct tm *result);
+
+time_t
+smb_timegm(struct tm *tm);
+
+struct tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+};
+
+static const int days_in_month[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+uint64_t
+smb_time_unix_to_nt(timestruc_t *unix_time)
+{
+ uint64_t nt_time;
+
+ if ((unix_time->tv_sec == 0) && (unix_time->tv_nsec == 0))
+ return (0);
+
+ nt_time = unix_time->tv_sec;
+ nt_time *= 10000000; /* seconds to 100ns */
+ nt_time += unix_time->tv_nsec / 100;
+ return (nt_time + NT_TIME_BIAS);
+}
+
+void
+smb_time_nt_to_unix(uint64_t nt_time, timestruc_t *unix_time)
+{
+ uint32_t seconds;
+
+ ASSERT(unix_time);
+
+ if ((nt_time == 0) || (nt_time == -1)) {
+ unix_time->tv_sec = 0;
+ unix_time->tv_nsec = 0;
+ return;
+ }
+
+ /*
+ * Can't represent times less than or equal NT_TIME_BIAS,
+ * so convert them to the oldest date we can store.
+ * Note that time zero is "special" being converted
+ * both directions as 0:0 (unix-to-nt, nt-to-unix).
+ */
+ if (nt_time <= NT_TIME_BIAS) {
+ unix_time->tv_sec = 0;
+ unix_time->tv_nsec = 100;
+ return;
+ }
+
+ nt_time -= NT_TIME_BIAS;
+ seconds = nt_time / 10000000;
+ unix_time->tv_sec = seconds;
+ unix_time->tv_nsec = (nt_time % 10000000) * 100;
+}
+
+
+/*
+ * smb_time_dos_to_unix
+ *
+ * Convert SMB_DATE & SMB_TIME values to a unix timestamp.
+ *
+ * A date/time field of 0 means that that server file system
+ * assigned value need not be changed. The behaviour when the
+ * date/time field is set to -1 is not documented but is
+ * generally treated like 0.
+ * If date or time is 0 or -1 the unix time is returned as 0
+ * so that the caller can identify and handle this special case.
+ */
+int32_t
+smb_time_dos_to_unix(int16_t date, int16_t time)
+{
+ struct tm atm;
+
+ if (((date == 0) || (time == 0)) ||
+ ((date == -1) || (time == -1))) {
+ return (0);
+ }
+
+ atm.tm_year = ((date >> 9) & 0x3F) + 80;
+ atm.tm_mon = ((date >> 5) & 0x0F) - 1;
+ atm.tm_mday = ((date >> 0) & 0x1F);
+ atm.tm_hour = ((time >> 11) & 0x1F);
+ atm.tm_min = ((time >> 5) & 0x3F);
+ atm.tm_sec = ((time >> 0) & 0x1F) << 1;
+
+ return (smb_timegm(&atm));
+}
+
+void
+smb_time_unix_to_dos(int32_t ux_time, int16_t *date_p, int16_t *time_p)
+{
+ struct tm atm;
+ int i;
+ time_t tmp_time;
+
+ if (ux_time == 0) {
+ *date_p = 0;
+ *time_p = 0;
+ return;
+ }
+
+ tmp_time = (time_t)ux_time;
+ (void) smb_gmtime_r(&tmp_time, &atm);
+
+ if (date_p) {
+ i = 0;
+ i += atm.tm_year - 80;
+ i <<= 4;
+ i += atm.tm_mon + 1;
+ i <<= 5;
+ i += atm.tm_mday;
+
+ *date_p = (short)i;
+ }
+ if (time_p) {
+ i = 0;
+ i += atm.tm_hour;
+ i <<= 6;
+ i += atm.tm_min;
+ i <<= 5;
+ i += atm.tm_sec >> 1;
+
+ *time_p = (short)i;
+ }
+}
+
+/*
+ * smb_gmtime_r
+ *
+ * Thread-safe version of smb_gmtime. Returns a null pointer if either
+ * input parameter is a null pointer. Otherwise returns a pointer
+ * to result.
+ *
+ * Day of the week calculation: the Epoch was a thursday.
+ *
+ * There are no timezone corrections so tm_isdst and tm_gmtoff are
+ * always zero, and the zone is always WET.
+ */
+struct tm *
+smb_gmtime_r(time_t *clock, struct tm *result)
+{
+ time_t tsec;
+ int year;
+ int month;
+ int sec_per_month;
+
+ if (clock == 0 || result == 0)
+ return (0);
+
+ bzero(result, sizeof (struct tm));
+ tsec = *clock;
+ tsec -= tzh_leapcnt;
+
+ result->tm_wday = tsec / SECSPERDAY;
+ result->tm_wday = (result->tm_wday + TM_THURSDAY) % DAYSPERWEEK;
+
+ year = EPOCH_YEAR;
+ while (tsec >= (isleap(year) ? (SECSPERDAY * DAYSPERLYEAR) :
+ (SECSPERDAY * DAYSPERNYEAR))) {
+ if (isleap(year))
+ tsec -= SECSPERDAY * DAYSPERLYEAR;
+ else
+ tsec -= SECSPERDAY * DAYSPERNYEAR;
+
+ ++year;
+ }
+
+ result->tm_year = year - TM_YEAR_BASE;
+ result->tm_yday = tsec / SECSPERDAY;
+
+ for (month = TM_JANUARY; month <= TM_DECEMBER; ++month) {
+ sec_per_month = days_in_month[month] * SECSPERDAY;
+
+ if (month == TM_FEBRUARY && isleap(year))
+ sec_per_month += SECSPERDAY;
+
+ if (tsec < sec_per_month)
+ break;
+
+ tsec -= sec_per_month;
+ }
+
+ result->tm_mon = month;
+ result->tm_mday = (tsec / SECSPERDAY) + 1;
+ tsec %= SECSPERDAY;
+ result->tm_sec = tsec % 60;
+ tsec /= 60;
+ result->tm_min = tsec % 60;
+ tsec /= 60;
+ result->tm_hour = (int)tsec;
+
+ return (result);
+}
+
+
+/*
+ * smb_timegm
+ *
+ * Converts the broken-down time in tm to a time value, i.e. the number
+ * of seconds since the Epoch (00:00:00 UTC, January 1, 1970). This is
+ * not a POSIX or ANSI function. Per the man page, the input values of
+ * tm_wday and tm_yday are ignored and, as the input data is assumed to
+ * represent GMT, we force tm_isdst and tm_gmtoff to 0.
+ *
+ * Before returning the clock time, we use smb_gmtime_r to set up tm_wday
+ * and tm_yday, and bring the other fields within normal range. I don't
+ * think this is really how it should be done but it's convenient for
+ * now.
+ */
+time_t
+smb_timegm(struct tm *tm)
+{
+ time_t tsec;
+ int dd;
+ int mm;
+ int yy;
+ int year;
+
+ if (tm == 0)
+ return (-1);
+
+ year = tm->tm_year + TM_YEAR_BASE;
+ tsec = tzh_leapcnt;
+
+ for (yy = EPOCH_YEAR; yy < year; ++yy) {
+ if (isleap(yy))
+ tsec += SECSPERDAY * DAYSPERLYEAR;
+ else
+ tsec += SECSPERDAY * DAYSPERNYEAR;
+ }
+
+ for (mm = TM_JANUARY; mm < tm->tm_mon; ++mm) {
+ dd = days_in_month[mm] * SECSPERDAY;
+
+ if (mm == TM_FEBRUARY && isleap(year))
+ dd += SECSPERDAY;
+
+ tsec += dd;
+ }
+
+ tsec += (tm->tm_mday - 1) * SECSPERDAY;
+ tsec += tm->tm_sec;
+ tsec += tm->tm_min * SECSPERMIN;
+ tsec += tm->tm_hour * SECSPERHOUR;
+
+ tm->tm_isdst = 0;
+ (void) smb_gmtime_r(&tsec, tm);
+ return (tsec);
+}
diff --git a/usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c b/usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c
new file mode 100644
index 0000000000..b58bd54b83
--- /dev/null
+++ b/usr/src/cmd/smbsrv/test-msgbuf/test_msgbuf.c
@@ -0,0 +1,638 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
+ * Test putting/getting unicode strings in mbchains.
+ */
+
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/varargs.h>
+#include <smbsrv/smb_kproto.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include "test_defs.h"
+
+static char mbsa[] = "A\xef\xbc\xa1."; // A fwA . (5)
+static char mbsp[] = "P\xf0\x9f\x92\xa9."; // P poop . (6)
+static smb_wchar_t wcsa[] = { 'A', 0xff21, '.', 0 }; // (3)
+static smb_wchar_t wcsp[] = { 'P', 0xd83d, 0xdca9, '.', 0 }; // (4)
+
+/*
+ * Put ASCII string with NULL
+ */
+static void
+msg_put_a0()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ rc = smb_msgbuf_encode(&mb, "sw", "one", 42);
+ if (rc != 6) {
+ printf("Fail: msg_put_a0 encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 6)) {
+ printf("Fail: msg_put_a0 cmp:\n");
+ hexdump((uchar_t *)temp, 6);
+ return;
+ }
+
+ printf("Pass: msg_put_a0\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/*
+ * Put ASCII string, no NULL
+ */
+static void
+msg_put_a1()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ rc = smb_msgbuf_encode(&mb, "4sw", "one.", 42);
+ if (rc != 6) {
+ printf("Fail: msg_put_a1 encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 6)) {
+ printf("Fail: msg_put_a1 cmp:\n");
+ hexdump((uchar_t *)temp, 6);
+ return;
+ }
+
+ printf("Pass: msg_put_a1\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_apad()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 0, 0 };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ /* Encode with wire length > strlen */
+ rc = smb_msgbuf_encode(&mb, "5s", "one");
+ if (rc != 5) {
+ printf("Fail: msg_put_apad encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 5)) {
+ printf("Fail: msg_put_apad cmp:\n");
+ hexdump((uchar_t *)temp, 5);
+ return;
+ }
+
+ printf("Pass: msg_put_apad\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_atrunc()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 't', };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ /* Encode with wire length < strlen */
+ rc = smb_msgbuf_encode(&mb, "4s", "onetwo");
+ /* Trunc should put exactly 4 */
+ if (rc != 4) {
+ printf("Fail: msg_put_atrunc encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 4)) {
+ printf("Fail: msg_put_atrunc cmp:\n");
+ hexdump((uchar_t *)temp, 4);
+ return;
+ }
+
+ printf("Pass: msg_put_atrunc\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/*
+ * Put unicode string with NULL
+ */
+static void
+msg_put_u0()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ rc = smb_msgbuf_encode(&mb, "Uw", "one", 42);
+ if (rc != 10) {
+ printf("Fail: msg_put_u0 encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 10)) {
+ printf("Fail: msg_put_u0 cmp:\n");
+ hexdump((uchar_t *)temp, 10);
+ return;
+ }
+
+ printf("Pass: msg_put_u0\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/*
+ * Put unicode string, no NULL
+ */
+static void
+msg_put_u1()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ rc = smb_msgbuf_encode(&mb, "8Uw", "one.", 42);
+ if (rc != 10) {
+ printf("Fail: msg_put_u1 encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 10)) {
+ printf("Fail: msg_put_u1 cmp:\n");
+ hexdump((uchar_t *)temp, 10);
+ return;
+ }
+
+ printf("Pass: msg_put_u1\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_u3()
+{
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ rc = smb_msgbuf_encode(&mb, "U", mbsa);
+ if (rc != 8) {
+ printf("Fail: msg_put_u3 encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wcsa, 8)) {
+ printf("Fail: msg_put_u3 cmp:\n");
+ hexdump((uchar_t *)temp, 8);
+ return;
+ }
+
+ printf("Pass: msg_put_u3\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_u4()
+{
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ rc = smb_msgbuf_encode(&mb, "U", mbsp);
+ if (rc != 10) {
+ printf("Fail: msg_put_u4 encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wcsp, 10)) {
+ printf("Fail: msg_put_u4 cmp:\n");
+ hexdump((uchar_t *)temp, 10);
+ return;
+ }
+
+ printf("Pass: msg_put_u4\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_upad()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 0, 0 };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ /* Encode with wire length > strlen */
+ rc = smb_msgbuf_encode(&mb, "10U", "one");
+ if (rc != 10) {
+ printf("Fail: msg_put_upad encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 10)) {
+ printf("Fail: msg_put_upad cmp:\n");
+ hexdump((uchar_t *)temp, 10);
+ return;
+ }
+
+ printf("Pass: msg_put_upad\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_put_utrunc()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 't' };
+ uint8_t temp[32];
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ int rc;
+
+ smb_msgbuf_init(&mb, temp, sizeof (temp), mbflags);
+
+ /* Encode with wire length < strlen */
+ rc = smb_msgbuf_encode(&mb, "8U", "onetwo");
+ /* Trunc should put exactly 8 */
+ if (rc != 8) {
+ printf("Fail: msg_put_utrunc encode\n");
+ goto out;
+ }
+
+ if (memcmp(temp, wire, 8)) {
+ printf("Fail: msg_put_utrunc cmp:\n");
+ hexdump((uchar_t *)temp, 8);
+ return;
+ }
+
+ printf("Pass: msg_put_utrunc\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse an ascii string.
+ */
+static void
+msg_get_a0()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "sw", &s, &w);
+ if (rc != 6) {
+ printf("Fail: msg_get_a0 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: msg_get_a0 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: msg_get_a0 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_a0\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse an ascii string, no NULL
+ */
+static void
+msg_get_a1()
+{
+ uint8_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "3s.w", &s, &w);
+ if (rc != 6) {
+ printf("Fail: msg_get_a1 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: msg_get_a1 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: msg_get_a1 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_a1\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/* parse exactly to end of data */
+static void
+msg_get_a2()
+{
+ uint8_t wire[] = { 'o', 'n', 'e' };
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+
+ smb_msgbuf_init(&mb, wire, sizeof (wire), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "3s", &s);
+ if (rc != 3) {
+ printf("Fail: msg_get_a2 decode\n");
+ goto out;
+ }
+ if (mb.scan != mb.end) {
+ printf("Fail: msg_get_a2 wrong pos\n");
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: msg_get_a2 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_a2\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse a unicode string.
+ */
+static void
+msg_get_u0()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', 0, 42, 0 };
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "Uw", &s, &w);
+ if (rc != 10) {
+ printf("Fail: msg_get_u0 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: msg_get_u0 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: msg_get_u0 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_u0\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/*
+ * Parse a string that's NOT null terminated.
+ */
+static void
+msg_get_u1()
+{
+ uint16_t wire[] = { 'o', 'n', 'e', '.', 42, 0 };
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+ uint16_t w;
+
+ smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "6U..w", &s, &w);
+ if (rc != 10) {
+ printf("Fail: msg_get_u1 decode\n");
+ goto out;
+ }
+ /*
+ * Decode a word after the string to make sure we
+ * end up positioned correctly after the string.
+ */
+ if (w != 42) {
+ printf("Fail: msg_get_u1 w=%d\n", w);
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: msg_get_u1 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_u1\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+/* parse exactly to end of data */
+static void
+msg_get_u2()
+{
+ uint16_t wire[] = { 'o', 'n', 'e' };
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+
+ smb_msgbuf_init(&mb, (uint8_t *)wire, sizeof (wire), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "6U", &s);
+ if (rc != 6) {
+ printf("Fail: msg_get_u2 decode\n");
+ goto out;
+ }
+ if (mb.scan != mb.end) {
+ printf("Fail: msg_get_u2 wrong pos\n");
+ return;
+ }
+ if (strcmp(s, "one") != 0) {
+ printf("Fail: msg_get_u2 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_u2\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_get_u3()
+{
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+
+ smb_msgbuf_init(&mb, (uint8_t *)wcsa, sizeof (wcsa), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsa), &s);
+ if (rc != 8) {
+ printf("Fail: msg_get_u3 decode\n");
+ goto out;
+ }
+ if (strcmp(s, mbsa) != 0) {
+ printf("Fail: msg_get_u3 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_u3\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+static void
+msg_get_u4()
+{
+ smb_msgbuf_t mb;
+ int mbflags = 0;
+ char *s;
+ int rc;
+
+ smb_msgbuf_init(&mb, (uint8_t *)wcsp, sizeof (wcsp), mbflags);
+
+ rc = smb_msgbuf_decode(&mb, "#U", sizeof (wcsp), &s);
+ if (rc != 10) {
+ printf("Fail: msg_get_u4 decode\n");
+ goto out;
+ }
+ if (strcmp(s, mbsp) != 0) {
+ printf("Fail: msg_get_u4 cmp: <%s>\n", s);
+ return;
+ }
+
+ printf("Pass: msg_get_u4\n");
+
+out:
+ smb_msgbuf_term(&mb);
+}
+
+void
+test_msgbuf()
+{
+
+ msg_put_a0();
+ msg_put_a1();
+ msg_put_apad();
+ msg_put_atrunc();
+
+ msg_put_u0();
+ msg_put_u1();
+ msg_put_u3();
+ msg_put_u4();
+ msg_put_upad();
+ msg_put_utrunc();
+
+ msg_get_a0();
+ msg_get_a1();
+ msg_get_a2();
+ msg_get_u0();
+ msg_get_u1();
+ msg_get_u2();
+ msg_get_u3();
+ msg_get_u4();
+
+}
diff --git a/usr/src/common/smbsrv/smb_match.c b/usr/src/common/smbsrv/smb_match.c
index e687e3cc9f..b35833ff29 100644
--- a/usr/src/common/smbsrv/smb_match.c
+++ b/usr/src/common/smbsrv/smb_match.c
@@ -142,7 +142,7 @@ smb_match_private(const char *pat, const char *str, struct match_priv *priv)
const char *limit;
char pc; /* current pattern char */
int rc;
- smb_wchar_t wcpat, wcstr; /* current wchar in pat, str */
+ uint32_t wcpat, wcstr; /* current wchar in pat, str */
int nbpat, nbstr; /* multi-byte length of it */
if (priv->depth >= SMB_MATCH_DEPTH_MAX)
diff --git a/usr/src/common/smbsrv/smb_msgbuf.c b/usr/src/common/smbsrv/smb_msgbuf.c
index 54cb75e066..b11cd39a50 100644
--- a/usr/src/common/smbsrv/smb_msgbuf.c
+++ b/usr/src/common/smbsrv/smb_msgbuf.c
@@ -22,7 +22,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -53,6 +53,12 @@ static int buf_encode(smb_msgbuf_t *, char *, va_list ap);
static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t);
static int smb_msgbuf_chkerc(char *text, int erc);
+static int msgbuf_get_oem_string(smb_msgbuf_t *, char **, int);
+static int msgbuf_get_unicode_string(smb_msgbuf_t *, char **, int);
+static int msgbuf_put_oem_string(smb_msgbuf_t *, char *, int);
+static int msgbuf_put_unicode_string(smb_msgbuf_t *, char *, int);
+
+
/*
* Returns the offset or number of bytes used within the buffer.
*/
@@ -177,7 +183,7 @@ smb_msgbuf_term(smb_msgbuf_t *mb)
* Decode a smb_msgbuf buffer as indicated by the format string into
* the variable arg list. This is similar to a scanf operation.
*
- * On success, returns the number of bytes encoded. Otherwise
+ * On success, returns the number of bytes decoded. Otherwise
* returns a -ve error code.
*/
int
@@ -213,15 +219,12 @@ smb_msgbuf_decode(smb_msgbuf_t *mb, char *fmt, ...)
static int
buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
{
- uint32_t ival;
uint8_t c;
uint8_t *bvalp;
uint16_t *wvalp;
uint32_t *lvalp;
uint64_t *llvalp;
- char *cvalp;
char **cvalpp;
- smb_wchar_t wchar;
boolean_t repc_specified;
int repc;
int rc;
@@ -324,75 +327,23 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap)
goto unicode_translation;
/*FALLTHROUGH*/
- case 's': /* get string */
- if (!repc_specified)
- repc = strlen((const char *)mb->scan) + 1;
- if (smb_msgbuf_has_space(mb, repc) == 0)
- return (SMB_MSGBUF_UNDERFLOW);
- if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
- return (SMB_MSGBUF_UNDERFLOW);
+ case 's': /* get OEM string */
cvalpp = va_arg(ap, char **);
- *cvalpp = cvalp;
- /* Translate OEM to mbs */
- while (repc > 0) {
- wchar = *mb->scan++;
- repc--;
- if (wchar == 0)
- break;
- ival = smb_wctomb(cvalp, wchar);
- cvalp += ival;
- }
- *cvalp = '\0';
- if (repc > 0)
- mb->scan += repc;
+ if (!repc_specified)
+ repc = 0;
+ rc = msgbuf_get_oem_string(mb, cvalpp, repc);
+ if (rc != 0)
+ return (rc);
break;
- case 'U': /* get unicode string */
+ case 'U': /* get UTF-16 string */
unicode_translation:
- /*
- * Unicode strings are always word aligned.
- * The malloc'd area is larger than the
- * original string because the UTF-8 chars
- * may be longer than the wide-chars.
- */
- smb_msgbuf_word_align(mb);
- if (!repc_specified) {
- /*
- * Count bytes, including the null.
- */
- uint8_t *tmp_scan = mb->scan;
- repc = 2; /* the null */
- while ((wchar = LE_IN16(tmp_scan)) != 0) {
- tmp_scan += 2;
- repc += 2;
- }
- }
- if (smb_msgbuf_has_space(mb, repc) == 0)
- return (SMB_MSGBUF_UNDERFLOW);
- /*
- * Get space for translated string
- * Allocates worst-case size.
- */
- if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0)
- return (SMB_MSGBUF_UNDERFLOW);
cvalpp = va_arg(ap, char **);
- *cvalpp = cvalp;
- /*
- * Translate unicode to mbs, stopping after
- * null or repc limit.
- */
- while (repc >= 2) {
- wchar = LE_IN16(mb->scan);
- mb->scan += 2;
- repc -= 2;
- if (wchar == 0)
- break;
- ival = smb_wctomb(cvalp, wchar);
- cvalp += ival;
- }
- *cvalp = '\0';
- if (repc > 0)
- mb->scan += repc;
+ if (!repc_specified)
+ repc = 0;
+ rc = msgbuf_get_unicode_string(mb, cvalpp, repc);
+ if (rc != 0)
+ return (rc);
break;
case 'M':
@@ -416,6 +367,151 @@ unicode_translation:
return (SMB_MSGBUF_SUCCESS);
}
+/*
+ * msgbuf_get_oem_string
+ *
+ * Decode an OEM string, returning its UTF-8 form in strpp,
+ * allocated using smb_msgbuf_malloc (automatically freed).
+ * If max_bytes != 0, consume at most max_bytes of the mb.
+ * See also: mbc_marshal_get_oem_string
+ */
+static int
+msgbuf_get_oem_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
+{
+ char *mbs;
+ uint8_t *oembuf = NULL;
+ int oemlen; // len of OEM string, w/o null
+ int datalen; // OtW data len
+ int mbsmax; // max len of ret str
+ int rlen;
+
+ if (max_bytes == 0)
+ max_bytes = 0xffff;
+
+ /*
+ * Determine the OtW data length and OEM string length
+ * Note: oemlen is the string length (w/o null) and
+ * datalen is how much we move mb->scan
+ */
+ datalen = 0;
+ oemlen = 0;
+ for (;;) {
+ if (datalen >= max_bytes)
+ break;
+ /* in-line smb_msgbuf_has_space */
+ if ((mb->scan + datalen) >= mb->end)
+ return (SMB_MSGBUF_UNDERFLOW);
+ datalen++;
+ if (mb->scan[datalen - 1] == 0)
+ break;
+ oemlen++;
+ }
+
+ /*
+ * Get datalen bytes into a temp buffer
+ * sized with room to add a null.
+ * Free oembuf in smb_msgbuf_term
+ */
+ oembuf = smb_msgbuf_malloc(mb, datalen + 1);
+ if (oembuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ bcopy(mb->scan, oembuf, datalen);
+ mb->scan += datalen;
+ oembuf[oemlen] = '\0';
+
+ /*
+ * Get the buffer we'll return and convert to UTF-8.
+ * May take as much as double the space.
+ */
+ mbsmax = oemlen * 2;
+ mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
+ if (mbs == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_oemtombs(mbs, oembuf, mbsmax);
+ if (rlen < 0)
+ return (SMB_MSGBUF_UNDERFLOW);
+ if (rlen > mbsmax)
+ rlen = mbsmax;
+ mbs[rlen] = '\0';
+ *strpp = mbs;
+ return (0);
+}
+
+/*
+ * msgbuf_get_unicode_string
+ *
+ * Decode a UTF-16 string, returning its UTF-8 form in strpp,
+ * allocated using smb_msgbuf_malloc (automatically freed).
+ * If max_bytes != 0, consume at most max_bytes of the mb.
+ * See also: mbc_marshal_get_unicode_string
+ */
+static int
+msgbuf_get_unicode_string(smb_msgbuf_t *mb, char **strpp, int max_bytes)
+{
+ char *mbs;
+ uint16_t *wcsbuf = NULL;
+ int wcslen; // wchar count
+ int datalen; // OtW data len
+ size_t mbsmax; // max len of ret str
+ size_t rlen;
+
+ if (max_bytes == 0)
+ max_bytes = 0xffff;
+
+ /*
+ * Unicode strings are always word aligned.
+ */
+ smb_msgbuf_word_align(mb);
+
+ /*
+ * Determine the OtW data length and (WC) string length
+ * Note: wcslen counts 16-bit wide_chars (w/o null),
+ * and datalen is how much we move mb->scan
+ */
+ datalen = 0;
+ wcslen = 0;
+ for (;;) {
+ if (datalen >= max_bytes)
+ break;
+ /* in-line smb_msgbuf_has_space */
+ if ((mb->scan + datalen) >= mb->end)
+ return (SMB_MSGBUF_UNDERFLOW);
+ datalen += 2;
+ if (mb->scan[datalen - 2] == 0 &&
+ mb->scan[datalen - 1] == 0)
+ break;
+ wcslen++;
+ }
+
+ /*
+ * Get datalen bytes into a temp buffer
+ * sized with room to add a (WC) null.
+ * Note: wcsbuf has little-endian order
+ */
+ wcsbuf = smb_msgbuf_malloc(mb, datalen + 2);
+ if (wcsbuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ bcopy(mb->scan, wcsbuf, datalen);
+ mb->scan += datalen;
+ wcsbuf[wcslen] = 0;
+
+ /*
+ * Get the buffer we'll return and convert to UTF-8.
+ * May take as much 4X number of wide chars.
+ */
+ mbsmax = wcslen * MTS_MB_CUR_MAX;
+ mbs = smb_msgbuf_malloc(mb, mbsmax + 1);
+ if (mbs == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
+ if (rlen == (size_t)-1)
+ return (SMB_MSGBUF_UNDERFLOW);
+ if (rlen > mbsmax)
+ rlen = mbsmax;
+ mbs[rlen] = '\0';
+ *strpp = mbs;
+ return (0);
+}
/*
* smb_msgbuf_encode
@@ -466,8 +562,6 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
uint8_t *bvalp;
char *cvalp;
uint8_t c;
- smb_wchar_t wchar;
- int count;
boolean_t repc_specified;
int repc;
int rc;
@@ -571,80 +665,23 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap)
goto unicode_translation;
/* FALLTHROUGH */
- case 's': /* put string */
+ case 's': /* put OEM string */
cvalp = va_arg(ap, char *);
- if (!repc_specified) {
- repc = smb_sbequiv_strlen(cvalp);
- if (repc == -1)
- return (SMB_MSGBUF_OVERFLOW);
- if (!(mb->flags & SMB_MSGBUF_NOTERM))
- repc++;
- }
- if (smb_msgbuf_has_space(mb, repc) == 0)
- return (SMB_MSGBUF_OVERFLOW);
- while (repc > 0) {
- count = smb_mbtowc(&wchar, cvalp,
- MTS_MB_CHAR_MAX);
- if (count < 0)
- return (SMB_MSGBUF_DATA_ERROR);
- cvalp += count;
- if (wchar == 0)
- break;
- *mb->scan++ = (uint8_t)wchar;
- repc--;
- if (wchar & 0xff00) {
- *mb->scan++ = wchar >> 8;
- repc--;
- }
- }
- if (*cvalp == '\0' && repc > 0 &&
- (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
- *mb->scan++ = 0;
- repc--;
- }
- while (repc > 0) {
- *mb->scan++ = 0;
- repc--;
- }
+ if (!repc_specified)
+ repc = 0;
+ rc = msgbuf_put_oem_string(mb, cvalp, repc);
+ if (rc != 0)
+ return (rc);
break;
- case 'U': /* put unicode string */
+ case 'U': /* put UTF-16 string */
unicode_translation:
- /*
- * Unicode strings are always word aligned.
- */
- smb_msgbuf_word_align(mb);
cvalp = va_arg(ap, char *);
- if (!repc_specified) {
- repc = smb_wcequiv_strlen(cvalp);
- if (!(mb->flags & SMB_MSGBUF_NOTERM))
- repc += 2;
- }
- if (!smb_msgbuf_has_space(mb, repc))
- return (SMB_MSGBUF_OVERFLOW);
- while (repc >= 2) {
- count = smb_mbtowc(&wchar, cvalp,
- MTS_MB_CHAR_MAX);
- if (count < 0)
- return (SMB_MSGBUF_DATA_ERROR);
- cvalp += count;
- if (wchar == 0)
- break;
-
- LE_OUT16(mb->scan, wchar);
- mb->scan += 2;
- repc -= 2;
- }
- if (*cvalp == '\0' && repc >= 2 &&
- (mb->flags & SMB_MSGBUF_NOTERM) == 0) {
- LE_OUT16(mb->scan, 0);
- mb->scan += 2;
- repc -= 2;
- }
- while (repc > 0) {
- *mb->scan++ = 0;
- repc--;
- }
+ if (!repc_specified)
+ repc = 0;
+ rc = msgbuf_put_unicode_string(mb, cvalp, repc);
+ if (rc != 0)
+ return (rc);
break;
case 'M':
@@ -665,6 +702,141 @@ unicode_translation:
return (SMB_MSGBUF_SUCCESS);
}
+/*
+ * Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
+ * Also write a null unless the repc count limits the length we put.
+ * When (repc > 0) the length we marshal must be exactly repc, and
+ * truncate or pad the mb data as necessary.
+ * See also: mbc_marshal_put_oem_string
+ */
+static int
+msgbuf_put_oem_string(smb_msgbuf_t *mb, char *mbs, int repc)
+{
+ uint8_t *oembuf = NULL;
+ uint8_t *s;
+ int oemlen;
+ int rlen;
+
+ /*
+ * Compute length of converted OEM string,
+ * NOT including null terminator
+ */
+ if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
+ return (SMB_MSGBUF_DATA_ERROR);
+
+ /*
+ * If repc not specified, put whole string + NULL,
+ * otherwise will truncate or pad as needed.
+ */
+ if (repc <= 0) {
+ repc = oemlen;
+ if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
+ repc += sizeof (char);
+ }
+ if (smb_msgbuf_has_space(mb, repc) == 0)
+ return (SMB_MSGBUF_OVERFLOW);
+
+ /*
+ * Convert into a temporary buffer
+ * Free oembuf in smb_msgbuf_term.
+ */
+ oembuf = smb_msgbuf_malloc(mb, oemlen + 1);
+ if (oembuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_mbstooem(oembuf, mbs, oemlen);
+ if (rlen < 0)
+ return (SMB_MSGBUF_DATA_ERROR);
+ if (rlen > oemlen)
+ rlen = oemlen;
+ oembuf[rlen] = '\0';
+
+ /*
+ * Copy the converted string into the message,
+ * truncated or paded as required.
+ */
+ s = oembuf;
+ while (repc > 0) {
+ *mb->scan++ = *s;
+ if (*s != '\0')
+ s++;
+ repc--;
+ }
+
+ return (0);
+}
+
+/*
+ * Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
+ * Also write a null unless the repc count limits the length.
+ * When (repc > 0) the length we marshal must be exactly repc,
+ * and truncate or pad the mb data as necessary.
+ * See also: mbc_marshal_put_unicode_string
+ */
+static int
+msgbuf_put_unicode_string(smb_msgbuf_t *mb, char *mbs, int repc)
+{
+ smb_wchar_t *wcsbuf = NULL;
+ smb_wchar_t *wp;
+ size_t wcslen, wcsbytes;
+ size_t rlen;
+
+ /* align to word boundary */
+ smb_msgbuf_word_align(mb);
+
+ /*
+ * Compute length of converted UTF-16 string,
+ * NOT including null terminator (in bytes).
+ */
+ wcsbytes = smb_wcequiv_strlen(mbs);
+ if (wcsbytes == (size_t)-1)
+ return (SMB_MSGBUF_DATA_ERROR);
+
+ /*
+ * If repc not specified, put whole string + NULL,
+ * otherwise will truncate or pad as needed.
+ */
+ if (repc <= 0) {
+ repc = (int)wcsbytes;
+ if ((mb->flags & SMB_MSGBUF_NOTERM) == 0)
+ repc += sizeof (smb_wchar_t);
+ }
+ if (smb_msgbuf_has_space(mb, repc) == 0)
+ return (SMB_MSGBUF_OVERFLOW);
+
+ /*
+ * Convert into a temporary buffer
+ * Free wcsbuf in smb_msgbuf_term
+ */
+ wcslen = wcsbytes / 2;
+ wcsbuf = smb_msgbuf_malloc(mb, wcsbytes + 2);
+ if (wcsbuf == NULL)
+ return (SMB_MSGBUF_UNDERFLOW);
+ rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
+ if (rlen == (size_t)-1)
+ return (SMB_MSGBUF_DATA_ERROR);
+ if (rlen > wcslen)
+ rlen = wcslen;
+ wcsbuf[rlen] = 0;
+
+ /*
+ * Copy the converted string into the message,
+ * truncated or paded as required. Preserve
+ * little-endian order while copying.
+ */
+ wp = wcsbuf;
+ while (repc > 1) {
+ smb_wchar_t wchar = LE_IN16(wp);
+ LE_OUT16(mb->scan, wchar);
+ mb->scan += 2;
+ if (wchar != 0)
+ wp++;
+ repc -= sizeof (smb_wchar_t);
+ }
+ if (repc > 0)
+ *mb->scan++ = '\0';
+
+ return (0);
+}
/*
* smb_msgbuf_malloc
diff --git a/usr/src/common/smbsrv/smb_string.c b/usr/src/common/smbsrv/smb_string.c
index 3d2abc474b..7922d84916 100644
--- a/usr/src/common/smbsrv/smb_string.c
+++ b/usr/src/common/smbsrv/smb_string.c
@@ -174,8 +174,8 @@ smb_islower(int c)
* If the specified character is lowercase, the uppercase value will
* be returned. Otherwise the original value will be returned.
*/
-int
-smb_toupper(int c)
+uint32_t
+smb_toupper(uint32_t c)
{
uint16_t mask = is_unicode ? 0xffff : 0xff;
@@ -187,8 +187,8 @@ smb_toupper(int c)
* If the specified character is uppercase, the lowercase value will
* be returned. Otherwise the original value will be returned.
*/
-int
-smb_tolower(int c)
+uint32_t
+smb_tolower(uint32_t c)
{
uint16_t mask = is_unicode ? 0xffff : 0xff;
@@ -204,7 +204,7 @@ smb_tolower(int c)
char *
smb_strupr(char *s)
{
- smb_wchar_t c;
+ uint32_t c;
char *p = s;
while (*p) {
@@ -235,7 +235,7 @@ smb_strupr(char *s)
char *
smb_strlwr(char *s)
{
- smb_wchar_t c;
+ uint32_t c;
char *p = s;
while (*p) {
@@ -264,7 +264,7 @@ smb_strlwr(char *s)
int
smb_isstrlwr(const char *s)
{
- smb_wchar_t c;
+ uint32_t c;
int n;
const char *p = s;
@@ -295,7 +295,7 @@ smb_isstrlwr(const char *s)
int
smb_isstrupr(const char *s)
{
- smb_wchar_t c;
+ uint32_t c;
int n;
const char *p = s;
@@ -440,7 +440,7 @@ smb_unicode_init(void)
* unc_server server or domain name with no leading/trailing '\'
* unc_share share name with no leading/trailing '\'
* unc_path relative path to the share with no leading/trailing '\'
- * it is valid for unc_path to be NULL.
+ * it is valid for unc_path to be NULL.
*
* Upon successful return of this function, smb_unc_free()
* MUST be called when returned 'unc' is no longer needed.
diff --git a/usr/src/common/smbsrv/smb_utf8.c b/usr/src/common/smbsrv/smb_utf8.c
index 3c3583471f..8446fb0b9e 100644
--- a/usr/src/common/smbsrv/smb_utf8.c
+++ b/usr/src/common/smbsrv/smb_utf8.c
@@ -22,32 +22,12 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
- * Multibyte/wide-char conversion routines. Wide-char encoding provides
- * a fixed size character encoding that maps to the Unicode 16-bit
- * (UCS-2) character set standard. Multibyte or UCS transformation
- * format (UTF) encoding is a variable length character encoding scheme
- * that s compatible with existing ASCII characters and guarantees that
- * the resultant strings do not contain embedded null characters. Both
- * types of encoding provide a null terminator: single byte for UTF-8
- * and a wide-char null for Unicode. See RFC 2044.
- *
- * The table below illustrates the UTF-8 encoding scheme. The letter x
- * indicates bits available for encoding the character value.
- *
- * UCS-2 UTF-8 octet sequence (binary)
- * 0x0000-0x007F 0xxxxxxx
- * 0x0080-0x07FF 110xxxxx 10xxxxxx
- * 0x0800-0xFFFF 1110xxxx 10xxxxxx 10xxxxxx
- *
- * RFC 2044
- * UTF-8,a transformation format of UNICODE and ISO 10646
- * F. Yergeau
- * Alis Technologies
- * October 1996
+ * Multibyte/wide-char conversion routines. SMB uses UTF-16 on the wire
+ * (smb_wchar_t) and we use UTF-8 internally (our multi-byte, or mbs).
*/
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
@@ -60,6 +40,7 @@
#include <iconv.h>
#include <assert.h>
#endif /* _KERNEL || _FAKE_KERNEL */
+#include <sys/u8_textprep.h>
#include <smbsrv/string.h>
@@ -76,26 +57,37 @@
* multibyte character is encountered.
*/
size_t
-smb_mbstowcs(smb_wchar_t *wcstring, const char *mbstring, size_t nwchars)
+smb_mbstowcs(smb_wchar_t *wcs, const char *mbs, size_t nwchars)
{
- int len;
- smb_wchar_t *start = wcstring;
-
- while (nwchars--) {
- len = smb_mbtowc(wcstring, mbstring, MTS_MB_CHAR_MAX);
- if (len < 0) {
- *wcstring = 0;
- return ((size_t)-1);
- }
+ size_t mbslen, wcslen;
+ int err;
- if (*mbstring == 0)
- break;
+ /* NULL or empty input is allowed. */
+ if (mbs == NULL || *mbs == '\0') {
+ if (wcs != NULL && nwchars > 0)
+ *wcs = 0;
+ return (0);
+ }
- ++wcstring;
- mbstring += len;
+ /*
+ * Traditional mbstowcs(3C) allows wcs==NULL to get the length.
+ * SMB never calls it that way, but let's future-proof.
+ */
+ if (wcs == NULL) {
+ return ((size_t)-1);
}
- return (wcstring - start);
+ mbslen = strlen(mbs);
+ wcslen = nwchars;
+ err = uconv_u8tou16((const uchar_t *)mbs, &mbslen,
+ wcs, &wcslen, UCONV_OUT_LITTLE_ENDIAN);
+ if (err != 0)
+ return ((size_t)-1);
+
+ if (wcslen < nwchars)
+ wcs[wcslen] = 0;
+
+ return (wcslen);
}
@@ -114,49 +106,36 @@ smb_mbstowcs(smb_wchar_t *wcstring, const char *mbstring, size_t nwchars)
* states. Otherwise it should be return 0.
*
* If mbchar is non-null, returns the number of bytes processed in
- * mbchar. If mbchar is invalid, returns -1.
+ * mbchar. If mbchar is null, convert the null (wcharp=0) but
+ * return length zero. If mbchar is invalid, returns -1.
*/
int /*ARGSUSED*/
-smb_mbtowc(smb_wchar_t *wcharp, const char *mbchar, size_t nbytes)
+smb_mbtowc(uint32_t *wcharp, const char *mbchar, size_t nbytes)
{
- unsigned char mbyte;
- smb_wchar_t wide_char;
- int count;
- int bytes_left;
+ uint32_t wide_char;
+ int count, err;
+ size_t mblen;
+ size_t wclen;
if (mbchar == NULL)
return (0); /* no shift states */
- /* 0xxxxxxx -> 1 byte ASCII encoding */
- if (((mbyte = *mbchar++) & 0x80) == 0) {
- if (wcharp)
- *wcharp = (smb_wchar_t)mbyte;
-
- return (mbyte ? 1 : 0);
- }
-
- /* 10xxxxxx -> invalid first byte */
- if ((mbyte & 0x40) == 0)
+ /*
+ * How many bytes in this symbol?
+ */
+ count = u8_validate((char *)mbchar, nbytes, NULL, 0, &err);
+ if (count < 0)
return (-1);
- wide_char = mbyte;
- if ((mbyte & 0x20) == 0) {
- wide_char &= 0x1f;
- bytes_left = 1;
- } else if ((mbyte & 0x10) == 0) {
- wide_char &= 0x0f;
- bytes_left = 2;
- } else {
+ mblen = count;
+ wclen = 1;
+ err = uconv_u8tou32((const uchar_t *)mbchar, &mblen,
+ &wide_char, &wclen, UCONV_OUT_SYSTEM_ENDIAN);
+ if (err != 0)
return (-1);
- }
-
- count = 1;
- while (bytes_left--) {
- if (((mbyte = *mbchar++) & 0xc0) != 0x80)
- return (-1);
-
- count++;
- wide_char = (wide_char << 6) | (mbyte & 0x3f);
+ if (wclen == 0) {
+ wide_char = 0;
+ count = 0;
}
if (wcharp)
@@ -174,25 +153,27 @@ smb_mbtowc(smb_wchar_t *wcharp, const char *mbchar, size_t nbytes)
* mbchar must be large enough to accommodate the multibyte character.
*
* Returns the numberof bytes written to mbchar.
+ * Note: handles null like any 1-byte char.
*/
int
-smb_wctomb(char *mbchar, smb_wchar_t wchar)
+smb_wctomb(char *mbchar, uint32_t wchar)
{
- if ((wchar & ~0x7f) == 0) {
- *mbchar = (char)wchar;
- return (1);
- }
+ char junk[MTS_MB_CUR_MAX+1];
+ size_t mblen;
+ size_t wclen;
+ int err;
- if ((wchar & ~0x7ff) == 0) {
- *mbchar++ = (wchar >> 6) | 0xc0;
- *mbchar = (wchar & 0x3f) | 0x80;
- return (2);
- }
+ if (mbchar == NULL)
+ mbchar = junk;
- *mbchar++ = (wchar >> 12) | 0xe0;
- *mbchar++ = ((wchar >> 6) & 0x3f) | 0x80;
- *mbchar = (wchar & 0x3f) | 0x80;
- return (3);
+ mblen = MTS_MB_CUR_MAX;
+ wclen = 1;
+ err = uconv_u32tou8(&wchar, &wclen, (uchar_t *)mbchar, &mblen,
+ UCONV_IN_SYSTEM_ENDIAN | UCONV_IGNORE_NULL);
+ if (err != 0)
+ return (-1);
+
+ return ((int)mblen);
}
@@ -206,46 +187,46 @@ smb_wctomb(char *mbchar, smb_wchar_t wchar)
* terminated if there is room.
*
* Returns the number of bytes converted, not counting the terminating
- * null byte.
+ * null byte. Returns -1 if an invalid WC sequence is encountered.
*/
size_t
-smb_wcstombs(char *mbstring, const smb_wchar_t *wcstring, size_t nbytes)
+smb_wcstombs(char *mbs, const smb_wchar_t *wcs, size_t nbytes)
{
- char *start = mbstring;
- const smb_wchar_t *wcp = wcstring;
- smb_wchar_t wide_char = 0;
- char buf[4];
- size_t len;
+ size_t mbslen, wcslen;
+ int err;
- if ((mbstring == NULL) || (wcstring == NULL))
+ /* NULL or empty input is allowed. */
+ if (wcs == NULL || *wcs == 0) {
+ if (mbs != NULL && nbytes > 0)
+ *mbs = '\0';
return (0);
+ }
- while (nbytes > MTS_MB_CHAR_MAX) {
- wide_char = *wcp++;
- len = smb_wctomb(mbstring, wide_char);
-
- if (wide_char == 0)
- /*LINTED E_PTRDIFF_OVERFLOW*/
- return (mbstring - start);
-
- mbstring += len;
- nbytes -= len;
+ /*
+ * Traditional wcstombs(3C) allows mbs==NULL to get the length.
+ * SMB never calls it that way, but let's future-proof.
+ */
+ if (mbs == NULL) {
+ return ((size_t)-1);
}
- while (wide_char && nbytes) {
- wide_char = *wcp++;
- if ((len = smb_wctomb(buf, wide_char)) > nbytes) {
- *mbstring = 0;
- break;
- }
+ /*
+ * Compute wcslen
+ */
+ wcslen = 0;
+ while (wcs[wcslen] != 0)
+ wcslen++;
- bcopy(buf, mbstring, len);
- mbstring += len;
- nbytes -= len;
- }
+ mbslen = nbytes;
+ err = uconv_u16tou8(wcs, &wcslen,
+ (uchar_t *)mbs, &mbslen, UCONV_IN_LITTLE_ENDIAN);
+ if (err != 0)
+ return ((size_t)-1);
+
+ if (mbslen < nbytes)
+ mbs[mbslen] = '\0';
- /*LINTED E_PTRDIFF_OVERFLOW*/
- return (mbstring - start);
+ return (mbslen);
}
@@ -257,7 +238,7 @@ smb_wcstombs(char *mbstring, const smb_wchar_t *wcstring, size_t nbytes)
size_t
smb_wcequiv_strlen(const char *mbs)
{
- smb_wchar_t wide_char;
+ uint32_t wide_char;
size_t bytes;
size_t len = 0;
@@ -265,9 +246,15 @@ smb_wcequiv_strlen(const char *mbs)
bytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX);
if (bytes == ((size_t)-1))
return ((size_t)-1);
+ mbs += bytes;
len += sizeof (smb_wchar_t);
- mbs += bytes;
+ if (bytes > 3) {
+ /*
+ * Extended unicode, so TWO smb_wchar_t
+ */
+ len += sizeof (smb_wchar_t);
+ }
}
return (len);
@@ -277,27 +264,37 @@ smb_wcequiv_strlen(const char *mbs)
/*
* Returns the number of bytes that would be written if the multi-
* byte string mbs was converted to an OEM character string,
- * not counting the terminating null character.
+ * (smb_mbstooem) not counting the terminating null character.
*/
size_t
smb_sbequiv_strlen(const char *mbs)
{
- smb_wchar_t wide_char;
size_t nbytes;
size_t len = 0;
while (*mbs) {
- nbytes = smb_mbtowc(&wide_char, mbs, MTS_MB_CHAR_MAX);
+ nbytes = smb_mbtowc(NULL, mbs, MTS_MB_CHAR_MAX);
if (nbytes == ((size_t)-1))
return ((size_t)-1);
+ if (nbytes == 0)
+ break;
- /*
- * Assume OEM characters are 1-byte (for now).
- * That's true for cp850, which is the only
- * codeset this currently supports. See:
- * smb_oem.c : smb_oem_codeset
- */
- ++len;
+ if (nbytes == 1) {
+ /* ASCII */
+ len++;
+ } else if (nbytes < 8) {
+ /* Compute OEM length */
+ char mbsbuf[8];
+ uint8_t oembuf[8];
+ int oemlen;
+ (void) strlcpy(mbsbuf, mbs, nbytes+1);
+ oemlen = smb_mbstooem(oembuf, mbsbuf, 8);
+ if (oemlen < 0)
+ return ((size_t)-1);
+ len += oemlen;
+ } else {
+ return ((size_t)-1);
+ }
mbs += nbytes;
}
diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
index 8f1ad4a552..37b8272be4 100644
--- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
+++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers
@@ -270,6 +270,7 @@ SYMBOL_VERSION SUNWprivate {
smb_lookup_name;
smb_lookup_sid;
smb_match_netlogon_seqnum;
+ smb_mbstooem;
smb_mbstowcs;
smb_mbtowc;
smb_msgbuf_base;
@@ -308,6 +309,7 @@ SYMBOL_VERSION SUNWprivate {
smb_nic_getnum;
smb_nic_init;
smb_notify_dc_changed;
+ smb_oemtombs;
smb_priv_getbyname;
smb_priv_getbyvalue;
smb_priv_presentable_ids;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
index e4cd2435db..f9b629b24f 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c
@@ -22,7 +22,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -45,8 +45,7 @@ static int mbc_marshal_put_char(mbuf_chain_t *mbc, uint8_t);
static int mbc_marshal_put_short(mbuf_chain_t *mbc, uint16_t);
static int mbc_marshal_put_long(mbuf_chain_t *mbc, uint32_t);
static int mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t);
-static int mbc_marshal_put_oem_string(smb_request_t *, mbuf_chain_t *,
- char *, int);
+static int mbc_marshal_put_oem_string(mbuf_chain_t *, char *, int);
static int mbc_marshal_put_unicode_string(mbuf_chain_t *, char *, int);
static int mbc_marshal_put_uio(mbuf_chain_t *, struct uio *);
static int mbc_marshal_put_mbufs(mbuf_chain_t *mbc, mbuf_t *m);
@@ -310,7 +309,7 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
goto unicode_translation;
/* FALLTHROUGH */
- case 's': /* OEM string */
+ case 's': /* get OEM string */
oem_conversion:
ASSERT(sr != NULL);
charpp = va_arg(ap, char **);
@@ -321,14 +320,12 @@ oem_conversion:
return (-1);
break;
- case 'U': /* Convert from unicode */
+ case 'U': /* get UTF-16 string */
unicode_translation:
ASSERT(sr != 0);
charpp = va_arg(ap, char **);
if (!repc_specified)
repc = 0;
- if (mbc->chain_offset & 1)
- mbc->chain_offset++;
if (mbc_marshal_get_unicode_string(sr,
mbc, charpp, repc) != 0)
return (-1);
@@ -519,7 +516,7 @@ smb_mbc_peek(mbuf_chain_t *mbc, int offset, const char *fmt, ...)
int
smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
{
- char *charp;
+ char *charp;
uint8_t *cvalp;
timestruc_t *tvp;
smb_vdb_t *vdp;
@@ -680,20 +677,18 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, const char *fmt, va_list ap)
goto unicode_translation;
/* FALLTHROUGH */
- case 's': /* OEM string */
+ case 's': /* put OEM string */
oem_conversion:
charp = va_arg(ap, char *);
if (!repc_specified)
repc = 0;
- if (mbc_marshal_put_oem_string(sr, mbc,
+ if (mbc_marshal_put_oem_string(mbc,
charp, repc) != 0)
return (DECODE_NO_MORE_DATA);
break;
- case 'U': /* Convert to unicode, align to word boundary */
+ case 'U': /* put UTF-16 string */
unicode_translation:
- if (mbc->chain_offset & 1)
- mbc->chain_offset++;
charp = va_arg(ap, char *);
if (!repc_specified)
repc = 0;
@@ -1071,91 +1066,147 @@ mbc_marshal_put_long_long(mbuf_chain_t *mbc, uint64_t data)
/*
* Marshal a UTF-8 string (str) into mbc, converting to OEM codeset.
* Also write a null unless the repc count limits the length we put.
+ * When (repc > 0) the length we marshal must be exactly repc, and
+ * truncate or pad the mbc data as necessary.
+ * See also: msgbuf_put_oem_string
*/
static int
-mbc_marshal_put_oem_string(
- smb_request_t *sr,
- mbuf_chain_t *mbc,
- char *str,
- int repc)
+mbc_marshal_put_oem_string(mbuf_chain_t *mbc, char *mbs, int repc)
{
- uint8_t *s, *oembuf;
- int buflen;
+ uint8_t *oembuf = NULL;
+ uint8_t *s;
int oemlen;
- int putlen;
+ int rlen;
+ int rc;
/*
- * First convert to OEM string. The OEM string
- * will be no longer than the UTF-8 string.
+ * Compute length of converted OEM string,
+ * NOT including null terminator
*/
- buflen = strlen(str) + 1;
- oembuf = smb_srm_zalloc(sr, buflen);
- oemlen = smb_mbstooem(oembuf, str, buflen);
- if (oemlen == -1)
+ if ((oemlen = smb_sbequiv_strlen(mbs)) == -1)
return (DECODE_NO_MORE_DATA);
- /* null terminator */
- if (oemlen < buflen)
- oembuf[oemlen++] = '\0';
-
- /* If specified, repc limits the length. */
- putlen = oemlen;
- if ((repc > 0) && (repc < putlen))
- putlen = repc;
-
- if (mbc_marshal_make_room(mbc, putlen))
+ /*
+ * If repc not specified, put whole string + NULL,
+ * otherwise will truncate or pad as needed.
+ */
+ if (repc <= 0)
+ repc = oemlen + 1;
+ if (mbc_marshal_make_room(mbc, repc))
return (DECODE_NO_MORE_DATA);
+ /*
+ * Convert into a temporary buffer
+ * Free oembuf before return.
+ */
+ oembuf = smb_mem_zalloc(oemlen + 1);
+ ASSERT(oembuf != NULL);
+ rlen = smb_mbstooem(oembuf, mbs, oemlen);
+ if (rlen < 0) {
+ rc = DECODE_NO_MORE_DATA;
+ goto out;
+ }
+ if (rlen > oemlen)
+ rlen = oemlen;
+ oembuf[rlen] = '\0';
+
+ /*
+ * Copy the converted string into the message,
+ * truncated or paded as required.
+ */
s = oembuf;
- while (putlen > 0) {
+ while (repc > 0) {
mbc_marshal_store_byte(mbc, *s);
- s++;
- putlen--;
+ if (*s != '\0')
+ s++;
+ repc--;
}
+ rc = 0;
- return (0);
+out:
+ if (oembuf != NULL)
+ smb_mem_free(oembuf);
+ return (rc);
}
/*
* Marshal a UTF-8 string (str) into mbc, converting to UTF-16.
- * Also write a UTF-16 null (2 bytes) unless the repc count
- * limits the length we put into the mbc.
+ * Also write a null unless the repc count limits the length.
+ * When (repc > 0) the length we marshal must be exactly repc,
+ * and truncate or pad the mbc data as necessary.
+ * See also: msgbuf_put_unicode_string
*/
static int
-mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *str, int repc)
+mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *mbs, int repc)
{
- smb_wchar_t wchar;
- int consumed;
- int length;
+ smb_wchar_t *wcsbuf = NULL;
+ smb_wchar_t *wp;
+ size_t wcslen, wcsbytes;
+ size_t rlen;
+ int rc;
+
+ /* align to word boundary */
+ if (mbc->chain_offset & 1) {
+ if (mbc_marshal_make_room(mbc, 1))
+ return (DECODE_NO_MORE_DATA);
+ mbc_marshal_store_byte(mbc, 0);
+ }
- if ((length = smb_wcequiv_strlen(str)) == -1)
+ /*
+ * Compute length of converted UTF-16 string,
+ * NOT including null terminator (in bytes).
+ */
+ wcsbytes = smb_wcequiv_strlen(mbs);
+ if (wcsbytes == (size_t)-1)
return (DECODE_NO_MORE_DATA);
- /* null terminator */
- length += sizeof (smb_wchar_t);
+ /*
+ * If repc not specified, put whole string + NULL,
+ * otherwise will truncate or pad as needed.
+ */
+ if (repc <= 0)
+ repc = wcsbytes + 2;
+ if (mbc_marshal_make_room(mbc, repc))
+ return (DECODE_NO_MORE_DATA);
- /* If specified, repc limits the length. */
- if ((repc > 0) && (repc < length))
- length = repc;
+ /*
+ * Convert into a temporary buffer
+ * Free wcsbuf before return.
+ */
+ wcslen = wcsbytes / 2;
+ wcsbuf = smb_mem_zalloc(wcsbytes + 2);
+ ASSERT(wcsbuf != NULL);
+ rlen = smb_mbstowcs(wcsbuf, mbs, wcslen);
+ if (rlen == (size_t)-1) {
+ rc = DECODE_NO_MORE_DATA;
+ goto out;
+ }
+ if (rlen > wcslen)
+ rlen = wcslen;
+ wcsbuf[rlen] = 0;
- if (mbc_marshal_make_room(mbc, length))
- return (DECODE_NO_MORE_DATA);
- while (length > 0) {
- consumed = smb_mbtowc(&wchar, str, MTS_MB_CHAR_MAX);
- if (consumed == -1)
- break; /* Invalid sequence */
- /*
- * Note that consumed will be 0 when the null terminator
- * is encountered and str will not be advanced beyond
- * that point. Length will continue to be decremented so
- * we won't get stuck here.
- */
- str += consumed;
+ /*
+ * Copy the converted string into the message,
+ * truncated or paded as required. Preserve
+ * little-endian order while copying.
+ */
+ wp = wcsbuf;
+ while (repc > 1) {
+ smb_wchar_t wchar = LE_IN16(wp);
mbc_marshal_store_byte(mbc, wchar);
mbc_marshal_store_byte(mbc, wchar >> 8);
- length -= sizeof (smb_wchar_t);
+ if (wchar != 0)
+ wp++;
+ repc -= sizeof (smb_wchar_t);
}
- return (0);
+ if (repc > 0)
+ mbc_marshal_store_byte(mbc, 0);
+
+ rc = 0;
+out:
+ if (wcsbuf != NULL)
+ smb_mem_free(wcsbuf);
+ return (rc);
}
static int /*ARGSUSED*/
@@ -1394,61 +1445,71 @@ mbc_marshal_get_long_long(mbuf_chain_t *mbc, uint64_t *data)
*
* Decode an OEM string, returning its UTF-8 form in strpp,
* allocated using smb_srm_zalloc (automatically freed).
- * If repc != 0, consume no more than repc bytes.
+ * If max_bytes != 0, consume at most max_bytes of the mbc.
+ * See also: msgbuf_get_oem_string
*/
static int
-mbc_marshal_get_oem_string(
- smb_request_t *sr,
- mbuf_chain_t *mbc,
- char **strpp,
- int repc)
+mbc_marshal_get_oem_string(smb_request_t *sr,
+ mbuf_chain_t *mbc, char **strpp, int max_bytes)
{
- uint8_t *ch, *rcvbuf;
- char *mbsbuf;
- int mbslen, mbsmax;
- int buflen;
- int oemlen;
-
- buflen = MALLOC_QUANTUM;
- rcvbuf = smb_srm_zalloc(sr, buflen);
+ char *mbs;
+ uint8_t *oembuf = NULL;
+ int oemlen, oemmax;
+ int mbsmax;
+ int rlen;
+ int rc;
- if (repc == 0)
- repc = 0xffff;
+ if (max_bytes == 0)
+ max_bytes = 0xffff;
+ /*
+ * Get the OtW data into a temporary buffer.
+ * Free oembuf before return.
+ */
oemlen = 0;
- ch = rcvbuf;
+ oemmax = MALLOC_QUANTUM;
+ oembuf = smb_mem_alloc(oemmax);
for (;;) {
- while (oemlen < buflen) {
- if (repc-- <= 0) {
- *ch++ = 0;
- goto multibyte_encode;
- }
- if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0) {
- /* Data will never be available */
- return (DECODE_NO_MORE_DATA);
- }
- if ((*ch++ = mbc_marshal_fetch_byte(mbc)) == 0)
- goto multibyte_encode;
- oemlen++;
+ uint8_t ch;
+
+ if (oemlen >= max_bytes)
+ break;
+ if ((oemlen + 2) >= oemmax) {
+ oemmax += MALLOC_QUANTUM;
+ oembuf = smb_mem_realloc(oembuf, oemmax);
}
- buflen += MALLOC_QUANTUM;
- rcvbuf = smb_srm_rezalloc(sr, rcvbuf, buflen);
- ch = rcvbuf + oemlen;
+ if (mbc_marshal_get_char(mbc, &ch) != 0) {
+ rc = DECODE_NO_MORE_DATA;
+ goto out;
+ }
+ if (ch == 0)
+ break;
+ oembuf[oemlen++] = ch;
}
+ oembuf[oemlen] = '\0';
-multibyte_encode:
/*
- * UTF-8 encode the return string for internal system use.
- * Allocated size is worst-case: 3x larger than OEM.
+ * Get the buffer we'll return and convert to UTF-8.
+ * May take as much as double the space.
*/
- mbsmax = (oemlen + 1) * MTS_MB_CHAR_MAX;
- mbsbuf = smb_srm_zalloc(sr, mbsmax);
- mbslen = smb_oemtombs(mbsbuf, rcvbuf, mbsmax);
- if (mbslen == -1)
- return (DECODE_NO_MORE_DATA);
-
- *strpp = mbsbuf;
- return (0);
+ mbsmax = oemlen * 2;
+ mbs = smb_srm_zalloc(sr, mbsmax + 1);
+ ASSERT(mbs != NULL);
+ rlen = smb_oemtombs(mbs, oembuf, mbsmax);
+ if (rlen < 0) {
+ rc = DECODE_NO_MORE_DATA;
+ goto out;
+ }
+ if (rlen > mbsmax)
+ rlen = mbsmax;
+ mbs[rlen] = '\0';
+ *strpp = mbs;
+ rc = 0;
+
+out:
+ if (oembuf != NULL)
+ smb_mem_free(oembuf);
+ return (rc);
}
/*
@@ -1456,46 +1517,83 @@ multibyte_encode:
*
* Decode a UTF-16 string, returning its UTF-8 form in strpp,
* allocated using smb_srm_zalloc (automatically freed).
- * If repc != 0, consume no more than repc bytes.
+ * If max_bytes != 0, consume at most max_bytes of the mbc.
+ * See also: msgbuf_get_unicode_string
*/
static int
mbc_marshal_get_unicode_string(smb_request_t *sr,
- mbuf_chain_t *mbc, char **strpp, int repc)
+ mbuf_chain_t *mbc, char **strpp, int max_bytes)
{
- int max;
- uint16_t wchar;
- char *ch;
- int emitted;
- int length = 0;
+ char *mbs;
+ uint16_t *wcsbuf = NULL;
+ int wcslen; // wchar count
+ int wcsmax; // byte count
+ size_t mbsmax;
+ size_t rlen;
+ int rc;
- if (repc == 0)
- repc = 0xffff;
+ if (max_bytes == 0)
+ max_bytes = 0xffff;
- max = MALLOC_QUANTUM;
- *strpp = smb_srm_zalloc(sr, max);
+ /*
+ * Unicode strings are always word aligned.
+ */
+ if (mbc->chain_offset & 1) {
+ if (MBC_ROOM_FOR(mbc, sizeof (char)) == 0)
+ return (DECODE_NO_MORE_DATA);
+ mbc->chain_offset++;
+ }
- ch = *strpp;
+ /*
+ * Get the OtW data into a temporary buffer.
+ * Free wcsbuf before return.
+ */
+ wcslen = 0;
+ wcsmax = MALLOC_QUANTUM;
+ wcsbuf = smb_mem_alloc(wcsmax);
for (;;) {
- while ((length + MTS_MB_CHAR_MAX) < max) {
- if (repc <= 0)
- goto done;
- repc -= 2;
-
- if (mbc_marshal_get_short(mbc, &wchar) != 0)
- return (DECODE_NO_MORE_DATA);
-
- if (wchar == 0) goto done;
+ uint16_t wchar;
- emitted = smb_wctomb(ch, wchar);
- length += emitted;
- ch += emitted;
+ if ((wcslen * 2) >= max_bytes)
+ break;
+ if (((wcslen * 2) + 4) >= wcsmax) {
+ wcsmax += MALLOC_QUANTUM;
+ wcsbuf = smb_mem_realloc(wcsbuf, wcsmax);
+ }
+ if (mbc_marshal_get_short(mbc, &wchar) != 0) {
+ rc = DECODE_NO_MORE_DATA;
+ goto out;
}
- max += MALLOC_QUANTUM;
- *strpp = smb_srm_rezalloc(sr, *strpp, max);
- ch = *strpp + length;
+ if (wchar == 0)
+ break;
+ /* Keep in little-endian form. */
+ LE_OUT16(wcsbuf + wcslen, wchar);
+ wcslen++;
}
-done: *ch = 0;
- return (0);
+ wcsbuf[wcslen] = 0;
+
+ /*
+ * Get the buffer we'll return and convert to UTF-8.
+ * May take as much 4X number of wide chars.
+ */
+ mbsmax = wcslen * MTS_MB_CUR_MAX;
+ mbs = smb_srm_zalloc(sr, mbsmax + 1);
+ ASSERT(mbs != NULL);
+ rlen = smb_wcstombs(mbs, wcsbuf, mbsmax);
+ if (rlen == (size_t)-1) {
+ rc = DECODE_NO_MORE_DATA;
+ goto out;
+ }
+ if (rlen > mbsmax)
+ rlen = mbsmax;
+ mbs[rlen] = '\0';
+ *strpp = mbs;
+ rc = 0;
+
+out:
+ if (wcsbuf != NULL)
+ smb_mem_free(wcsbuf);
+ return (rc);
}
static int /*ARGSUSED*/
diff --git a/usr/src/uts/common/fs/smbsrv/smb_vops.c b/usr/src/uts/common/fs/smbsrv/smb_vops.c
index 23ba643089..7a3905d7bb 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_vops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c
@@ -1587,7 +1587,7 @@ smb_vop_catia_v5tov4(char *name, char *buf, int buflen)
{
int v4_idx, numbytes, inc;
int space_left = buflen - 1; /* one byte reserved for null */
- smb_wchar_t wc;
+ uint32_t wc;
char mbstring[MTS_MB_CHAR_MAX];
char *p, *src = name, *dst = buf;
@@ -1644,7 +1644,7 @@ smb_vop_catia_v4tov5(char *name, char *buf, int buflen)
{
int v5_idx, numbytes;
int space_left = buflen - 1; /* one byte reserved for null */
- smb_wchar_t wc;
+ uint32_t wc;
char mbstring[MTS_MB_CHAR_MAX];
char *src = name, *dst = buf;
diff --git a/usr/src/uts/common/smbsrv/string.h b/usr/src/uts/common/smbsrv/string.h
index 510fe502c3..14b95f6efa 100644
--- a/usr/src/uts/common/smbsrv/string.h
+++ b/usr/src/uts/common/smbsrv/string.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2017 by Delphix. All rights reserved.
*/
@@ -85,7 +85,7 @@ typedef enum codepage_id {
/*
* Maximum number of bytes per multi-byte character.
*/
-#define MTS_MB_CUR_MAX 3
+#define MTS_MB_CUR_MAX 4
#define MTS_MB_CHAR_MAX MTS_MB_CUR_MAX
typedef uint16_t smb_wchar_t;
@@ -118,8 +118,8 @@ void smb_codepage_fini(void);
int smb_isupper(int);
int smb_islower(int);
-int smb_toupper(int);
-int smb_tolower(int);
+uint32_t smb_toupper(uint32_t);
+uint32_t smb_tolower(uint32_t);
char *smb_strupr(char *);
char *smb_strlwr(char *);
int smb_isstrupr(const char *);
@@ -130,8 +130,8 @@ boolean_t smb_match(const char *, const char *, boolean_t);
size_t smb_mbstowcs(smb_wchar_t *, const char *, size_t);
size_t smb_wcstombs(char *, const smb_wchar_t *, size_t);
-int smb_mbtowc(smb_wchar_t *, const char *, size_t);
-int smb_wctomb(char *, smb_wchar_t);
+int smb_mbtowc(uint32_t *, const char *, size_t);
+int smb_wctomb(char *, uint32_t);
size_t smb_wcequiv_strlen(const char *);
size_t smb_sbequiv_strlen(const char *);
@@ -139,9 +139,6 @@ size_t smb_sbequiv_strlen(const char *);
int smb_oemtombs(char *, const uint8_t *, int);
int smb_mbstooem(uint8_t *, const char *, int);
-int smb_stombs(char *, char *, int);
-int smb_mbstos(char *, const char *);
-
size_t ucstooem(char *, const smb_wchar_t *, size_t, uint32_t);
size_t oemtoucs(smb_wchar_t *, const char *, size_t, uint32_t);