changeset 13340:43bfdefd2c41

895 snoop lies about SMB errors Reviewed by: Richard Lowe <richlowe@richlowe.net> Reviewed by: richard.elling@richardelling.com Approved by: Albert Lee <trisk@opensolaris.org>
author Gordon Ross <gwr@nexenta.com>
date Sun, 17 Apr 2011 19:11:45 -0400
parents 287b9c4687e8
children 52d3d213dc6e
files usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c
diffstat 4 files changed, 1075 insertions(+), 618 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h	Thu Apr 07 23:12:24 2011 +0530
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h	Sun Apr 17 19:11:45 2011 -0400
@@ -22,6 +22,8 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #ifndef	_SNOOP_H
@@ -43,6 +45,7 @@
 #include <netinet/icmp6.h>
 #include <net/pppoe.h>
 #include <libdlpi.h>
+#include <note.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -187,6 +190,9 @@
 extern void show_pktinfo(int, int, char *, char *, struct timeval *,
 		struct timeval *, int, int);
 extern void show_line(char *);
+/*PRINTFLIKE1*/
+extern void show_printf(char *fmt, ...)
+    __PRINTFLIKE(1);
 extern char *getxdr_time(void);
 extern char *showxdr_time(char *);
 extern char *addrtoname(int, const void *);
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c	Thu Apr 07 23:12:24 2011 +0530
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_display.c	Sun Apr 17 19:11:45 2011 -0400
@@ -21,10 +21,10 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -36,7 +36,7 @@
 #include <sys/time.h>
 #include <sys/bufmod.h>
 #include <setjmp.h>
-#include <varargs.h>
+#include <stdarg.h>
 #include <sys/socket.h>
 #include <net/if.h>
 #include <netinet/in_systm.h>
@@ -190,6 +190,7 @@
 	xdrmem_create(&xdrm, addr, len, XDR_DECODE);
 }
 
+/* Note: begin+end are ignored in get_detail_line */
 char *
 get_line(int begin, int end)
 {
@@ -210,7 +211,17 @@
 void
 show_line(char *str)
 {
-	(void) strcpy(get_line(0, 0), str);
+	(void) strlcpy(get_line(0, 0), str, get_line_remain());
+}
+
+void
+show_printf(char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	(void) vsnprintf(get_line(0, 0), get_line_remain(), fmt, ap);
+	va_end(ap);
 }
 
 char
@@ -770,3 +781,56 @@
 
 	printf("\n");
 }
+
+char *
+show_string(const char *str, int dlen, int maxlen)
+/*
+ *   Prints len bytes from str enclosed in quotes.
+ *   If len is negative, length is taken from strlen(str).
+ *   No more than maxlen bytes will be printed.  Longer
+ *   strings are flagged with ".." after the closing quote.
+ *   Non-printing characters are converted to C-style escape
+ *   codes or octal digits.
+ */
+{
+#define	TBSIZE	256
+	static char tbuff[TBSIZE];
+	const char *p;
+	char *pp;
+	int printable = 0;
+	int c, len;
+
+	len = dlen > maxlen ? maxlen : dlen;
+	dlen = len;
+
+	for (p = str, pp = tbuff; len; p++, len--) {
+		switch (c = *p & 0xFF) {
+		case '\n': (void) strcpy(pp, "\\n"); pp += 2; break;
+		case '\b': (void) strcpy(pp, "\\b"); pp += 2; break;
+		case '\t': (void) strcpy(pp, "\\t"); pp += 2; break;
+		case '\r': (void) strcpy(pp, "\\r"); pp += 2; break;
+		case '\f': (void) strcpy(pp, "\\f"); pp += 2; break;
+		default:
+			if (isascii(c) && isprint(c)) {
+				*pp++ = c;
+				printable++;
+			} else {
+				(void) snprintf(pp, TBSIZE - (pp - tbuff),
+					isdigit(*(p + 1)) ?
+					"\\%03o" : "\\%o", c);
+				pp += strlen(pp);
+			}
+			break;
+		}
+		*pp = '\0';
+		/*
+		 * Check for overflow of temporary buffer.  Allow for
+		 * the next character to be a \nnn followed by a trailing
+		 * null.  If not, then just bail with what we have.
+		 */
+		if (pp + 5 >= &tbuff[TBSIZE]) {
+			break;
+		}
+	}
+	return (printable > dlen / 2 ? tbuff : "");
+}
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c	Thu Apr 07 23:12:24 2011 +0530
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c	Sun Apr 17 19:11:45 2011 -0400
@@ -23,12 +23,8 @@
  * Use is subject to license terms.
  */
 
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SunOS */
-
 #include <stdio.h>
 #include <stdlib.h>
-#include <ctype.h>
 #include <strings.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
@@ -454,56 +450,3 @@
 	}
 	return (1);
 }
-
-char *
-show_string(const char *str, int dlen, int maxlen)
-/*
- *   Prints len bytes from str enclosed in quotes.
- *   If len is negative, length is taken from strlen(str).
- *   No more than maxlen bytes will be printed.  Longer
- *   strings are flagged with ".." after the closing quote.
- *   Non-printing characters are converted to C-style escape
- *   codes or octal digits.
- */
-{
-#define	TBSIZE	256
-	static char tbuff[TBSIZE];
-	const char *p;
-	char *pp;
-	int printable = 0;
-	int c, len;
-
-	len = dlen > maxlen ? maxlen : dlen;
-	dlen = len;
-
-	for (p = str, pp = tbuff; len; p++, len--) {
-		switch (c = *p & 0xFF) {
-		case '\n': (void) strcpy(pp, "\\n"); pp += 2; break;
-		case '\b': (void) strcpy(pp, "\\b"); pp += 2; break;
-		case '\t': (void) strcpy(pp, "\\t"); pp += 2; break;
-		case '\r': (void) strcpy(pp, "\\r"); pp += 2; break;
-		case '\f': (void) strcpy(pp, "\\f"); pp += 2; break;
-		default:
-			if (isascii(c) && isprint(c)) {
-				*pp++ = c;
-				printable++;
-			} else {
-				(void) snprintf(pp, TBSIZE - (pp - tbuff),
-					isdigit(*(p + 1)) ?
-					"\\%03o" : "\\%o", c);
-				pp += strlen(pp);
-			}
-			break;
-		}
-		*pp = '\0';
-		/*
-		 * Check for overflow of temporary buffer.  Allow for
-		 * the next character to be a \nnn followed by a trailing
-		 * null.  If not, then just bail with what we have.
-		 */
-		if (pp + 5 >= &tbuff[TBSIZE]) {
-			break;
-		}
-	}
-	return (printable > dlen / 2 ? tbuff : "");
-}
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c	Thu Apr 07 23:12:24 2011 +0530
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c	Sun Apr 17 19:11:45 2011 -0400
@@ -19,12 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * References used throughout this code:
  *
@@ -38,16 +38,13 @@
  */
 
 #include <fcntl.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "snoop.h"
 
-/* some macros just for compactness */
-#define	GETLINE get_line(0, 0)
-#define	DECARGS int flags, uchar_t *data, int len, char *extrainfo
-
 /*
  * SMB Format (header)
  * [X/Open-SMB, Sec. 5.1]
@@ -55,9 +52,7 @@
 struct smb {
 	uchar_t idf[4]; /*  identifier, contains 0xff, 'SMB'  */
 	uchar_t com;    /*  command code  */
-	uchar_t rcls;   /*  error class  */
-	uchar_t res;
-	uchar_t err[2]; /*  error code  */
+	uchar_t err[4]; /*  NT Status, or error class+code */
 	uchar_t flags;
 	uchar_t flags2[2];
 	uchar_t re[12];
@@ -75,14 +70,19 @@
 };
 
 /* smb flags */
-#define	SERVER_RESPONSE	0x80
+#define	SERVER_RESPONSE		0x80
+
+/* smb flags2 */
+#define	FLAGS2_EXT_SEC		0x0800	/* Extended security */
+#define	FLAGS2_NT_STATUS	0x4000	/* NT status codes */
+#define	FLAGS2_UNICODE		0x8000	/* String are Unicode */
 
-static void interpret_sesssetupX(DECARGS);
-static void interpret_tconX(DECARGS);
-static void interpret_trans(DECARGS);
-static void interpret_trans2(DECARGS);
-static void interpret_negprot(DECARGS);
-static void interpret_default(DECARGS);
+static void interpret_sesssetupX(int, uchar_t *, int, char *, int);
+static void interpret_tconX(int, uchar_t *, int, char *, int);
+static void interpret_trans(int, uchar_t *, int, char *, int);
+static void interpret_trans2(int, uchar_t *, int, char *, int);
+static void interpret_negprot(int, uchar_t *, int, char *, int);
+static void interpret_default(int, uchar_t *, int, char *, int);
 
 /*
  * Trans2 subcommand codes
@@ -101,7 +101,7 @@
 
 struct decode {
 	char *name;
-	void (*func)(DECARGS);
+	void (*func)(int, uchar_t *, int, char *, int);
 	char *callfmt;
 	char *replyfmt;
 };
@@ -120,27 +120,35 @@
 	{
 		"close", 0,
 		/* [X/Open-SMB, Sec. 7.10] */
-		"WFileID\0lLastModTime\0wByteCount\0\0",
-		"wByteCount\0\0"
+		"WFileID\0"
+		"lLastModTime\0"
+		"dByteCount\0\0",
+		"dByteCount\0\0"
 	},
 
 	{ "flush", 0, 0, 0 },
 	{ "unlink", 0, 0, 0 },
 
 	{
-		"mv", 0,
+		"move", 0,
 		/* [X/Open-SMB, Sec. 7.11] */
-		"wFileAttributes\0wByteCount\0"
-		"r\0UFileName\0r\0UNewPath\0\0",
-		"wByteCount\0\0"
+		"wFileAttributes\0"
+		"dByteCount\0r\0"
+		"UFileName\0r\0"
+		"UNewPath\0\0",
+		"dByteCount\0\0"
 	},
 
 	{
 		"getatr", 0,
 		/* [X/Open-SMB, Sec. 8.4] */
-		"dBytecount\0r\0UFileName\0\0",
-		"wFileAttributes\0lTime\0lSize\0R\0R\0R\0"
-		"R\0R\0wByteCount\0\0"
+		"dBytecount\0r\0"
+		"UFileName\0\0",
+		"wFileAttributes\0"
+		"lTime\0"
+		"lSize\0"
+		"R\0R\0R\0R\0R\0"
+		"dByteCount\0\0"
 	},
 
 	{ "setatr", 0, 0, 0 },
@@ -148,17 +156,26 @@
 	{
 		"read", 0,
 		/* [X/Open-SMB, Sec. 7.4] */
-		"WFileID\0wI/0 Bytes\0LFileOffset\0"
-		"WBytesLeft\0wByteCount\0\0",
-		"WDataLength\0R\0R\0R\0R\0wByteCount\0\0"
+		"WFileID\0"
+		"wI/0 Bytes\0"
+		"LFileOffset\0"
+		"WBytesLeft\0"
+		"dByteCount\0\0",
+		"WDataLength\0"
+		"R\0R\0R\0R\0"
+		"dByteCount\0\0"
 	},
 
 	{
 		"write", 0,
 		/* [X/Open-SMB, Sec. 7.5] */
-		"WFileID\0wI/0 Bytes\0LFileOffset\0WBytesLeft\0"
-		"wByteCount\0\0",
-		"WDataLength\0wByteCount\0\0"
+		"WFileID\0"
+		"wI/0 Bytes\0"
+		"LFileOffset\0"
+		"WBytesLeft\0"
+		"dByteCount\0\0",
+		"WDataLength\0"
+		"dByteCount\0\0"
 	},
 
 	{ "lock", 0, 0, 0 },
@@ -170,8 +187,9 @@
 	{
 		"chkpth", 0,
 		/* [X/Open-SMB, Sec. 8.7] */
-		"wByteCount\0r\0UFile\0\0",
-		"wByteCount\0\0"
+		"dByteCount\0r\0"
+		"UFile\0\0",
+		"dByteCount\0\0"
 	},
 
 	{ "exit", 0, 0, 0 },
@@ -187,8 +205,12 @@
 	{
 		"readbraw", 0,
 		/* [X/Open-SMB, Sec. 10.1] */
-		"WFileID\0LFileOffset\0wMaxCount\0"
-		"wMinCount\0lTimeout\0R\0wByteCount\0\0", 0
+		"WFileID\0"
+		"LFileOffset\0"
+		"wMaxCount\0"
+		"wMinCount\0"
+		"lTimeout\0R\0"
+		"dByteCount\0\0", 0
 	},
 
 	{ "readbmpx", 0, 0, 0 },
@@ -206,9 +228,14 @@
 	{
 		"lockingX", 0,
 		/* [X/Open-SMB, Sec. 12.2] */
-		"wChainedCommand\0wNextOffset\0WFileID\0"
-		"wLockType\0lOpenTimeout\0"
-		"W#Unlocks\0W#Locks\0wByteCount\0\0", 0
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"WFileID\0"
+		"wLockType\0"
+		"lOpenTimeout\0"
+		"W#Unlocks\0"
+		"W#Locks\0"
+		"dByteCount\0\0", 0
 	},
 
 	{ "trans", interpret_trans, 0, 0 },
@@ -221,20 +248,83 @@
 	{ "writeclose", 0, 0, 0 },
 
 	{
+		/* [X/Open-SMB, Sec. 12.1] */
 		"openX", 0,
-		/* [X/Open-SMB, Sec. 12.1] */
-		"wChainedCommand\0wNextOffset\0wFlags\0"
-		"wMode\0wSearchAttributes\0wFileAttributes\0"
-		"lTime\0wOpenFunction\0lFileSize\0lOpenTimeout\0"
-		"R\0R\0wByteCount\0r\0UFileName\0\0",
-		"wChainedCommand\0wNextOffset\0WFileID\0"
-		"wAttributes\0lTime\0LSize\0wOpenMode\0"
-		"wFileType\0wDeviceState\0wActionTaken\0"
-		"lUniqueFileID\0R\0wBytecount\0\0"
+		/* call */
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"wFlags\0"
+		"wMode\0"
+		"wSearchAttributes\0"
+		"wFileAttributes\0"
+		"lTime\0"
+		"wOpenFunction\0"
+		"lFileSize\0"
+		"lOpenTimeout\0R\0R\0"
+		"dByteCount\0r\0"
+		"UFileName\0\0",
+		/* reply */
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"WFileID\0"
+		"wAttributes\0"
+		"lTime\0"
+		"LSize\0"
+		"wOpenMode\0"
+		"wFileType\0"
+		"wDeviceState\0"
+		"wActionTaken\0"
+		"lUniqueFileID\0R\0"
+		"wBytecount\0\0"
 	},
 
-	{ "readX", 0, 0, 0 },
-	{ "writeX", 0, 0, 0 },
+	{
+		/* [CIFS 4.2.4] */
+		"readX", 0,
+		/* call */
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"WFileID\0"
+		"LOffset\0"
+		"DMaxCount\0"
+		"dMinCount\0"
+		"dMaxCountHigh\0"
+		"R\0"
+		"wRemaining\0"
+		"lOffsetHigh\0"
+		"dByteCount\0\0",
+		/* reply */
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"dRemaining\0R\0R\0"
+		"DCount\0"
+		"dDataOffset\0"
+		"dCountHigh\0"
+		"R\0R\0R\0R\0"
+		"dByteCount\0\0"
+	},
+
+	{
+		/* [CIFS 4.2.5] */
+		"writeX", 0,
+		/* call */
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"WFileID\0"
+		"LOffset\0R\0R\0"
+		"wWriteMode\0"
+		"wRemaining\0"
+		"dDataLenHigh\0"
+		"DDataLen\0"
+		"dDataOffset\0"
+		"lOffsetHigh\0\0",
+		/* reply */
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"DCount\0"
+		"wRemaining\0"
+		"wCountHigh\0\0"
+	},
 
 	/* 0x30 */
 	{ 0, 0, 0, 0 },
@@ -244,8 +334,9 @@
 	{
 		"findclose", 0,
 		/* [X/Open-SMB, Sec. 15.4 ] */
-		"WFileID\0wByteCount\0\0",
-		"wByteCount\0\0"
+		"WFileID\0"
+		"dByteCount\0\0",
+		"dByteCount\0\0"
 	},
 	{ 0, 0, 0, 0 },
 	{ 0, 0, 0, 0 },
@@ -318,16 +409,18 @@
 	{
 		"tdis", 0,
 		/* [X/Open-SMB, Sec. 6.3] */
-		"wByteCount\0\0",
-		"wByteCount\0\0"
+		"dByteCount\0\0",
+		"dByteCount\0\0"
 	},
 	{ "negprot", interpret_negprot, 0, 0 },
 	{ "sesssetupX", interpret_sesssetupX, 0, 0 },
 	{
 		"uloggoffX", 0,
 		/* [X/Open-SMB, Sec. 15.5] */
-		"wChainedCommand\0wNextOffset\0\0",
-		"wChainedCommnad\0wNextOffset\0\0" },
+		"wChainedCommand\0"
+		"wNextOffset\0\0",
+		"wChainedCommnad\0"
+		"wNextOffset\0\0" },
 	{ "tconX", interpret_tconX, 0, 0 },
 	{ 0, 0, 0, 0 },
 	{ 0, 0, 0, 0 },
@@ -381,26 +474,40 @@
 	 * Command codes 0xa0 to 0xa7 are from
 	 * [CIFS/1.0, Sec. 5.1]
 	 */
-	{ " NT_Trans", 0, 0, 0 },
-	{ " NT_Trans2", 0, 0, 0 },
+	{ "_NT_Trans", 0, 0, 0 },
+	{ "_NT_Trans2", 0, 0, 0 },
 	{
-		" NT_CreateX", 0,
 		/* [CIFS/1.0, Sec. 4.2.1] */
-		"wChainedCommand\0wNextOffset\0r\0"
-		"wNameLength\0lCreateFlags\0lRootDirFID\0"
-		"lDesiredAccess\0R\0R\0R\0R\0"
-		"lNTFileAttributes\0lFileShareAccess\0"
-		"R\0R\0lCreateOption\0lImpersonationLevel\0"
-		"bSecurityFlags\0wByteCount\0r\0"
+		"_NT_CreateX", 0,
+		/* Call */
+		"wChainedCommand\0"
+		"wNextOffset\0r\0"
+		"dNameLength\0"
+		"lCreateFlags\0"
+		"lRootDirFID\0"
+		"lDesiredAccess\0"
+		"lAllocSizeLow\0"
+		"lAllocSizeHigh\0"
+		"lNTFileAttributes\0"
+		"lShareAccess\0"
+		"lOpenDisposition\0"
+		"lCreateOption\0"
+		"lImpersonationLevel\0"
+		"bSecurityFlags\0"
+		"dByteCount\0r\0"
 		"UFileName\0\0",
-		"wChainedCommand\0wNextOffset\0"
-		"bOplockLevel\0WFileID\0lCreateAction\0\0"
+		/* Reply */
+		"wChainedCommand\0"
+		"wNextOffset\0"
+		"bOplockLevel\0"
+		"WFileID\0"
+		"lCreateAction\0\0"
 	},
 	{ 0, 0, 0, 0 },
 	{
-		" NT_Cancel", 0,
+		"_NT_Cancel", 0,
 		/* [CIFS/1.0, Sec. 4.1.8] */
-		"wByteCount\0", 0
+		"dByteCount\0", 0
 	},
 	{ 0, 0, 0, 0 },
 	{ 0, 0, 0, 0 },
@@ -505,15 +612,56 @@
 	{ 0, 0, 0, 0 }
 };
 
-/* Helpers to get short and int values in Intel order. */
-static ushort_t
+/* Helpers to get values in Intel order (often mis-aligned). */
+static uint16_t
 get2(uchar_t *p) {
 	return (p[0] + (p[1]<<8));
 }
-static uint_t
+static uint32_t
 get4(uchar_t *p) {
 	return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
 }
+static uint64_t
+get8(uchar_t *p) {
+	return (get4(p) | ((uint64_t)get4(p+4) << 32));
+}
+
+/*
+ * Support displaying NT times.
+ * Number of seconds between 1970 and 1601 year
+ * (134774 days)
+ */
+static const uint64_t DIFF1970TO1601 = 11644473600ULL;
+static const uint32_t TEN_MIL = 10000000UL;
+static char *
+format_nttime(uint64_t nt_time)
+{
+	uint64_t nt_sec;	/* seconds */
+	uint64_t nt_tus;	/* tenths of uSec. */
+	uint32_t ux_nsec;
+	int64_t ux_sec;
+
+	/* Optimize time zero. */
+	if (nt_time == 0) {
+		ux_sec = 0;
+		ux_nsec = 0;
+		goto out;
+	}
+
+	nt_sec = nt_time / TEN_MIL;
+	nt_tus = nt_time % TEN_MIL;
+
+	if (nt_sec <= DIFF1970TO1601) {
+		ux_sec = 0;
+		ux_nsec = 0;
+		goto out;
+	}
+	ux_sec = nt_sec - DIFF1970TO1601;
+	ux_nsec = nt_tus * 100;
+
+out:
+	return (format_time(ux_sec, ux_nsec));
+}
 
 /*
  * This is called by snoop_netbios.c.
@@ -523,20 +671,17 @@
 interpret_smb(int flags, uchar_t *data, int len)
 {
 	struct smb *smb;
-	char *call_reply_detail, *call_reply_sum;
 	struct decode *decoder;
-	char xtra[300];
-	char *line;
+	char xtra[MAXLINE];
+	ushort_t smb_flags2;
+	void (*func)(int, uchar_t *, int, char *, int);
+
+	if (len < sizeof (struct smb))
+		return;
 
 	smb = (struct smb *)data;
 	decoder = &SMBtable[smb->com & 255];
-	if (smb->flags & SERVER_RESPONSE) {
-		call_reply_detail = "SERVER RESPONSE";
-		call_reply_sum = "R";
-	} else {
-		call_reply_detail =	"CLIENT REQUEST";
-		call_reply_sum = "C";
-	}
+	smb_flags2 = get2(smb->flags2);
 	xtra[0] = '\0';
 
 	/*
@@ -546,83 +691,94 @@
 	if (flags & F_DTAIL) {
 		show_header("SMB:  ", "SMB Header", len);
 		show_space();
-		sprintf(GETLINE, "%s", call_reply_detail);
 
-		(void) sprintf(GETLINE, "Command code = 0x%x",
-				smb->com);
-		if (decoder->name)
-			(void) sprintf(GETLINE,
-				"Command name =  SMB%s", decoder->name);
-
-		show_space();
-		sprintf(GETLINE, "SMB Status:");
+		if (smb->flags & SERVER_RESPONSE)
+			show_line("SERVER RESPONSE");
+		else
+			show_line("CLIENT REQUEST");
 
-		/* Error classes [X/Open-SMB, Sec. 5.6] */
-		switch (smb->rcls) {
-		case 0x00:
-			sprintf(GETLINE,
-				"   - Error class = No error");
-			break;
-		case 0x01:
-			sprintf(GETLINE,
-				"   - Error class = Operating System");
-			break;
-		case 0x02:
-			sprintf(GETLINE,
-				"   - Error class = LMX server");
-			break;
-		case 0x03:
-			sprintf(GETLINE,
-				"   - Error class = Hardware");
-			break;
-		case 0xff:
-		default:
-			sprintf(GETLINE,
-				"   - Error class = Incorrect format.");
-			break;
+		if (decoder->name)
+			show_printf("Command code = 0x%x (SMB%s)",
+			    smb->com, decoder->name);
+		else
+			show_printf("Command code = 0x%x", smb->com);
+
+		/*
+		 * NT status or error class/code
+		 * [X/Open-SMB, Sec. 5.6]
+		 */
+		if (smb_flags2 & FLAGS2_NT_STATUS) {
+			show_printf("NT Status = %x", get4(smb->err));
+		} else {
+			/* Error classes [X/Open-SMB, Sec. 5.6] */
+			show_printf("Error class/code = %d/%d",
+			    smb->err[0], get2(&smb->err[2]));
 		}
 
-		if (smb->err[0] != 0x00) {
-			sprintf(GETLINE,
-				"   - Error code = %x", smb->err[0]);
-		} else
-			sprintf(GETLINE, "   - Error code = No error");
-
-		show_space();
-
-		sprintf(GETLINE, "Header:");
-		sprintf(GETLINE, "   - Tree ID      (TID) = 0x%.4x",
-			get2(smb->tid));
-		sprintf(GETLINE, "   - Process ID   (PID) = 0x%.4x",
-			get2(smb->pid));
-		sprintf(GETLINE, "   - User ID      (UID) = 0x%.4x",
-			get2(smb->uid));
-		sprintf(GETLINE, "   - Multiplex ID (MID) = 0x%.4x",
-			get2(smb->mid));
-		sprintf(GETLINE, "   - Flags summary = 0x%.2x",
-					smb->flags);
-		sprintf(GETLINE, "   - Flags2 summary = 0x%.4x",
-					get2(smb->flags2));
+		show_printf("Flags summary = 0x%.2x", smb->flags);
+		show_printf("Flags2 summary = 0x%.4x", smb_flags2);
+		show_printf("Tree ID  (TID) = 0x%.4x", get2(smb->tid));
+		show_printf("Proc. ID (PID) = 0x%.4x", get2(smb->pid));
+		show_printf("User ID  (UID) = 0x%.4x", get2(smb->uid));
+		show_printf("Mux. ID  (MID) = 0x%.4x", get2(smb->mid));
 		show_space();
 	}
 
-	if (decoder->func)
-		(decoder->func)(flags, (uchar_t *)data, len, xtra);
-	else
-		interpret_default(flags, (uchar_t *)data, len, xtra);
+	if ((func = decoder->func) == NULL)
+		func = interpret_default;
+	(*func)(flags, (uchar_t *)data, len, xtra, sizeof (xtra));
 
 	if (flags & F_SUM) {
-		line = get_sum_line();
-		if (decoder->name)
-			sprintf(line,
-			"SMB %s Code=0x%x Name=SMB%s %sError=%x ",
-			call_reply_sum, smb->com, decoder->name, xtra,
-			smb->err[0]);
+		char *p;
+		int sz, tl;
+
+		/* Will advance p and decr. sz */
+		p = get_sum_line();
+		sz = MAXLINE;
+
+		/* Call or Reply */
+		if (smb->flags & SERVER_RESPONSE)
+			tl = snprintf(p, sz, "SMB R");
+		else
+			tl = snprintf(p, sz, "SMB C");
+		p += tl;
+		sz -= tl;
+
+		/* The name, if known, else the cmd code */
+		if (decoder->name) {
+			tl = snprintf(p, sz, " Cmd=SMB%s", decoder->name);
+		} else {
+			tl = snprintf(p, sz, " Cmd=0x%02X", smb->com);
+		}
+		p += tl;
+		sz -= tl;
 
-		else sprintf(line, "SMB %s Code=0x%x Error=%x ",
-					call_reply_sum, smb->com, smb->err[0]);
+		/*
+		 * The "extra" (cmd-specific summary).
+		 * If non-null, has leading blank.
+		 */
+		if (xtra[0] != '\0') {
+			tl = snprintf(p, sz, "%s", xtra);
+			p += tl;
+			sz -= tl;
+		}
 
-		line += strlen(line);
+		/*
+		 * NT status or error class/code
+		 * [X/Open-SMB, Sec. 5.6]
+		 *
+		 * Only show for response, not call.
+		 */
+		if (smb->flags & SERVER_RESPONSE) {
+			if (smb_flags2 & FLAGS2_NT_STATUS) {
+				uint_t status = get4(smb->err);
+				snprintf(p, sz, " Status=0x%x", status);
+			} else {
+				uchar_t errcl = smb->err[0];
+				ushort_t code = get2(&smb->err[2]);
+				snprintf(p, sz, " Error=%d/%d", errcl, code);
+			}
+		}
 	}
 
 	if (flags & F_DTAIL)
@@ -636,14 +792,13 @@
 	char buff[80];
 	char word[10];
 
-	buff[0] = word[0] = '\0';
-	sprintf(GETLINE, "Byte values (in hex):");
+	(void) strlcpy(buff, "  ", sizeof (buff));
 	for (i = 0; i < bytecount; i++) {
-		sprintf(word, "%.2x ", data[i]);
-		strcat(buff, word);
+		snprintf(word, sizeof (word), "%.2x ", data[i]);
+		(void) strlcat(buff, word, sizeof (buff));
 		if ((i+1)%16 == 0 || i == (bytecount-1)) {
-			sprintf(GETLINE, "%s", buff);
-			strcpy(buff, "");
+			show_line(buff);
+			(void) strlcpy(buff, "  ", sizeof (buff));
 		}
 	}
 }
@@ -675,30 +830,58 @@
 }
 
 /*
+ * Convenience macro to copy a string from the data,
+ * either in UCS-2 or ASCII as indicated by UCS.
+ * OBUF must be an array type (see sizeof) and
+ * DP must be an L-value (this increments it).
+ */
+#define	GET_STRING(OBUF, DP, UCS)				\
+{								\
+	int _len, _sz = sizeof (OBUF);				\
+	if (UCS) {						\
+		if (((uintptr_t)DP) & 1)			\
+			DP++;					\
+		_len = unicode2ascii(OBUF, _sz, DP, 2 * _sz);	\
+		DP += 2 * (_len + 1);				\
+	} else {						\
+		_len = strlcpy(OBUF, (char *)DP, _sz);		\
+		DP += (_len + 1);				\
+	}							\
+}
+
+/*
  * TRANS2 information levels
  * [X/Open-SMB, Sec. 16.1.6]
  */
 static void
-get_info_level(char *outstr, int value)
+get_info_level(char *outstr, int outsz, int value)
 {
 
 	switch (value) {
 	case 1:
-		sprintf(outstr, "Standard"); break;
+		snprintf(outstr, outsz, "Standard");
+		break;
 	case 2:
-		sprintf(outstr, "Query EA Size"); break;
+		snprintf(outstr, outsz, "Query EA Size");
+		break;
 	case 3:
-		sprintf(outstr, "Query EAS from List"); break;
+		snprintf(outstr, outsz, "Query EAS from List");
+		break;
 	case 0x101:
-		sprintf(outstr, "Directory Info"); break;
+		snprintf(outstr, outsz, "Directory Info");
+		break;
 	case 0x102:
-		sprintf(outstr, "Full Directory Info"); break;
+		snprintf(outstr, outsz, "Full Directory Info");
+		break;
 	case 0x103:
-		sprintf(outstr, "Names Info"); break;
+		snprintf(outstr, outsz, "Names Info");
+		break;
 	case 0x104:
-		sprintf(outstr, "Both Directory Info"); break;
+		snprintf(outstr, outsz, "Both Directory Info");
+		break;
 	default:
-		sprintf(outstr, "Unknown"); break;
+		snprintf(outstr, outsz, "Unknown");
+		break;
 	}
 }
 
@@ -708,27 +891,26 @@
  */
 /* ARGSUSED */
 static void
-output_trans2_querypath(int flags, uchar_t *data, char *xtra)
+output_trans2_querypath(int flags, uchar_t *data, char *xtra, int xsz)
 {
 	int length;
 	char filename[256];
 
 	if (flags & F_SUM) {
-		length = sprintf(xtra, "QueryPathInfo ");
+		length = snprintf(xtra, xsz, " QueryPathInfo");
 		xtra += length;
+		xsz -= length;
 		data += 6;
 		(void) unicode2ascii(filename, 256, data, 512);
-		sprintf(xtra, "File=%s ", filename);
+		snprintf(xtra, xsz, " File=%s", filename);
 	}
 
 	if (flags & F_DTAIL) {
-		sprintf(GETLINE, "FunctionName = QueryPathInfo");
-		sprintf(GETLINE, "InfoLevel = 0x%.4x",
-			get2(data));
+		show_line("FunctionName = QueryPathInfo");
+		show_printf("InfoLevel = 0x%.4x", get2(data));
 		data += 6;
 		(void) unicode2ascii(filename, 256, data, 512);
-		sprintf(GETLINE, "FileName = %s",
-			filename);
+		show_printf("FileName = %s", filename);
 	}
 }
 
@@ -738,23 +920,22 @@
  */
 /* ARGSUSED */
 static void
-output_trans2_queryfile(int flags, uchar_t *data, char *xtra)
+output_trans2_queryfile(int flags, uchar_t *data, char *xtra, int xsz)
 {
 	int length;
 
 	if (flags & F_SUM) {
-		length = sprintf(xtra, "QueryFileInfo ");
+		length = snprintf(xtra, xsz, " QueryFileInfo");
 		xtra += length;
-		sprintf(xtra, "FileID=0x%x ", get2(data));
+		xsz -= length;
+		snprintf(xtra, xsz, " FileID=0x%x", get2(data));
 	}
 
 	if (flags & F_DTAIL) {
-		sprintf(GETLINE, "FunctionName = QueryFileInfo");
-		sprintf(GETLINE, "FileID = 0x%.4x",
-			get2(data));
+		show_line("FunctionName = QueryFileInfo");
+		show_printf("FileID = 0x%.4x", get2(data));
 		data += 2;
-		sprintf(GETLINE, "InfoLevel = 0x%.4x",
-			get2(data));
+		show_printf("InfoLevel = 0x%.4x", get2(data));
 	}
 }
 
@@ -764,23 +945,22 @@
  */
 /* ARGSUSED */
 static void
-output_trans2_setfile(int flags, uchar_t *data, char *xtra)
+output_trans2_setfile(int flags, uchar_t *data, char *xtra, int xsz)
 {
 	int length;
 
 	if (flags & F_SUM) {
-		length = sprintf(xtra, "SetFileInfo ");
+		length = snprintf(xtra, xsz, " SetFileInfo");
 		xtra += length;
-		sprintf(xtra, "FileID=0x%x ", get2(data));
+		xsz -= length;
+		snprintf(xtra, xsz, " FileID=0x%x", get2(data));
 	}
 
 	if (flags & F_DTAIL) {
-		sprintf(GETLINE, "FunctionName = SetFileInfo");
-		sprintf(GETLINE, "FileID = 0x%.4x",
-			get2(data));
+		show_line("FunctionName = SetFileInfo");
+		show_printf("FileID = 0x%.4x", get2(data));
 		data += 2;
-		sprintf(GETLINE, "InfoLevel = 0x%.4x",
-			get2(data));
+		show_printf("InfoLevel = 0x%.4x", get2(data));
 	}
 }
 
@@ -790,38 +970,34 @@
  */
 /* ARGSUSED */
 static void
-output_trans2_findfirst(int flags, uchar_t *data, char *xtra)
+output_trans2_findfirst(int flags, uchar_t *data, char *xtra, int xsz)
 {
 	int length;
 	char filename[256];
 	char infolevel[100];
 
 	if (flags & F_SUM) {
-		length = sprintf(xtra, "Findfirst ");
+		length = snprintf(xtra, xsz, " Findfirst");
 		xtra += length;
+		xsz -= length;
 		data += 12;
 		(void) unicode2ascii(filename, 256, data, 512);
-		sprintf(xtra, "File=%s ", filename);
+		snprintf(xtra, xsz, " File=%s", filename);
 	}
 
 	if (flags & F_DTAIL) {
-		sprintf(GETLINE, "FunctionName = Findfirst");
-		sprintf(GETLINE, "SearchAttributes = 0x%.4x",
-			get2(data));
+		show_line("FunctionName = Findfirst");
+		show_printf("SearchAttributes = 0x%.4x", get2(data));
 		data += 2;
-		sprintf(GETLINE, "FindCount = 0x%.4x",
-			get2(data));
+		show_printf("FindCount = 0x%.4x", get2(data));
 		data += 2;
-		sprintf(GETLINE, "FindFlags = 0x%.4x",
-			get2(data));
+		show_printf("FindFlags = 0x%.4x", get2(data));
 		data += 2;
-		get_info_level(infolevel, get2(data));
-		sprintf(GETLINE, "InfoLevel = %s",
-			infolevel);
+		get_info_level(infolevel, sizeof (infolevel), get2(data));
+		show_printf("InfoLevel = %s", infolevel);
 		data += 6;
 		(void) unicode2ascii(filename, 256, data, 512);
-		sprintf(GETLINE, "FileName = %s",
-			filename);
+		show_printf("FileName = %s", filename);
 	}
 }
 
@@ -832,41 +1008,36 @@
  */
 /* ARGSUSED */
 static void
-output_trans2_findnext(int flags, uchar_t *data, char *xtra)
+output_trans2_findnext(int flags, uchar_t *data, char *xtra, int xsz)
 {
 	int length;
 	char filename[256];
 	char infolevel[100];
 
 	if (flags & F_SUM) {
-		length = sprintf(xtra, "Findnext ");
+		length = snprintf(xtra, xsz, " Findnext");
 		xtra += length;
+		xsz -= length;
 		data += 12;
 		(void) unicode2ascii(filename, 256, data, 512);
-		sprintf(xtra, "File=%s ", filename);
+		snprintf(xtra, xsz, " File=%s", filename);
 	}
 
 	if (flags & F_DTAIL) {
-		sprintf(GETLINE, "FunctionName = Findnext");
-		sprintf(GETLINE, "FileID = 0x%.4x",
-			get2(data));
+		show_line("FunctionName = Findnext");
+		show_printf("FileID = 0x%.4x", get2(data));
 		data += 2;
-		sprintf(GETLINE, "FindCount = 0x%.4x",
-			get2(data));
+		show_printf("FindCount = 0x%.4x", get2(data));
 		data += 2;
-		get_info_level(infolevel, get2(data));
-		sprintf(GETLINE, "InfoLevel = %s",
-			infolevel);
+		get_info_level(infolevel, sizeof (infolevel), get2(data));
+		show_printf("InfoLevel = %s", infolevel);
 		data += 2;
-		sprintf(GETLINE, "FindKey = 0x%.8x",
-			get4(data));
+		show_printf("FindKey = 0x%.8x", get4(data));
 		data += 4;
-		sprintf(GETLINE, "FindFlags = 0x%.4x",
-			get2(data));
+		show_printf("FindFlags = 0x%.4x", get2(data));
 		data += 2;
 		(void) unicode2ascii(filename, 256, data, 512);
-		sprintf(GETLINE, "FileName = %s",
-			filename);
+		show_printf("FileName = %s", filename);
 	}
 }
 
@@ -876,27 +1047,26 @@
  */
 /* ARGSUSED */
 static void
-interpret_negprot(int flags, uchar_t *data, int len, char *xtra)
+interpret_negprot(int flags, uchar_t *data, int len, char *xtra, int xsz)
 {
-	int length;
+	int i, last, length;
 	int bytecount;
-	char dialect[256];
+	int key_len;
+	int wordcount;
+	char tbuf[256];
 	struct smb *smbdata;
 	uchar_t *protodata;
+	uchar_t *byte0;
+	uint64_t nttime;
+	uint32_t caps;
+	ushort_t smb_flags2;
 
 	smbdata  = (struct smb *)data;
+	smb_flags2 = get2(smbdata->flags2);
 	protodata = (uchar_t *)data + sizeof (struct smb);
-	protodata++;			/* skip wordcount */
+	wordcount = *protodata++;
 
-	if (smbdata->flags & SERVER_RESPONSE) {
-		if (flags & F_SUM) {
-			sprintf(xtra, "Dialect#=%d ", protodata[0]);
-		}
-		if (flags & F_DTAIL) {
-			sprintf(GETLINE, "Protocol Index = %d",
-					protodata[0]);
-		}
-	} else {
+	if ((smbdata->flags & SERVER_RESPONSE) == 0) {
 		/*
 		 * request packet:
 		 * short bytecount;
@@ -904,29 +1074,102 @@
 		 */
 		bytecount = get2(protodata);
 		protodata += 2;
+		byte0 = protodata;
+
+		if (flags & F_DTAIL)
+			show_printf("ByteCount = %d", bytecount);
+		if (bytecount > len)
+			bytecount = len;
+
+		/* Walk the list of dialects. */
+		i = last = 0;
+		tbuf[0] = '\0';
+		while (protodata < (byte0 + bytecount - 2)) {
+			if (*protodata++ != 2)	/* format code */
+				break;
+			length = strlcpy(tbuf, (char *)protodata,
+			    sizeof (tbuf));
+			protodata += (length + 1);
+			if (flags & F_DTAIL) {
+				show_printf("Dialect[%d] = %s",
+				    i, tbuf);
+			}
+			last = i++;
+		}
 		if (flags & F_SUM) {
-			while (bytecount > 1) {
-				length = snprintf(dialect, sizeof (dialect),
-				    "%s", (char *)protodata+1);
-				protodata += (length+2);
-				if (protodata >= data+len)
-					break;
-				bytecount -= (length+2);
-			}
-			sprintf(xtra, "LastDialect=%s ", dialect);
+			/*
+			 * Just print the last dialect, which is
+			 * normally the interesting one.
+			 */
+			snprintf(xtra, xsz, " Dialect[%d]=%s", last, tbuf);
+		}
+	} else {
+		/* Parse reply */
+		if (flags & F_SUM) {
+			snprintf(xtra, xsz, " Dialect#=%d", protodata[0]);
 		}
-		if (flags & F_DTAIL) {
-			sprintf(GETLINE, "ByteCount = %d", bytecount);
-			while (bytecount > 1) {
-				length = snprintf(dialect, sizeof (dialect),
-				    "%s", (char *)protodata+1);
-				sprintf(GETLINE, "Dialect String = %s",
-				    dialect);
-				protodata += (length+2);
-				if (protodata >= data+len)
-					break;
-				bytecount -= (length+2);
+		if ((flags & F_DTAIL) == 0)
+			return;
+		if (wordcount < 13)
+			return;
+		show_printf("WordCount = %d", wordcount);
+		show_printf("Dialect Index = %d", protodata[0]);
+		protodata += 2;
+		show_printf("Security Mode = 0x%x", protodata[0]);
+		protodata++;
+		show_printf("MaxMPXRequests = %d", get2(protodata));
+		protodata += 2;
+		show_printf("MaxVCs = %d", get2(protodata));
+		protodata += 2;
+		show_printf("MaxBufferSize = %d", get4(protodata));
+		protodata += 4;
+		show_printf("MaxRawBuffer = %d", get4(protodata));
+		protodata += 4;
+		show_printf("SessionKey = 0x%.8x", get4(protodata));
+		protodata += 4;
+
+		caps = get4(protodata);
+		protodata += 4;
+		show_printf("Capabilities = 0x%.8x", caps);
+
+		/* Server Time */
+		nttime = get8(protodata);
+		protodata += 8;
+		show_printf("Server Time = %s", format_nttime(nttime));
+
+		show_printf("Server TZ = %d", get2(protodata));
+		protodata += 2;
+
+		key_len = *protodata++;
+		show_printf("KeyLength = %d", key_len);
+		bytecount = get2(protodata);
+		protodata += 2;
+		show_printf("ByteCount = %d", bytecount);
+
+		if (smb_flags2 & FLAGS2_EXT_SEC) {
+			show_printf("Server GUID (16)");
+			output_bytes(protodata, 16);
+			protodata += 16;
+			show_printf("Security Blob (SPNEGO)");
+			output_bytes(protodata, bytecount - 16);
+		} else {
+			show_printf("NTLM Challenge: (%d)", key_len);
+			output_bytes(protodata, key_len);
+			protodata += key_len;
+			/*
+			 * Get Unicode from capabilities here,
+			 * as flags2 typically doesn't have it.
+			 * Also, this one is NOT aligned!
+			 */
+			tbuf[0] = '\0';
+			if (caps & 4) {
+				(void) unicode2ascii(tbuf, sizeof (tbuf),
+				    protodata, 2 * sizeof (tbuf));
+			} else {
+				(void) strlcpy(tbuf, (char *)protodata,
+				    sizeof (tbuf));
 			}
+			show_printf("Server Domain = %s", tbuf);
 		}
 	}
 }
@@ -935,7 +1178,7 @@
  * LAN Manager remote admin function names.
  * [X/Open-SMB, Appendix B.8]
  */
-static const char *apinames[] = {
+static const char *apiname_table[] = {
 	"RNetShareEnum",
 	"RNetShareGetInfo",
 	"NetShareSetInfo",
@@ -1042,9 +1285,55 @@
 	"DosPrintQPurge",
 	"NetServerEnum2"
 };
-static const int apimax = (
-	sizeof (apinames) /
-	sizeof (apinames[0]));
+static const int apinum_max = (
+	sizeof (apiname_table) /
+	sizeof (apiname_table[0]));
+
+static const char *
+pipeapi_name(int code)
+{
+	char *name;
+
+	switch (code) {
+	case 0x01:
+		name = "SetNmPipeState";
+		break;
+	case 0x11:
+		name = "RawReadNmPipe";
+		break;
+	case 0x21:
+		name = "QueryNmPipeState";
+		break;
+	case 0x22:
+		name = "QueryNmPipeInfo";
+		break;
+	case 0x23:
+		name = "PeekNmPipe";
+		break;
+	case 0x26:
+		name = "XactNmPipe";
+		break;
+	case 0x31:
+		name = "RawWriteNmPipe";
+		break;
+	case 0x36:
+		name = "ReadNmPipe";
+		break;
+	case 0x37:
+		name = "WriteNmPipe";
+		break;
+	case 0x53:
+		name = "WaitNmPipe";
+		break;
+	case 0x54:
+		name = "CallNmPipe";
+		break;
+	default:
+		name = "?";
+		break;
+	}
+	return (name);
+}
 
 /*
  * Interpret a "trans" SMB
@@ -1054,7 +1343,7 @@
  */
 /* ARGSUSED */
 static void
-interpret_trans(int flags, uchar_t *data, int len, char *xtra)
+interpret_trans(int flags, uchar_t *data, int len, char *xtra, int xsz)
 {
 	struct smb *smb;
 	uchar_t *vwv; /* word parameters */
@@ -1070,11 +1359,18 @@
 	int apinum;
 	int isunicode;
 	char filename[256];
+	const char *apiname;
+	const char *subcname;
+	ushort_t smb_flags2;
 
-	smb  = (struct smb *)data;
+	smb = (struct smb *)data;
+	smb_flags2 = get2(smb->flags2);
 	vwv = (uchar_t *)data + sizeof (struct smb);
 	wordcount = *vwv++;
 
+	/* Is the pathname in unicode? */
+	isunicode = smb_flags2 & FLAGS2_UNICODE;
+
 	byteparms = vwv + (2 * wordcount);
 	bytecount = get2(byteparms);
 	byteparms += 2;
@@ -1083,10 +1379,8 @@
 	 * Print the lengths before we (potentially) bail out
 	 * due to lack of data (so the user knows why we did).
 	 */
-	if (flags & F_DTAIL) {
-		sprintf(GETLINE, "WordCount = %d", wordcount);
-		sprintf(GETLINE, "ByteCount = %d", bytecount);
-	}
+	if (flags & F_DTAIL)
+		show_printf("WordCount = %d", wordcount);
 
 	/* Get length and location of params and setup data. */
 	if (!(smb->flags & SERVER_RESPONSE)) {
@@ -1106,72 +1400,93 @@
 		setupcount = *(vwv + (2 *  9));
 		setupdata  =   vwv + (2 * 10);
 	}
-	if (setupcount > 0)
-		subcode = get2(setupdata);
-	else
-		subcode = -1; /* invalid */
 
 	/* The parameters are offset from the SMB header. */
 	params = data + paramoffset;
-	if (parambytes > 0)
-		apinum = params[0];
-	else
-		apinum = -1; /* invalid */
+
+	if ((smb->flags & SERVER_RESPONSE) == 0) {
+		/* This is a CALL. */
+
+		if (setupcount > 0)
+			subcode = get2(setupdata);
+		else
+			subcode = -1; /* invalid */
+		subcname = pipeapi_name(subcode);
 
-	/* Is the pathname in unicode? */
-	isunicode = smb->flags2[1] & 0x80;
+		if (parambytes > 0)
+			apinum = params[0];
+		else
+			apinum = -1; /* invalid */
+		if (0 <= apinum && apinum < apinum_max)
+			apiname = apiname_table[apinum];
+		else
+			apiname = "?";
 
-	if (flags & F_DTAIL && !(smb->flags & SERVER_RESPONSE)) {
-		/* This is a CALL. */
+		if (flags & F_SUM) {
+			int tl;
+			/* Only get one or the other */
+			if (*subcname != '?') {
+				tl = snprintf(xtra, xsz,
+				    " Func=%s", subcname);
+				xtra += tl;
+				xsz -= tl;
+			}
+			if (*apiname != '?')
+				snprintf(xtra, xsz,
+				    " Func=%s", apiname);
+			return;
+		}
+		if ((flags & F_DTAIL) == 0)
+			return;
+
 		/* print the word parameters */
-		sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
-		sprintf(GETLINE, "TotalDataBytes = %d", get2(vwv+2));
-		sprintf(GETLINE, "MaxParamBytes = %d", get2(vwv+4));
-		sprintf(GETLINE, "MaxDataBytes = %d", get2(vwv+6));
-		sprintf(GETLINE, "MaxSetupWords = %d", vwv[8]);
-		sprintf(GETLINE, "TransFlags = 0x%.4x", get2(vwv+10));
-		sprintf(GETLINE, "Timeout = 0x%.8x", get4(vwv+12));
+		show_printf("TotalParamBytes = %d", get2(vwv));
+		show_printf("TotalDataBytes = %d", get2(vwv+2));
+		show_printf("MaxParamBytes = %d", get2(vwv+4));
+		show_printf("MaxDataBytes = %d", get2(vwv+6));
+		show_printf("MaxSetupWords = %d", vwv[8]);
+		show_printf("TransFlags = 0x%.4x", get2(vwv+10));
+		show_printf("Timeout = 0x%.8x", get4(vwv+12));
 		/* skip Reserved2 */
-		sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
-		sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
-		sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+22));
-		sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+24));
-		sprintf(GETLINE, "SetupWords = %d", setupcount);
+		show_printf("ParamBytes = %d", parambytes);
+		show_printf("ParamOffset = %d", paramoffset);
+		show_printf("DataBytes = %d", get2(vwv+22));
+		show_printf("DataOffset = %d", get2(vwv+24));
+		show_printf("SetupWords = %d", setupcount);
+		show_printf("ByteCount = %d", bytecount);
 
 		/* That finishes the VWV, now the misc. stuff. */
-		if (subcode >= 0)
-			sprintf(GETLINE, "Setup[0] = %d", subcode);
-		if (apinum >= 0)
-			sprintf(GETLINE, "APIcode = %d", apinum);
-		if (0 <= apinum && apinum < apimax)
-			sprintf(GETLINE, "APIname = %s", apinames[apinum]);
+		if (setupcount > 0)
+			show_printf("NmPipeFunc = 0x%x (%s)",
+			    subcode, subcname);
+		if (parambytes > 0)
+			show_printf("RAP_Func = %d (%s)",
+			    apinum, apiname);
 
 		/* Finally, print the byte parameters. */
-		if (isunicode) {
-			byteparms += 1;  /* alignment padding */
-			(void) unicode2ascii(
-				filename, 256, byteparms, bytecount);
-		} else {
-			strlcpy(filename, (char *)byteparms, sizeof (filename));
-		}
-		sprintf(GETLINE, "FileName = %s", filename);
-	}
-
-	if (flags & F_DTAIL && smb->flags & SERVER_RESPONSE) {
+		GET_STRING(filename, byteparms, isunicode);
+		show_printf("FileName = %s", filename);
+	} else {
 		/* This is a REPLY. */
+		if (flags & F_SUM)
+			return;
+		if ((flags & F_DTAIL) == 0)
+			return;
 		/* print the word parameters */
-		sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
-		sprintf(GETLINE, "TotalDataBytes = %d", get2(vwv+2));
+		show_printf("TotalParamBytes = %d", get2(vwv));
+		show_printf("TotalDataBytes = %d", get2(vwv+2));
 		/* skip Reserved */
-		sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
-		sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
-		sprintf(GETLINE, "ParamDispl. = 0x%.4x", get2(vwv+10));
-		sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+12));
-		sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+14));
-		sprintf(GETLINE, "DataDispl. = 0x%.4x", get2(vwv+16));
-		sprintf(GETLINE, "SetupWords = %d", setupcount);
+		show_printf("ParamBytes = 0x%.4x", parambytes);
+		show_printf("ParamOffset = 0x%.4x", paramoffset);
+		show_printf("ParamDispl. = 0x%.4x", get2(vwv+10));
+		show_printf("DataBytes = 0x%.4x", get2(vwv+12));
+		show_printf("DataOffset = 0x%.4x", get2(vwv+14));
+		show_printf("DataDispl. = 0x%.4x", get2(vwv+16));
+		show_printf("SetupWords = %d", setupcount);
+		show_printf("ByteCount = %d", bytecount);
 
-		output_bytes(byteparms, bytecount);
+		show_printf("ParamVec (%d)", parambytes);
+		output_bytes(params, parambytes);
 	}
 }
 
@@ -1181,86 +1496,100 @@
  */
 /* ARGSUSED */
 static void
-interpret_tconX(int flags, uchar_t *data, int len, char *xtra)
+interpret_tconX(int flags, uchar_t *data, int len, char *xtra, int xsz)
 {
 	int length;
+	int isunicode;
 	int bytecount;
-	int passwordlength;
 	int wordcount;
-	char tempstring[256];
+	int andxcmd;
+	int andxoffset;
+	int tconflags;
+	int pw_len;
+	char path[256];
+	char tbuf[256];
+	char svc[8];
 	struct smb *smbdata;
 	uchar_t *tcondata;
+	ushort_t smb_flags2;
 
-	smbdata  = (struct smb *)data;
+	smbdata = (struct smb *)data;
+	smb_flags2 = get2(smbdata->flags2);
 	tcondata = (uchar_t *)data + sizeof (struct smb);
 	wordcount = *tcondata++;
 
-	if (flags & F_SUM && !(smbdata->flags & SERVER_RESPONSE)) {
-		tcondata += 6;
-		passwordlength = get2(tcondata);
-		tcondata = tcondata + 4 + passwordlength;
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)tcondata);
-		sprintf(xtra, "Share=%s ", tempstring);
-	}
+	isunicode = smb_flags2 & FLAGS2_UNICODE;
 
-	if (flags & F_SUM && smbdata->flags & SERVER_RESPONSE) {
-		tcondata += 8;
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)tcondata);
-		sprintf(xtra, "Type=%s ", tempstring);
-	}
-
-	if (flags & F_DTAIL && !(smbdata->flags & SERVER_RESPONSE)) {
-		sprintf(GETLINE, "WordCount = %d", wordcount);
-		sprintf(GETLINE, "ChainedCommand = 0x%.2x",
-			tcondata[0]);
+	if ((smbdata->flags & SERVER_RESPONSE) == 0) {
+		/* Request */
+		if (wordcount < 4)
+			return;
+		andxcmd = get2(tcondata);
 		tcondata += 2;
-		sprintf(GETLINE, "NextOffset = 0x%.4x",
-			get2(tcondata));
+		andxoffset = get2(tcondata);
 		tcondata += 2;
-		sprintf(GETLINE, "DisconnectFlag = 0x%.4x",
-			get2(tcondata));
+		tconflags = get2(tcondata);
 		tcondata += 2;
-		passwordlength = get2(tcondata);
-		sprintf(GETLINE, "PasswordLength = 0x%.4x",
-			passwordlength);
+		pw_len = get2(tcondata);
 		tcondata += 2;
 		bytecount = get2(tcondata);
-		sprintf(GETLINE, "ByteCount = %d", bytecount);
-		tcondata = tcondata + 2 + passwordlength;
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)tcondata);
-		tcondata += (length+1);
-		sprintf(GETLINE, "FileName = %s", tempstring);
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)tcondata);
-		tcondata += (length+1);
-		sprintf(GETLINE, "ServiceName = %s", tempstring);
-	}
+		tcondata += 2;
+
+		/* skip password */
+		if (pw_len > len)
+			pw_len = len;
+		tcondata += pw_len;
+
+		GET_STRING(path, tcondata, isunicode);
+		(void) strlcpy(svc, (char *)tcondata, sizeof (svc));
+
+		if (flags & F_SUM) {
+			snprintf(xtra, xsz, " Share=%s", path);
+			return;
+		}
+
+		if ((flags & F_DTAIL) == 0)
+			return;
 
-	if (flags & F_DTAIL && smbdata->flags & SERVER_RESPONSE) {
-		sprintf(GETLINE, "WordCount = %d", wordcount);
-		sprintf(GETLINE, "ChainedCommand = 0x%.2x",
-			tcondata[0]);
+		show_printf("WordCount = %d", wordcount);
+		show_printf("ChainedCommand = 0x%.2x", andxcmd);
+		show_printf("NextOffset = 0x%.4x", andxoffset);
+		show_printf("TconFlags = 0x%.4x", tconflags);
+		show_printf("PasswordLength = 0x%.4x", pw_len);
+		show_printf("ByteCount = %d", bytecount);
+		show_printf("SharePath = %s", path);
+		show_printf("ServiceType = %s", svc);
+	} else {
+		/* response */
+		if (wordcount < 3)
+			return;
+		andxcmd = get2(tcondata);
 		tcondata += 2;
-		sprintf(GETLINE, "NextOffset = 0x%.4x",
-			get2(tcondata));
+		andxoffset = get2(tcondata);
 		tcondata += 2;
-		sprintf(GETLINE, "OptionalSupport = 0x%.4x",
-			get2(tcondata));
+		tconflags = get2(tcondata);
 		tcondata += 2;
 		bytecount = get2(tcondata);
-		sprintf(GETLINE, "ByteCount = %d", bytecount);
 		tcondata += 2;
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)tcondata);
-		tcondata += (length+1);
-		sprintf(GETLINE, "ServiceName = %s", tempstring);
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)tcondata);
-		tcondata += (length+1);
-		sprintf(GETLINE, "NativeFS = %s", tempstring);
+
+		length = strlcpy(svc, (char *)tcondata, sizeof (svc));
+		tcondata += (length + 1);
+
+		if (flags & F_SUM) {
+			snprintf(xtra, xsz, " Type=%s", svc);
+			return;
+		}
+		if ((flags & F_DTAIL) == 0)
+			return;
+
+		show_printf("WordCount = %d", wordcount);
+		show_printf("ChainedCommand = 0x%.2x", andxcmd);
+		show_printf("NextOffset = 0x%.4x", andxoffset);
+		show_printf("OptionalSupport = 0x%.4x", tconflags);
+		show_printf("ByteCount = %d", bytecount);
+		show_printf("ServiceType = %s", svc);
+		GET_STRING(tbuf, tcondata, isunicode);
+		show_printf("NativeFS = %s", tbuf);
 	}
 }
 
@@ -1270,155 +1599,200 @@
  */
 /* ARGSUSED */
 static void
-interpret_sesssetupX(int flags, uchar_t *data, int len, char *xtra)
+interpret_sesssetupX(int flags, uchar_t *data, int len, char *xtra, int xsz)
 {
-	int length;
 	int bytecount;
-	int passwordlength;
+	int lm_pw_len;
+	int ext_security;
+	int sec_blob_len;
 	int isunicode;
-	int upasswordlength;
+	int nt_pw_len;
 	int wordcount;
 	int cap;
-	char tempstring[256];
+	char tbuf[256];
 	struct smb *smbdata;
 	uchar_t *setupdata;
+	ushort_t smb_flags2;
 
 	smbdata  = (struct smb *)data;
+	smb_flags2 = get2(smbdata->flags2);
 	setupdata = (uchar_t *)data + sizeof (struct smb);
 	wordcount = *setupdata++;
 
-	isunicode = smbdata->flags2[1] & 0x80;
+	isunicode = smb_flags2 & FLAGS2_UNICODE;
+	ext_security = smb_flags2 & FLAGS2_EXT_SEC;
 
-	if (flags & F_SUM && !(smbdata->flags & SERVER_RESPONSE)) {
-		if (wordcount != 13)
+	if ((smbdata->flags & SERVER_RESPONSE) == 0) {
+		/* request summary */
+		if (flags & F_SUM) {
+			if (ext_security) {
+				/* No decoder for SPNEGO */
+				snprintf(xtra, xsz, " (SPNEGO)");
+				return;
+			}
+			if (wordcount != 13)
+				return;
+			setupdata += 14;
+			lm_pw_len = get2(setupdata);
+			setupdata += 2;
+			nt_pw_len = get2(setupdata);
+			setupdata += 6;
+			cap = get4(setupdata);
+			setupdata += 6 + lm_pw_len + nt_pw_len;
+
+			GET_STRING(tbuf, setupdata, isunicode);
+			snprintf(xtra, xsz, " Username=%s", tbuf);
+		}
+
+		if ((flags & F_DTAIL) == 0)
 			return;
-		setupdata += 14;
-		passwordlength = get2(setupdata);
-		setupdata += 2;
-		upasswordlength = get2(setupdata);
-		setupdata += 6;
-		cap = get4(setupdata);
-		setupdata = setupdata + 6 + passwordlength + upasswordlength;
-		if (isunicode) {
-			setupdata += 1;
-			(void) unicode2ascii(tempstring, 256, setupdata, 256);
-			sprintf(xtra, "Username=%s ", tempstring);
-		} else {
-			length = snprintf(tempstring, sizeof (tempstring), "%s",
-			    (char *)setupdata);
-			sprintf(xtra, "Username=%s ", tempstring);
-		}
-	}
 
-	if (flags & F_DTAIL && !(smbdata->flags & SERVER_RESPONSE)) {
-		if (wordcount != 13)
+		/* request detail */
+		show_printf("WordCount = %d", wordcount);
+		if (wordcount < 7)
 			return;
-		sprintf(GETLINE, "ChainedCommand = 0x%.2x",
-			setupdata[0]);
+		/* words 0 - 6 */
+		show_printf("ChainedCommand = 0x%.2x", setupdata[0]);
 		setupdata += 2;
-		sprintf(GETLINE, "NextOffset = 0x%.4x",
-			get2(setupdata));
+		show_printf("NextOffset = 0x%.4x", get2(setupdata));
 		setupdata += 2;
-		sprintf(GETLINE, "MaxBufferSize = 0x%.4x",
-			get2(setupdata));
+		show_printf("MaxBufferSize = %d", get2(setupdata));
 		setupdata += 2;
-		sprintf(GETLINE, "MaxMPXRequests = %d",
-			get2(setupdata));
+		show_printf("MaxMPXRequests = %d", get2(setupdata));
 		setupdata += 2;
-		sprintf(GETLINE, "VCNumber = %d",
-			get2(setupdata));
+		show_printf("VCNumber = %d", get2(setupdata));
 		setupdata += 2;
-		sprintf(GETLINE, "SessionKey = %d",
-			get4(setupdata));
+		show_printf("SessionKey = 0x%.8x", get4(setupdata));
 		setupdata += 4;
-		passwordlength = get2(setupdata);
-		sprintf(GETLINE, "PasswordLength = 0x%.4x",
-			passwordlength);
-		setupdata += 2;
-		upasswordlength = get2(setupdata);
-		sprintf(GETLINE, "UnicodePasswordLength = 0x%.4x",
-			upasswordlength);
-		setupdata += 6;
+
+		if (ext_security) {
+			if (wordcount != 12)
+				return;
+			/* word 7 */
+			sec_blob_len = get2(setupdata);
+			setupdata += 2;
+			show_printf("Sec. blob len = %d", sec_blob_len);
+			/* words 8, 9 (reserved) */
+			setupdata += 4;
+		} else {
+			if (wordcount != 13)
+				return;
+			/* word 7 */
+			lm_pw_len = get2(setupdata);
+			setupdata += 2;
+			show_printf("LM_Hash_Len = %d", lm_pw_len);
+			/* word 8 */
+			nt_pw_len = get2(setupdata);
+			setupdata += 2;
+			show_printf("NT_Hash_Len = %d", nt_pw_len);
+			/* words 9, 10 (reserved) */
+			setupdata += 4;
+		}
+
 		cap = get4(setupdata);
-		sprintf(GETLINE, "Capabilities = 0x%0.8x", cap);
+		show_printf("Capabilities = 0x%.8x", cap);
 		setupdata += 4;
+
 		bytecount = get2(setupdata);
-		sprintf(GETLINE, "ByteCount = %d", bytecount);
-		setupdata = setupdata + 2 + passwordlength + upasswordlength;
-		if (isunicode) {
-			setupdata++;
-			length = 2*unicode2ascii(
-				tempstring, 256, setupdata, 256);
-			if (length == 2) {
-				sprintf(GETLINE,
-						"AccountName = %s", tempstring);
-				sprintf(GETLINE,
-						"DomainName = %s", tempstring);
-				setupdata += 3;
-			} else {
-				setupdata += length;
-				sprintf(GETLINE,
-						"AccountName = %s", tempstring);
-				length = 2*unicode2ascii(
-					tempstring, 256, setupdata, 256);
-				setupdata += length;
-				sprintf(GETLINE,
-						"DomainName = %s", tempstring);
-			}
-			length = 2*unicode2ascii(
-				tempstring, 256, setupdata, 256);
-			setupdata += (length+2);
-			sprintf(GETLINE,
-					"NativeOS = %s", tempstring);
-			length = 2*unicode2ascii(
-				tempstring, 256, setupdata, 256);
-			sprintf(GETLINE,
-					"NativeLanman = %s", tempstring);
+		setupdata += 2;
+		show_printf("ByteCount = %d", bytecount);
+
+		if (ext_security) {
+			/* No decoder for SPNEGO.  Just dump hex. */
+			show_printf("Security blob: (SPNEGO)");
+			output_bytes(setupdata, sec_blob_len);
+			setupdata += sec_blob_len;
 		} else {
-			length = snprintf(tempstring, sizeof (tempstring), "%s",
-			    (char *)setupdata);
-			setupdata += (length+1);
-			sprintf(GETLINE, "AccountName = %s", tempstring);
-			length = snprintf(tempstring, sizeof (tempstring), "%s",
-			    (char *)setupdata);
-			setupdata += (length+1);
-			sprintf(GETLINE, "DomainName = %s", tempstring);
-			length = snprintf(tempstring, sizeof (tempstring), "%s",
-			    (char *)setupdata);
-			setupdata += (length+1);
-			sprintf(GETLINE, "NativeOS = %s", tempstring);
-			snprintf(tempstring, sizeof (tempstring), "%s",
-			    (char *)setupdata);
-			sprintf(GETLINE, "NativeLanman = %s", tempstring);
+			/* Dump password hashes */
+			if (lm_pw_len > 0) {
+				show_printf("LM Hash (%d bytes)", lm_pw_len);
+				output_bytes(setupdata, lm_pw_len);
+				setupdata += lm_pw_len;
+			}
+			if (nt_pw_len > 0) {
+				show_printf("NT Hash (%d bytes)", nt_pw_len);
+				output_bytes(setupdata, nt_pw_len);
+				setupdata += nt_pw_len;
+			}
+
+			/* User */
+			GET_STRING(tbuf, setupdata, isunicode);
+			show_printf("AccountName = %s", tbuf);
+
+			/* Domain */
+			GET_STRING(tbuf, setupdata, isunicode);
+			show_printf("DomainName = %s", tbuf);
 		}
-	}
+
+		/*
+		 * Remainder is the same for etc. sec. or not
+		 * Native OS, Native LanMan
+		 */
+		GET_STRING(tbuf, setupdata, isunicode);
+		show_printf("NativeOS = %s", tbuf);
 
-	if (flags & F_DTAIL && smbdata->flags & SERVER_RESPONSE) {
-		if (wordcount != 3)
+		GET_STRING(tbuf, setupdata, isunicode);
+		show_printf("NativeLanman = %s", tbuf);
+	} else {
+		/* response summary */
+		if (flags & F_SUM) {
+			if (ext_security) {
+				/* No decoder for SPNEGO */
+				snprintf(xtra, xsz, " (SPNEGO)");
+			}
+			return;
+		}
+
+		if ((flags & F_DTAIL) == 0)
 			return;
-		sprintf(GETLINE, "ChainedCommand = 0x%.2x",
-			setupdata[0]);
-		setupdata += 2;
-		sprintf(GETLINE, "NextOffset = 0x%.4x",
-			get2(setupdata));
+
+		/* response detail */
+		show_printf("WordCount = %d", wordcount);
+		if (wordcount < 3)
+			return;
+
+		show_printf("ChainedCommand = 0x%.2x", setupdata[0]);
 		setupdata += 2;
-		sprintf(GETLINE, "SetupAction = 0x%.4x",
-			get2(setupdata));
+		show_printf("NextOffset = 0x%.4x", get2(setupdata));
 		setupdata += 2;
-		bytecount = get2(setupdata);
-		sprintf(GETLINE, "ByteCount = %d", bytecount);
+		show_printf("SetupAction = 0x%.4x", get2(setupdata));
 		setupdata += 2;
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)setupdata);
-		setupdata += (length+1);
-		sprintf(GETLINE, "NativeOS = %s", tempstring);
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)setupdata);
-		setupdata += (length+1);
-		sprintf(GETLINE, "NativeLanman = %s", tempstring);
-		length = snprintf(tempstring, sizeof (tempstring), "%s",
-		    (char *)setupdata);
-		sprintf(GETLINE, "DomainName = %s", tempstring);
+
+		if (ext_security) {
+			if (wordcount != 4)
+				return;
+			sec_blob_len = get2(setupdata);
+			setupdata += 2;
+			show_printf("Sec. blob len = %d", sec_blob_len);
+		} else {
+			if (wordcount != 3)
+				return;
+		}
+
+		bytecount = get2(setupdata);
+		setupdata += 2;
+		show_printf("ByteCount = %d", bytecount);
+
+		if (ext_security) {
+			/* No decoder for SPNEGO.  Just dump hex. */
+			show_line("Security blob: (SPNEGO)");
+			output_bytes(setupdata, sec_blob_len);
+			setupdata += sec_blob_len;
+		}
+
+		/*
+		 * Native OS, Native LanMan
+		 */
+		GET_STRING(tbuf, setupdata, isunicode);
+		show_printf("NativeOS = %s", tbuf);
+
+		GET_STRING(tbuf, setupdata, isunicode);
+		show_printf("NativeLanman = %s", tbuf);
+
+		if (ext_security == 0) {
+			GET_STRING(tbuf, setupdata, isunicode);
+			show_printf("DomainName = %s", tbuf);
+		}
 	}
 }
 
@@ -1430,7 +1804,7 @@
  */
 /* ARGSUSED */
 static void
-interpret_trans2(int flags, uchar_t *data, int len, char *xtra)
+interpret_trans2(int flags, uchar_t *data, int len, char *xtra, int xsz)
 {
 	struct smb *smb;
 	uchar_t *vwv; /* word parameters */
@@ -1458,8 +1832,8 @@
 	 * due to lack of data (so the user knows why we did).
 	 */
 	if (flags & F_DTAIL) {
-		sprintf(GETLINE, "WordCount = %d", wordcount);
-		sprintf(GETLINE, "ByteCount = %d", bytecount);
+		show_printf("WordCount = %d", wordcount);
+		show_printf("ByteCount = %d", bytecount);
 	}
 
 	/* Get length and location of params and setup data. */
@@ -1491,22 +1865,22 @@
 	if (flags & F_DTAIL && !(smb->flags & SERVER_RESPONSE)) {
 		/* This is a CALL. */
 		/* print the word parameters */
-		sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
-		sprintf(GETLINE, "TotalDataBytes = %d", get2(vwv+2));
-		sprintf(GETLINE, "MaxParamBytes = %d", get2(vwv+4));
-		sprintf(GETLINE, "MaxDataBytes = %d", get2(vwv+6));
-		sprintf(GETLINE, "MaxSetupWords = %d", vwv[8]);
-		sprintf(GETLINE, "TransFlags = 0x%.4x", get2(vwv+10));
-		sprintf(GETLINE, "Timeout = 0x%.8x", get4(vwv+12));
+		show_printf("TotalParamBytes = %d", get2(vwv));
+		show_printf("TotalDataBytes = %d", get2(vwv+2));
+		show_printf("MaxParamBytes = %d", get2(vwv+4));
+		show_printf("MaxDataBytes = %d", get2(vwv+6));
+		show_printf("MaxSetupWords = %d", vwv[8]);
+		show_printf("TransFlags = 0x%.4x", get2(vwv+10));
+		show_printf("Timeout = 0x%.8x", get4(vwv+12));
 		/* skip Reserved2 */
-		sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
-		sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
-		sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+22));
-		sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+24));
-		sprintf(GETLINE, "SetupWords = %d", setupcount);
+		show_printf("ParamBytes = 0x%.4x", parambytes);
+		show_printf("ParamOffset = 0x%.4x", paramoffset);
+		show_printf("DataBytes = 0x%.4x", get2(vwv+22));
+		show_printf("DataOffset = 0x%.4x", get2(vwv+24));
+		show_printf("SetupWords = %d", setupcount);
 
 		/* That finishes the VWV, now the misc. stuff. */
-		sprintf(GETLINE, "FunctionCode = %d", subcode);
+		show_printf("FunctionCode = %d", subcode);
 	}
 
 	if (!(smb->flags & SERVER_RESPONSE)) {
@@ -1516,25 +1890,25 @@
 			name = "Open";
 			goto name_only;
 		case TRANS2_FIND_FIRST:
-			output_trans2_findfirst(flags, params, xtra);
+			output_trans2_findfirst(flags, params, xtra, xsz);
 			break;
 		case TRANS2_FIND_NEXT2:
-			output_trans2_findnext(flags, params, xtra);
+			output_trans2_findnext(flags, params, xtra, xsz);
 			break;
 		case TRANS2_QUERY_FS_INFORMATION:
 			name = "QueryFSInfo";
 			goto name_only;
 		case TRANS2_QUERY_PATH_INFORMATION:
-			output_trans2_querypath(flags, params, xtra);
+			output_trans2_querypath(flags, params, xtra, xsz);
 			break;
 		case TRANS2_SET_PATH_INFORMATION:
 			name = "SetPathInfo";
 			goto name_only;
 		case TRANS2_QUERY_FILE_INFORMATION:
-			output_trans2_queryfile(flags, params, xtra);
+			output_trans2_queryfile(flags, params, xtra, xsz);
 			break;
 		case TRANS2_SET_FILE_INFORMATION:
-			output_trans2_setfile(flags, params, xtra);
+			output_trans2_setfile(flags, params, xtra, xsz);
 			break;
 		case TRANS2_CREATE_DIRECTORY:
 			name = "CreateDir";
@@ -1545,9 +1919,9 @@
 			/* fall through */
 		name_only:
 			if (flags & F_SUM)
-				sprintf(xtra, "%s ", name);
+				snprintf(xtra, xsz, " %s", name);
 			if (flags & F_DTAIL)
-				sprintf(GETLINE, "FunctionName = %s", name);
+				show_printf("FunctionName = %s", name);
 			break;
 		}
 	}
@@ -1555,16 +1929,16 @@
 	if (flags & F_DTAIL && smb->flags & SERVER_RESPONSE) {
 		/* This is a REPLY. */
 		/* print the word parameters */
-		sprintf(GETLINE, "TotalParamBytes = %d", get2(vwv));
-		sprintf(GETLINE, "TotalDataBytes = %d",  get2(vwv+2));
+		show_printf("TotalParamBytes = %d", get2(vwv));
+		show_printf("TotalDataBytes = %d",  get2(vwv+2));
 		/* skip Reserved */
-		sprintf(GETLINE, "ParamBytes = 0x%.4x", parambytes);
-		sprintf(GETLINE, "ParamOffset = 0x%.4x", paramoffset);
-		sprintf(GETLINE, "ParamDispl. = 0x%.4x", get2(vwv+10));
-		sprintf(GETLINE, "DataBytes = 0x%.4x", get2(vwv+12));
-		sprintf(GETLINE, "DataOffset = 0x%.4x", get2(vwv+14));
-		sprintf(GETLINE, "DataDispl. = 0x%.4x", get2(vwv+16));
-		sprintf(GETLINE, "SetupWords = %d", setupcount);
+		show_printf("ParamBytes = 0x%.4x", parambytes);
+		show_printf("ParamOffset = 0x%.4x", paramoffset);
+		show_printf("ParamDispl. = 0x%.4x", get2(vwv+10));
+		show_printf("DataBytes = 0x%.4x", get2(vwv+12));
+		show_printf("DataOffset = 0x%.4x", get2(vwv+14));
+		show_printf("DataDispl. = 0x%.4x", get2(vwv+16));
+		show_printf("SetupWords = %d", setupcount);
 
 		output_bytes(byteparms, bytecount);
 	}
@@ -1572,29 +1946,36 @@
 
 
 static void
-interpret_default(int flags, uchar_t *data, int len, char *xtra)
+interpret_default(int flags, uchar_t *data, int len, char *xtra, int xsz)
 {
 	int slength;
-	int i;
+	int i, tl;
+	int isunicode;
 	int printit;
 	int wordcount;
+	int outsz;
 	char *outstr;
-	char *prfmt;
 	char *format;
 	char valuetype;
 	char word[10];
 	char *label;
-	char tempstring[256];
+	char tempstr[256];
 	uchar_t *comdata, *limit;
 	char buff[80];
 	struct smb *smbdata;
 	struct decode *decoder;
+	uchar_t bval;
+	ushort_t wval;
+	ushort_t smb_flags2;
+	uint_t lval;
 
 	smbdata  = (struct smb *)data;
+	smb_flags2 = get2(smbdata->flags2);
 	comdata = (uchar_t *)data + sizeof (struct smb);
 	wordcount = *comdata++;
 	limit = data + len;
 
+	isunicode = smb_flags2 & FLAGS2_UNICODE;
 	decoder = &SMBtable[smbdata->com & 255];
 
 	if (smbdata->flags & SERVER_RESPONSE)
@@ -1603,87 +1984,150 @@
 		format = decoder->callfmt;
 
 	if (!format || strlen(format) == 0) {
-		if (wordcount == 0 || flags & F_SUM)
+		if (flags & F_SUM)
+			return;
+		show_printf("WordCount = %d", wordcount);
+		if (wordcount == 0)
 			return;
-		sprintf(GETLINE, "WordCount = %d", wordcount);
-		sprintf(GETLINE, "Word values (in hex):");
+		show_line("Word values (in hex):");
+		buff[0] = '\0';
 		for (i = 0; i < wordcount; i++) {
-			sprintf(word, "%.4x ", get2(comdata));
+			snprintf(word, sizeof (word), "%.4x ", get2(comdata));
 			comdata += 2;
 			if (comdata >= limit)
 				wordcount = i+1; /* terminate */
-			strcat(buff, word);
+			(void) strlcat(buff, word, sizeof (buff));
 			if (((i+1) & 7) == 0 || i == (wordcount-1)) {
-				sprintf(GETLINE, "%s", buff);
+				show_line(buff);
 				strcpy(buff, "");
 			}
 		}
 		return;
 	}
 
+	if (flags & F_DTAIL)
+		show_printf("WordCount = %d", wordcount);
+
+	outstr = xtra;
+	outsz = xsz;
 
 	valuetype = format[0];
 	while (valuetype != '\0') {
 		if (comdata >= limit)
 			break;
-		if ((flags & F_DTAIL) && valuetype != 'r' && valuetype != 'R')
-			outstr = GETLINE;
-		else
-			outstr = xtra + strlen(xtra);
 		label = format+1;
 		printit = (flags & F_DTAIL) || (valuetype <= 'Z');
 
 		switch (valuetype) {
 		case 'W':
 		case 'w':
-			prfmt = (flags & F_DTAIL) ? "%s = 0x%.4x" : "%s=0x%x ";
-			if (printit)
-				sprintf(outstr, prfmt, label, get2(comdata));
+			wval = get2(comdata);
 			comdata += 2;
+			if (!printit)
+				break;
+			if (flags & F_DTAIL)
+				show_printf(
+				    "%s = 0x%.4x", label, wval);
+			else {
+				tl = snprintf(outstr, outsz,
+				    " %s=0x%x", label, wval);
+				outstr += tl;
+				outsz -= tl;
+			}
 			break;
+
 		case 'D':
 		case 'd':
-			prfmt = (flags & F_DTAIL) ? "%s = %d" : "%s=%d ";
-			if (printit)
-				sprintf(outstr, prfmt, label, get2(comdata));
+			wval = get2(comdata);
 			comdata += 2;
+			if (!printit)
+				break;
+			if (flags & F_DTAIL)
+				show_printf(
+				    "%s = %d", label, wval);
+			else {
+				tl = snprintf(outstr, outsz,
+				    " %s=%d", label, wval);
+				outstr += tl;
+				outsz -= tl;
+			}
 			break;
+
 		case 'L':
 		case 'l':
-			prfmt = (flags & F_DTAIL) ? "%s = 0x%.8x" : "%s=0x%x ";
-			if (printit)
-				sprintf(outstr, prfmt, label, get4(comdata));
+			lval = get4(comdata);
 			comdata += 4;
+			if (!printit)
+				break;
+			if (flags & F_DTAIL)
+				show_printf(
+				    "%s = 0x%.8x", label, lval);
+			else {
+				tl = snprintf(outstr, outsz,
+				    " %s=0x%x", label, lval);
+				outstr += tl;
+				outsz -= tl;
+			}
 			break;
+
 		case 'B':
 		case 'b':
-			prfmt = (flags & F_DTAIL) ? "%s = 0x%.2x" : "%s=0x%x ";
-			if (printit)
-				sprintf(outstr, prfmt, label, comdata[0]);
+			bval = comdata[0];
 			comdata += 1;
+			if (!printit)
+				break;
+			if (flags & F_DTAIL)
+				show_printf(
+				    "%s = 0x%.2x", label, bval);
+			else {
+				tl = snprintf(outstr, outsz,
+				    " %s=0x%x", label, bval);
+				outstr += tl;
+				outsz -= tl;
+			}
 			break;
+
 		case 'r':
 			comdata++;
 			break;
+
 		case 'R':
 			comdata += 2;
 			break;
+
 		case 'U':
 		case 'u':
-			prfmt = (flags & F_DTAIL) ? "%s = %s" : "%s=%s ";
-			slength = unicode2ascii(tempstring, 256, comdata, 256);
-			if (printit)
-				sprintf(outstr, prfmt, label, tempstring);
-			comdata +=  (slength*2 + 1);
+			/* Unicode or ASCII string. */
+			GET_STRING(tempstr, comdata, isunicode);
+			if (!printit)
+				break;
+			if (flags & F_DTAIL)
+				show_printf(
+				    "%s = %s", label, tempstr);
+			else {
+				tl = snprintf(outstr, outsz,
+				    " %s=%s", label, tempstr);
+				outstr += tl;
+				outsz -= tl;
+			}
 			break;
+
 		case 'S':
 		case 's':
-			prfmt = (flags & F_DTAIL) ? "%s = %s" : "%s=%s ";
-			slength = snprintf(tempstring, sizeof (tempstring),
-			    "%s", (char *)comdata);
-			if (printit)
-				sprintf(outstr, prfmt, label, tempstring);
+			slength = strlcpy(tempstr, (char *)comdata,
+			    sizeof (tempstr));
 			comdata += (slength+1);
+			if (!printit)
+				break;
+			if (flags & F_DTAIL)
+				show_printf(
+				    "%s = %s", label, tempstr);
+			else {
+				tl = snprintf(outstr, outsz,
+				    " %s=%s", label, tempstr);
+				outstr += tl;
+				outsz -= tl;
+			}
 			break;
 		}
 		format += (strlen(format) + 1);