xref: /titanic_52/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_smb.c (revision ad8ef92ae01ac09e533731f5a517162c634308b4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 /*
29  * References used throughout this code:
30  *
31  * [CIFS/1.0] : A Common Internet File System (CIFS/1.0) Protocol
32  *		Internet Engineering Task Force (IETF) draft
33  *		Paul J. Leach, Microsoft, Dec. 1997
34  *
35  * [X/Open-SMB] : X/Open CAE Specification;
36  *		Protocols for X/Open PC Interworking: SMB, Version 2
37  *		X/Open Document Number: C209
38  */
39 
40 #include <fcntl.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 
46 #include "snoop.h"
47 
48 /*
49  * SMB Format (header)
50  * [X/Open-SMB, Sec. 5.1]
51  */
52 struct smb {
53 	uchar_t idf[4]; /*  identifier, contains 0xff, 'SMB'  */
54 	uchar_t com;    /*  command code  */
55 	uchar_t err[4]; /*  NT Status, or error class+code */
56 	uchar_t flags;
57 	uchar_t flags2[2];
58 	uchar_t re[12];
59 	uchar_t tid[2];
60 	uchar_t pid[2];
61 	uchar_t uid[2];
62 	uchar_t mid[2];
63 	/*
64 	 * immediately after the above 32 byte header:
65 	 *   unsigned char  WordCount;
66 	 *   unsigned short ParameterWords[ WordCount ];
67 	 *   unsigned short ByteCount;
68 	 *   unsigned char  ParameterBytes[ ByteCount ];
69 	 */
70 };
71 
72 /* smb flags */
73 #define	SERVER_RESPONSE		0x80
74 
75 /* smb flags2 */
76 #define	FLAGS2_EXT_SEC		0x0800	/* Extended security */
77 #define	FLAGS2_NT_STATUS	0x4000	/* NT status codes */
78 #define	FLAGS2_UNICODE		0x8000	/* String are Unicode */
79 
80 static void interpret_sesssetupX(int, uchar_t *, int, char *, int);
81 static void interpret_tconX(int, uchar_t *, int, char *, int);
82 static void interpret_trans(int, uchar_t *, int, char *, int);
83 static void interpret_trans2(int, uchar_t *, int, char *, int);
84 static void interpret_negprot(int, uchar_t *, int, char *, int);
85 static void interpret_default(int, uchar_t *, int, char *, int);
86 
87 /*
88  * Trans2 subcommand codes
89  * [X/Open-SMB, Sec. 16.1.7]
90  */
91 #define	TRANS2_OPEN 0x00
92 #define	TRANS2_FIND_FIRST 0x01
93 #define	TRANS2_FIND_NEXT2 0x02
94 #define	TRANS2_QUERY_FS_INFORMATION 0x03
95 #define	TRANS2_QUERY_PATH_INFORMATION 0x05
96 #define	TRANS2_SET_PATH_INFORMATION 0x06
97 #define	TRANS2_QUERY_FILE_INFORMATION 0x07
98 #define	TRANS2_SET_FILE_INFORMATION 0x08
99 #define	TRANS2_CREATE_DIRECTORY 0x0D
100 
101 
102 struct decode {
103 	char *name;
104 	void (*func)(int, uchar_t *, int, char *, int);
105 	char *callfmt;
106 	char *replyfmt;
107 };
108 
109 /*
110  * SMB command codes (function names)
111  * [X/Open-SMB, Sec. 5.2]
112  */
113 static struct decode SMBtable[256] = {
114 	/* 0x00 */
115 	{ "mkdir", 0, 0, 0 },
116 	{ "rmdir", 0, 0, 0 },
117 	{ "open", 0, 0, 0 },
118 	{ "create", 0, 0, 0 },
119 
120 	{
121 		"close", 0,
122 		/* [X/Open-SMB, Sec. 7.10] */
123 		"WFileID\0"
124 		"lLastModTime\0"
125 		"dByteCount\0\0",
126 		"dByteCount\0\0"
127 	},
128 
129 	{ "flush", 0, 0, 0 },
130 	{ "unlink", 0, 0, 0 },
131 
132 	{
133 		"move", 0,
134 		/* [X/Open-SMB, Sec. 7.11] */
135 		"wFileAttributes\0"
136 		"dByteCount\0r\0"
137 		"UFileName\0r\0"
138 		"UNewPath\0\0",
139 		"dByteCount\0\0"
140 	},
141 
142 	{
143 		"getatr", 0,
144 		/* [X/Open-SMB, Sec. 8.4] */
145 		"dBytecount\0r\0"
146 		"UFileName\0\0",
147 		"wFileAttributes\0"
148 		"lTime\0"
149 		"lSize\0"
150 		"R\0R\0R\0R\0R\0"
151 		"dByteCount\0\0"
152 	},
153 
154 	{ "setatr", 0, 0, 0 },
155 
156 	{
157 		"read", 0,
158 		/* [X/Open-SMB, Sec. 7.4] */
159 		"WFileID\0"
160 		"wI/0 Bytes\0"
161 		"LFileOffset\0"
162 		"WBytesLeft\0"
163 		"dByteCount\0\0",
164 		"WDataLength\0"
165 		"R\0R\0R\0R\0"
166 		"dByteCount\0\0"
167 	},
168 
169 	{
170 		"write", 0,
171 		/* [X/Open-SMB, Sec. 7.5] */
172 		"WFileID\0"
173 		"wI/0 Bytes\0"
174 		"LFileOffset\0"
175 		"WBytesLeft\0"
176 		"dByteCount\0\0",
177 		"WDataLength\0"
178 		"dByteCount\0\0"
179 	},
180 
181 	{ "lock", 0, 0, 0 },
182 	{ "unlock", 0, 0, 0 },
183 	{ "ctemp", 0, 0, 0 },
184 	{ "mknew", 0, 0, 0 },
185 
186 	/* 0x10 */
187 	{
188 		"chkpth", 0,
189 		/* [X/Open-SMB, Sec. 8.7] */
190 		"dByteCount\0r\0"
191 		"UFile\0\0",
192 		"dByteCount\0\0"
193 	},
194 
195 	{ "exit", 0, 0, 0 },
196 	{ "lseek", 0, 0, 0 },
197 	{ "lockread", 0, 0, 0 },
198 	{ "writeunlock", 0, 0, 0 },
199 	{ 0, 0, 0, 0 },
200 	{ 0, 0, 0, 0 },
201 	{ 0, 0, 0, 0 },
202 	{ 0, 0, 0, 0 },
203 	{ 0, 0, 0, 0 },
204 
205 	{
206 		"readbraw", 0,
207 		/* [X/Open-SMB, Sec. 10.1] */
208 		"WFileID\0"
209 		"LFileOffset\0"
210 		"wMaxCount\0"
211 		"wMinCount\0"
212 		"lTimeout\0R\0"
213 		"dByteCount\0\0", 0
214 	},
215 
216 	{ "readbmpx", 0, 0, 0 },
217 	{ "readbs", 0, 0, 0 },
218 	{ "writebraw", 0, 0, 0 },
219 	{ "writebmpx", 0, 0, 0 },
220 	{ "writebs", 0, 0, 0 },
221 
222 	/* 0x20 */
223 	{ "writec", 0, 0, 0 },
224 	{ "qrysrv", 0, 0, 0 },
225 	{ "setattrE", 0, 0, 0 },
226 	{ "getattrE", 0, 0, 0 },
227 
228 	{
229 		"lockingX", 0,
230 		/* [X/Open-SMB, Sec. 12.2] */
231 		"wChainedCommand\0"
232 		"wNextOffset\0"
233 		"WFileID\0"
234 		"wLockType\0"
235 		"lOpenTimeout\0"
236 		"W#Unlocks\0"
237 		"W#Locks\0"
238 		"dByteCount\0\0", 0
239 	},
240 
241 	{ "trans", interpret_trans, 0, 0 },
242 	{ "transs", 0, 0, 0 },
243 	{ "ioctl", 0, 0, 0 },
244 	{ "ioctls", 0, 0, 0 },
245 	{ "copy", 0, 0, 0 },
246 	{ "move", 0, 0, 0 },
247 	{ "echo", 0, 0, 0 },
248 	{ "writeclose", 0, 0, 0 },
249 
250 	{
251 		/* [X/Open-SMB, Sec. 12.1] */
252 		"openX", 0,
253 		/* call */
254 		"wChainedCommand\0"
255 		"wNextOffset\0"
256 		"wFlags\0"
257 		"wMode\0"
258 		"wSearchAttributes\0"
259 		"wFileAttributes\0"
260 		"lTime\0"
261 		"wOpenFunction\0"
262 		"lFileSize\0"
263 		"lOpenTimeout\0R\0R\0"
264 		"dByteCount\0r\0"
265 		"UFileName\0\0",
266 		/* reply */
267 		"wChainedCommand\0"
268 		"wNextOffset\0"
269 		"WFileID\0"
270 		"wAttributes\0"
271 		"lTime\0"
272 		"LSize\0"
273 		"wOpenMode\0"
274 		"wFileType\0"
275 		"wDeviceState\0"
276 		"wActionTaken\0"
277 		"lUniqueFileID\0R\0"
278 		"wBytecount\0\0"
279 	},
280 
281 	{
282 		/* [CIFS 4.2.4] */
283 		"readX", 0,
284 		/* call */
285 		"wChainedCommand\0"
286 		"wNextOffset\0"
287 		"WFileID\0"
288 		"LOffset\0"
289 		"DMaxCount\0"
290 		"dMinCount\0"
291 		"dMaxCountHigh\0"
292 		"R\0"
293 		"wRemaining\0"
294 		"lOffsetHigh\0"
295 		"dByteCount\0\0",
296 		/* reply */
297 		"wChainedCommand\0"
298 		"wNextOffset\0"
299 		"dRemaining\0R\0R\0"
300 		"DCount\0"
301 		"dDataOffset\0"
302 		"dCountHigh\0"
303 		"R\0R\0R\0R\0"
304 		"dByteCount\0\0"
305 	},
306 
307 	{
308 		/* [CIFS 4.2.5] */
309 		"writeX", 0,
310 		/* call */
311 		"wChainedCommand\0"
312 		"wNextOffset\0"
313 		"WFileID\0"
314 		"LOffset\0R\0R\0"
315 		"wWriteMode\0"
316 		"wRemaining\0"
317 		"dDataLenHigh\0"
318 		"DDataLen\0"
319 		"dDataOffset\0"
320 		"lOffsetHigh\0\0",
321 		/* reply */
322 		"wChainedCommand\0"
323 		"wNextOffset\0"
324 		"DCount\0"
325 		"wRemaining\0"
326 		"wCountHigh\0\0"
327 	},
328 
329 	/* 0x30 */
330 	{ 0, 0, 0, 0 },
331 	{ "closeTD", 0, 0, 0 },
332 	{ "trans2", interpret_trans2, 0, 0 },
333 	{ "trans2s", 0, 0, 0 },
334 	{
335 		"findclose", 0,
336 		/* [X/Open-SMB, Sec. 15.4 ] */
337 		"WFileID\0"
338 		"dByteCount\0\0",
339 		"dByteCount\0\0"
340 	},
341 	{ 0, 0, 0, 0 },
342 	{ 0, 0, 0, 0 },
343 	{ 0, 0, 0, 0 },
344 	{ 0, 0, 0, 0 },
345 	{ 0, 0, 0, 0 },
346 	{ 0, 0, 0, 0 },
347 	{ 0, 0, 0, 0 },
348 	{ 0, 0, 0, 0 },
349 	{ 0, 0, 0, 0 },
350 	{ 0, 0, 0, 0 },
351 	{ 0, 0, 0, 0 },
352 
353 	/* 0x40 */
354 	{ 0, 0, 0, 0 },
355 	{ 0, 0, 0, 0 },
356 	{ 0, 0, 0, 0 },
357 	{ 0, 0, 0, 0 },
358 	{ 0, 0, 0, 0 },
359 	{ 0, 0, 0, 0 },
360 	{ 0, 0, 0, 0 },
361 	{ 0, 0, 0, 0 },
362 	{ 0, 0, 0, 0 },
363 	{ 0, 0, 0, 0 },
364 	{ 0, 0, 0, 0 },
365 	{ 0, 0, 0, 0 },
366 	{ 0, 0, 0, 0 },
367 	{ 0, 0, 0, 0 },
368 	{ 0, 0, 0, 0 },
369 	{ 0, 0, 0, 0 },
370 
371 	/* 0x50 */
372 	{ 0, 0, 0, 0 },
373 	{ 0, 0, 0, 0 },
374 	{ 0, 0, 0, 0 },
375 	{ 0, 0, 0, 0 },
376 	{ 0, 0, 0, 0 },
377 	{ 0, 0, 0, 0 },
378 	{ 0, 0, 0, 0 },
379 	{ 0, 0, 0, 0 },
380 	{ 0, 0, 0, 0 },
381 	{ 0, 0, 0, 0 },
382 	{ 0, 0, 0, 0 },
383 	{ 0, 0, 0, 0 },
384 	{ 0, 0, 0, 0 },
385 	{ 0, 0, 0, 0 },
386 	{ 0, 0, 0, 0 },
387 	{ 0, 0, 0, 0 },
388 
389 	/* 0x60 */
390 	{ 0, 0, 0, 0 },
391 	{ 0, 0, 0, 0 },
392 	{ 0, 0, 0, 0 },
393 	{ 0, 0, 0, 0 },
394 	{ 0, 0, 0, 0 },
395 	{ 0, 0, 0, 0 },
396 	{ 0, 0, 0, 0 },
397 	{ 0, 0, 0, 0 },
398 	{ 0, 0, 0, 0 },
399 	{ 0, 0, 0, 0 },
400 	{ 0, 0, 0, 0 },
401 	{ 0, 0, 0, 0 },
402 	{ 0, 0, 0, 0 },
403 	{ 0, 0, 0, 0 },
404 	{ 0, 0, 0, 0 },
405 	{ 0, 0, 0, 0 },
406 
407 	/* 0x70 */
408 	{ "tcon", 0, 0, 0 },
409 	{
410 		"tdis", 0,
411 		/* [X/Open-SMB, Sec. 6.3] */
412 		"dByteCount\0\0",
413 		"dByteCount\0\0"
414 	},
415 	{ "negprot", interpret_negprot, 0, 0 },
416 	{ "sesssetupX", interpret_sesssetupX, 0, 0 },
417 	{
418 		"uloggoffX", 0,
419 		/* [X/Open-SMB, Sec. 15.5] */
420 		"wChainedCommand\0"
421 		"wNextOffset\0\0",
422 		"wChainedCommnad\0"
423 		"wNextOffset\0\0" },
424 	{ "tconX", interpret_tconX, 0, 0 },
425 	{ 0, 0, 0, 0 },
426 	{ 0, 0, 0, 0 },
427 	{ 0, 0, 0, 0 },
428 	{ 0, 0, 0, 0 },
429 	{ 0, 0, 0, 0 },
430 	{ 0, 0, 0, 0 },
431 	{ 0, 0, 0, 0 },
432 	{ 0, 0, 0, 0 },
433 	{ 0, 0, 0, 0 },
434 	{ 0, 0, 0, 0 },
435 
436 	/* 0x80 */
437 	{ "dskattr", 0, 0, 0 },
438 	{ "search", 0, 0, 0 },
439 	{ "ffirst", 0, 0, 0 },
440 	{ "funique", 0, 0, 0 },
441 	{ "fclose", 0, 0, 0 },
442 	{ 0, 0, 0, 0 },
443 	{ 0, 0, 0, 0 },
444 	{ 0, 0, 0, 0 },
445 	{ 0, 0, 0, 0 },
446 	{ 0, 0, 0, 0 },
447 	{ 0, 0, 0, 0 },
448 	{ 0, 0, 0, 0 },
449 	{ 0, 0, 0, 0 },
450 	{ 0, 0, 0, 0 },
451 	{ 0, 0, 0, 0 },
452 	{ 0, 0, 0, 0 },
453 
454 	/* 0x90 */
455 	{ 0, 0, 0, 0 },
456 	{ 0, 0, 0, 0 },
457 	{ 0, 0, 0, 0 },
458 	{ 0, 0, 0, 0 },
459 	{ 0, 0, 0, 0 },
460 	{ 0, 0, 0, 0 },
461 	{ 0, 0, 0, 0 },
462 	{ 0, 0, 0, 0 },
463 	{ 0, 0, 0, 0 },
464 	{ 0, 0, 0, 0 },
465 	{ 0, 0, 0, 0 },
466 	{ 0, 0, 0, 0 },
467 	{ 0, 0, 0, 0 },
468 	{ 0, 0, 0, 0 },
469 	{ 0, 0, 0, 0 },
470 	{ 0, 0, 0, 0 },
471 
472 	/* 0xa0 */
473 	/*
474 	 * Command codes 0xa0 to 0xa7 are from
475 	 * [CIFS/1.0, Sec. 5.1]
476 	 */
477 	{ "_NT_Trans", 0, 0, 0 },
478 	{ "_NT_Trans2", 0, 0, 0 },
479 	{
480 		/* [CIFS/1.0, Sec. 4.2.1] */
481 		"_NT_CreateX", 0,
482 		/* Call */
483 		"wChainedCommand\0"
484 		"wNextOffset\0r\0"
485 		"dNameLength\0"
486 		"lCreateFlags\0"
487 		"lRootDirFID\0"
488 		"lDesiredAccess\0"
489 		"lAllocSizeLow\0"
490 		"lAllocSizeHigh\0"
491 		"lNTFileAttributes\0"
492 		"lShareAccess\0"
493 		"lOpenDisposition\0"
494 		"lCreateOption\0"
495 		"lImpersonationLevel\0"
496 		"bSecurityFlags\0"
497 		"dByteCount\0r\0"
498 		"UFileName\0\0",
499 		/* Reply */
500 		"wChainedCommand\0"
501 		"wNextOffset\0"
502 		"bOplockLevel\0"
503 		"WFileID\0"
504 		"lCreateAction\0\0"
505 	},
506 	{ 0, 0, 0, 0 },
507 	{
508 		"_NT_Cancel", 0,
509 		/* [CIFS/1.0, Sec. 4.1.8] */
510 		"dByteCount\0", 0
511 	},
512 	{ 0, 0, 0, 0 },
513 	{ 0, 0, 0, 0 },
514 	{ 0, 0, 0, 0 },
515 	{ 0, 0, 0, 0 },
516 	{ 0, 0, 0, 0 },
517 	{ 0, 0, 0, 0 },
518 	{ 0, 0, 0, 0 },
519 	{ 0, 0, 0, 0 },
520 	{ 0, 0, 0, 0 },
521 	{ 0, 0, 0, 0 },
522 	{ 0, 0, 0, 0 },
523 
524 	/* 0xb0 */
525 	{ 0, 0, 0, 0 },
526 	{ 0, 0, 0, 0 },
527 	{ 0, 0, 0, 0 },
528 	{ 0, 0, 0, 0 },
529 	{ 0, 0, 0, 0 },
530 	{ 0, 0, 0, 0 },
531 	{ 0, 0, 0, 0 },
532 	{ 0, 0, 0, 0 },
533 	{ 0, 0, 0, 0 },
534 	{ 0, 0, 0, 0 },
535 	{ 0, 0, 0, 0 },
536 	{ 0, 0, 0, 0 },
537 	{ 0, 0, 0, 0 },
538 	{ 0, 0, 0, 0 },
539 	{ 0, 0, 0, 0 },
540 	{ 0, 0, 0, 0 },
541 
542 	/* 0xc0 */
543 	{ "splopen", 0, 0, 0 },
544 	{ "splwr", 0, 0, 0 },
545 	{ "splclose", 0, 0, 0 },
546 	{ "splretq", 0, 0, 0 },
547 	{ 0, 0, 0, 0 },
548 	{ 0, 0, 0, 0 },
549 	{ 0, 0, 0, 0 },
550 	{ 0, 0, 0, 0 },
551 	{ 0, 0, 0, 0 },
552 	{ 0, 0, 0, 0 },
553 	{ 0, 0, 0, 0 },
554 	{ 0, 0, 0, 0 },
555 	{ 0, 0, 0, 0 },
556 	{ 0, 0, 0, 0 },
557 	{ 0, 0, 0, 0 },
558 	{ 0, 0, 0, 0 },
559 
560 	/* 0xd0 */
561 	{ "sends", 0, 0, 0 },
562 	{ "sendb", 0, 0, 0 },
563 	{ "fwdname", 0, 0, 0 },
564 	{ "cancelf", 0, 0, 0 },
565 	{ "getmac", 0, 0, 0 },
566 	{ "sendstrt", 0, 0, 0 },
567 	{ "sendend", 0, 0, 0 },
568 	{ "sendtxt", 0, 0, 0 },
569 	{ 0, 0, 0, 0 },
570 	{ 0, 0, 0, 0 },
571 	{ 0, 0, 0, 0 },
572 	{ 0, 0, 0, 0 },
573 	{ 0, 0, 0, 0 },
574 	{ 0, 0, 0, 0 },
575 	{ 0, 0, 0, 0 },
576 	{ 0, 0, 0, 0 },
577 
578 	/* 0xe0 */
579 	{ 0, 0, 0, 0 },
580 	{ 0, 0, 0, 0 },
581 	{ 0, 0, 0, 0 },
582 	{ 0, 0, 0, 0 },
583 	{ 0, 0, 0, 0 },
584 	{ 0, 0, 0, 0 },
585 	{ 0, 0, 0, 0 },
586 	{ 0, 0, 0, 0 },
587 	{ 0, 0, 0, 0 },
588 	{ 0, 0, 0, 0 },
589 	{ 0, 0, 0, 0 },
590 	{ 0, 0, 0, 0 },
591 	{ 0, 0, 0, 0 },
592 	{ 0, 0, 0, 0 },
593 	{ 0, 0, 0, 0 },
594 	{ 0, 0, 0, 0 },
595 
596 	/* 0xf0 */
597 	{ 0, 0, 0, 0 },
598 	{ 0, 0, 0, 0 },
599 	{ 0, 0, 0, 0 },
600 	{ 0, 0, 0, 0 },
601 	{ 0, 0, 0, 0 },
602 	{ 0, 0, 0, 0 },
603 	{ 0, 0, 0, 0 },
604 	{ 0, 0, 0, 0 },
605 	{ 0, 0, 0, 0 },
606 	{ 0, 0, 0, 0 },
607 	{ 0, 0, 0, 0 },
608 	{ 0, 0, 0, 0 },
609 	{ 0, 0, 0, 0 },
610 	{ 0, 0, 0, 0 },
611 	{ 0, 0, 0, 0 },
612 	{ 0, 0, 0, 0 }
613 };
614 
615 /* Helpers to get values in Intel order (often mis-aligned). */
616 static uint16_t
617 get2(uchar_t *p) {
618 	return (p[0] + (p[1]<<8));
619 }
620 static uint32_t
621 get4(uchar_t *p) {
622 	return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
623 }
624 static uint64_t
625 get8(uchar_t *p) {
626 	return (get4(p) | ((uint64_t)get4(p+4) << 32));
627 }
628 
629 /*
630  * Support displaying NT times.
631  * Number of seconds between 1970 and 1601 year
632  * (134774 days)
633  */
634 static const uint64_t DIFF1970TO1601 = 11644473600ULL;
635 static const uint32_t TEN_MIL = 10000000UL;
636 static char *
637 format_nttime(uint64_t nt_time)
638 {
639 	uint64_t nt_sec;	/* seconds */
640 	uint64_t nt_tus;	/* tenths of uSec. */
641 	uint32_t ux_nsec;
642 	int64_t ux_sec;
643 
644 	/* Optimize time zero. */
645 	if (nt_time == 0) {
646 		ux_sec = 0;
647 		ux_nsec = 0;
648 		goto out;
649 	}
650 
651 	nt_sec = nt_time / TEN_MIL;
652 	nt_tus = nt_time % TEN_MIL;
653 
654 	if (nt_sec <= DIFF1970TO1601) {
655 		ux_sec = 0;
656 		ux_nsec = 0;
657 		goto out;
658 	}
659 	ux_sec = nt_sec - DIFF1970TO1601;
660 	ux_nsec = nt_tus * 100;
661 
662 out:
663 	return (format_time(ux_sec, ux_nsec));
664 }
665 
666 /*
667  * This is called by snoop_netbios.c.
668  * This is the external entry point.
669  */
670 void
671 interpret_smb(int flags, uchar_t *data, int len)
672 {
673 	struct smb *smb;
674 	struct decode *decoder;
675 	char xtra[MAXLINE];
676 	ushort_t smb_flags2;
677 	void (*func)(int, uchar_t *, int, char *, int);
678 
679 	if (len < sizeof (struct smb))
680 		return;
681 
682 	smb = (struct smb *)data;
683 	decoder = &SMBtable[smb->com & 255];
684 	smb_flags2 = get2(smb->flags2);
685 	xtra[0] = '\0';
686 
687 	/*
688 	 * SMB Header description
689 	 * [X/Open-SMB, Sec. 5.1]
690 	 */
691 	if (flags & F_DTAIL) {
692 		show_header("SMB:  ", "SMB Header", len);
693 		show_space();
694 
695 		if (smb->flags & SERVER_RESPONSE)
696 			show_line("SERVER RESPONSE");
697 		else
698 			show_line("CLIENT REQUEST");
699 
700 		if (decoder->name)
701 			show_printf("Command code = 0x%x (SMB%s)",
702 			    smb->com, decoder->name);
703 		else
704 			show_printf("Command code = 0x%x", smb->com);
705 
706 		/*
707 		 * NT status or error class/code
708 		 * [X/Open-SMB, Sec. 5.6]
709 		 */
710 		if (smb_flags2 & FLAGS2_NT_STATUS) {
711 			show_printf("NT Status = %x", get4(smb->err));
712 		} else {
713 			/* Error classes [X/Open-SMB, Sec. 5.6] */
714 			show_printf("Error class/code = %d/%d",
715 			    smb->err[0], get2(&smb->err[2]));
716 		}
717 
718 		show_printf("Flags summary = 0x%.2x", smb->flags);
719 		show_printf("Flags2 summary = 0x%.4x", smb_flags2);
720 		show_printf("Tree ID  (TID) = 0x%.4x", get2(smb->tid));
721 		show_printf("Proc. ID (PID) = 0x%.4x", get2(smb->pid));
722 		show_printf("User ID  (UID) = 0x%.4x", get2(smb->uid));
723 		show_printf("Mux. ID  (MID) = 0x%.4x", get2(smb->mid));
724 		show_space();
725 	}
726 
727 	if ((func = decoder->func) == NULL)
728 		func = interpret_default;
729 	(*func)(flags, (uchar_t *)data, len, xtra, sizeof (xtra));
730 
731 	if (flags & F_SUM) {
732 		char *p;
733 		int sz, tl;
734 
735 		/* Will advance p and decr. sz */
736 		p = get_sum_line();
737 		sz = MAXLINE;
738 
739 		/* Call or Reply */
740 		if (smb->flags & SERVER_RESPONSE)
741 			tl = snprintf(p, sz, "SMB R");
742 		else
743 			tl = snprintf(p, sz, "SMB C");
744 		p += tl;
745 		sz -= tl;
746 
747 		/* The name, if known, else the cmd code */
748 		if (decoder->name) {
749 			tl = snprintf(p, sz, " Cmd=SMB%s", decoder->name);
750 		} else {
751 			tl = snprintf(p, sz, " Cmd=0x%02X", smb->com);
752 		}
753 		p += tl;
754 		sz -= tl;
755 
756 		/*
757 		 * The "extra" (cmd-specific summary).
758 		 * If non-null, has leading blank.
759 		 */
760 		if (xtra[0] != '\0') {
761 			tl = snprintf(p, sz, "%s", xtra);
762 			p += tl;
763 			sz -= tl;
764 		}
765 
766 		/*
767 		 * NT status or error class/code
768 		 * [X/Open-SMB, Sec. 5.6]
769 		 *
770 		 * Only show for response, not call.
771 		 */
772 		if (smb->flags & SERVER_RESPONSE) {
773 			if (smb_flags2 & FLAGS2_NT_STATUS) {
774 				uint_t status = get4(smb->err);
775 				snprintf(p, sz, " Status=0x%x", status);
776 			} else {
777 				uchar_t errcl = smb->err[0];
778 				ushort_t code = get2(&smb->err[2]);
779 				snprintf(p, sz, " Error=%d/%d", errcl, code);
780 			}
781 		}
782 	}
783 
784 	if (flags & F_DTAIL)
785 		show_trailer();
786 }
787 
788 static void
789 output_bytes(uchar_t *data, int bytecount)
790 {
791 	int i;
792 	char buff[80];
793 	char word[10];
794 
795 	(void) strlcpy(buff, "  ", sizeof (buff));
796 	for (i = 0; i < bytecount; i++) {
797 		snprintf(word, sizeof (word), "%.2x ", data[i]);
798 		(void) strlcat(buff, word, sizeof (buff));
799 		if ((i+1)%16 == 0 || i == (bytecount-1)) {
800 			show_line(buff);
801 			(void) strlcpy(buff, "  ", sizeof (buff));
802 		}
803 	}
804 }
805 
806 /*
807  * Based on the Unicode Standard,  http://www.unicode.org/
808  * "The Unicode Standard: A Technical Introduction", June 1998
809  */
810 static int
811 unicode2ascii(char *outstr, int outlen, uchar_t *instr, int inlen)
812 {
813 	int i = 0, j = 0;
814 	char c;
815 
816 	while (i < inlen && j < (outlen-1)) {
817 		/* Show unicode chars >= 256 as '?' */
818 		if (instr[i+1])
819 			c = '?';
820 		else
821 			c = instr[i];
822 		if (c == '\0')
823 			break;
824 		outstr[j] = c;
825 		i += 2;
826 		j++;
827 	}
828 	outstr[j] = '\0';
829 	return (j);
830 }
831 
832 /*
833  * Convenience macro to copy a string from the data,
834  * either in UCS-2 or ASCII as indicated by UCS.
835  * OBUF must be an array type (see sizeof) and
836  * DP must be an L-value (this increments it).
837  */
838 #define	GET_STRING(OBUF, DP, UCS)				\
839 {								\
840 	int _len, _sz = sizeof (OBUF);				\
841 	if (UCS) {						\
842 		if (((uintptr_t)DP) & 1)			\
843 			DP++;					\
844 		_len = unicode2ascii(OBUF, _sz, DP, 2 * _sz);	\
845 		DP += 2 * (_len + 1);				\
846 	} else {						\
847 		_len = strlcpy(OBUF, (char *)DP, _sz);		\
848 		DP += (_len + 1);				\
849 	}							\
850 }
851 
852 /*
853  * TRANS2 information levels
854  * [X/Open-SMB, Sec. 16.1.6]
855  */
856 static void
857 get_info_level(char *outstr, int outsz, int value)
858 {
859 
860 	switch (value) {
861 	case 1:
862 		snprintf(outstr, outsz, "Standard");
863 		break;
864 	case 2:
865 		snprintf(outstr, outsz, "Query EA Size");
866 		break;
867 	case 3:
868 		snprintf(outstr, outsz, "Query EAS from List");
869 		break;
870 	case 0x101:
871 		snprintf(outstr, outsz, "Directory Info");
872 		break;
873 	case 0x102:
874 		snprintf(outstr, outsz, "Full Directory Info");
875 		break;
876 	case 0x103:
877 		snprintf(outstr, outsz, "Names Info");
878 		break;
879 	case 0x104:
880 		snprintf(outstr, outsz, "Both Directory Info");
881 		break;
882 	default:
883 		snprintf(outstr, outsz, "Unknown");
884 		break;
885 	}
886 }
887 
888 /*
889  * Interpret TRANS2_QUERY_PATH subcommand
890  * [X/Open-SMB, Sec. 16.7]
891  */
892 /* ARGSUSED */
893 static void
894 output_trans2_querypath(int flags, uchar_t *data, char *xtra, int xsz)
895 {
896 	int length;
897 	char filename[256];
898 
899 	if (flags & F_SUM) {
900 		length = snprintf(xtra, xsz, " QueryPathInfo");
901 		xtra += length;
902 		xsz -= length;
903 		data += 6;
904 		(void) unicode2ascii(filename, 256, data, 512);
905 		snprintf(xtra, xsz, " File=%s", filename);
906 	}
907 
908 	if (flags & F_DTAIL) {
909 		show_line("FunctionName = QueryPathInfo");
910 		show_printf("InfoLevel = 0x%.4x", get2(data));
911 		data += 6;
912 		(void) unicode2ascii(filename, 256, data, 512);
913 		show_printf("FileName = %s", filename);
914 	}
915 }
916 
917 /*
918  * Interpret TRANS2_QUERY_FILE subcommand
919  * [X/Open-SMB, Sec. 16.9]
920  */
921 /* ARGSUSED */
922 static void
923 output_trans2_queryfile(int flags, uchar_t *data, char *xtra, int xsz)
924 {
925 	int length;
926 
927 	if (flags & F_SUM) {
928 		length = snprintf(xtra, xsz, " QueryFileInfo");
929 		xtra += length;
930 		xsz -= length;
931 		snprintf(xtra, xsz, " FileID=0x%x", get2(data));
932 	}
933 
934 	if (flags & F_DTAIL) {
935 		show_line("FunctionName = QueryFileInfo");
936 		show_printf("FileID = 0x%.4x", get2(data));
937 		data += 2;
938 		show_printf("InfoLevel = 0x%.4x", get2(data));
939 	}
940 }
941 
942 /*
943  * Interpret TRANS2_SET_FILE subcommand
944  * [X/Open-SMB, Sec. 16.10]
945  */
946 /* ARGSUSED */
947 static void
948 output_trans2_setfile(int flags, uchar_t *data, char *xtra, int xsz)
949 {
950 	int length;
951 
952 	if (flags & F_SUM) {
953 		length = snprintf(xtra, xsz, " SetFileInfo");
954 		xtra += length;
955 		xsz -= length;
956 		snprintf(xtra, xsz, " FileID=0x%x", get2(data));
957 	}
958 
959 	if (flags & F_DTAIL) {
960 		show_line("FunctionName = SetFileInfo");
961 		show_printf("FileID = 0x%.4x", get2(data));
962 		data += 2;
963 		show_printf("InfoLevel = 0x%.4x", get2(data));
964 	}
965 }
966 
967 /*
968  * Interpret TRANS2_FIND_FIRST subcommand
969  * [X/Open-SMB, Sec. 16.3]
970  */
971 /* ARGSUSED */
972 static void
973 output_trans2_findfirst(int flags, uchar_t *data, char *xtra, int xsz)
974 {
975 	int length;
976 	char filename[256];
977 	char infolevel[100];
978 
979 	if (flags & F_SUM) {
980 		length = snprintf(xtra, xsz, " Findfirst");
981 		xtra += length;
982 		xsz -= length;
983 		data += 12;
984 		(void) unicode2ascii(filename, 256, data, 512);
985 		snprintf(xtra, xsz, " File=%s", filename);
986 	}
987 
988 	if (flags & F_DTAIL) {
989 		show_line("FunctionName = Findfirst");
990 		show_printf("SearchAttributes = 0x%.4x", get2(data));
991 		data += 2;
992 		show_printf("FindCount = 0x%.4x", get2(data));
993 		data += 2;
994 		show_printf("FindFlags = 0x%.4x", get2(data));
995 		data += 2;
996 		get_info_level(infolevel, sizeof (infolevel), get2(data));
997 		show_printf("InfoLevel = %s", infolevel);
998 		data += 6;
999 		(void) unicode2ascii(filename, 256, data, 512);
1000 		show_printf("FileName = %s", filename);
1001 	}
1002 }
1003 
1004 
1005 /*
1006  * Interpret TRANS2_FIND_NEXT subcommand
1007  * [X/Open-SMB, Sec. 16.4]
1008  */
1009 /* ARGSUSED */
1010 static void
1011 output_trans2_findnext(int flags, uchar_t *data, char *xtra, int xsz)
1012 {
1013 	int length;
1014 	char filename[256];
1015 	char infolevel[100];
1016 
1017 	if (flags & F_SUM) {
1018 		length = snprintf(xtra, xsz, " Findnext");
1019 		xtra += length;
1020 		xsz -= length;
1021 		data += 12;
1022 		(void) unicode2ascii(filename, 256, data, 512);
1023 		snprintf(xtra, xsz, " File=%s", filename);
1024 	}
1025 
1026 	if (flags & F_DTAIL) {
1027 		show_line("FunctionName = Findnext");
1028 		show_printf("FileID = 0x%.4x", get2(data));
1029 		data += 2;
1030 		show_printf("FindCount = 0x%.4x", get2(data));
1031 		data += 2;
1032 		get_info_level(infolevel, sizeof (infolevel), get2(data));
1033 		show_printf("InfoLevel = %s", infolevel);
1034 		data += 2;
1035 		show_printf("FindKey = 0x%.8x", get4(data));
1036 		data += 4;
1037 		show_printf("FindFlags = 0x%.4x", get2(data));
1038 		data += 2;
1039 		(void) unicode2ascii(filename, 256, data, 512);
1040 		show_printf("FileName = %s", filename);
1041 	}
1042 }
1043 
1044 /*
1045  * Interpret a "Negprot" SMB
1046  * [X/Open-SMB, Sec. 6.1]
1047  */
1048 /* ARGSUSED */
1049 static void
1050 interpret_negprot(int flags, uchar_t *data, int len, char *xtra, int xsz)
1051 {
1052 	int i, last, length;
1053 	int bytecount;
1054 	int key_len;
1055 	int wordcount;
1056 	char tbuf[256];
1057 	struct smb *smbdata;
1058 	uchar_t *protodata;
1059 	uchar_t *byte0;
1060 	uint64_t nttime;
1061 	uint32_t caps;
1062 	ushort_t smb_flags2;
1063 
1064 	smbdata  = (struct smb *)data;
1065 	smb_flags2 = get2(smbdata->flags2);
1066 	protodata = (uchar_t *)data + sizeof (struct smb);
1067 	wordcount = *protodata++;
1068 
1069 	if ((smbdata->flags & SERVER_RESPONSE) == 0) {
1070 		/*
1071 		 * request packet:
1072 		 * short bytecount;
1073 		 * struct { char fmt; char name[]; } dialects
1074 		 */
1075 		bytecount = get2(protodata);
1076 		protodata += 2;
1077 		byte0 = protodata;
1078 
1079 		if (flags & F_DTAIL)
1080 			show_printf("ByteCount = %d", bytecount);
1081 		if (bytecount > len)
1082 			bytecount = len;
1083 
1084 		/* Walk the list of dialects. */
1085 		i = last = 0;
1086 		tbuf[0] = '\0';
1087 		while (protodata < (byte0 + bytecount - 2)) {
1088 			if (*protodata++ != 2)	/* format code */
1089 				break;
1090 			length = strlcpy(tbuf, (char *)protodata,
1091 			    sizeof (tbuf));
1092 			protodata += (length + 1);
1093 			if (flags & F_DTAIL) {
1094 				show_printf("Dialect[%d] = %s",
1095 				    i, tbuf);
1096 			}
1097 			last = i++;
1098 		}
1099 		if (flags & F_SUM) {
1100 			/*
1101 			 * Just print the last dialect, which is
1102 			 * normally the interesting one.
1103 			 */
1104 			snprintf(xtra, xsz, " Dialect[%d]=%s", last, tbuf);
1105 		}
1106 	} else {
1107 		/* Parse reply */
1108 		if (flags & F_SUM) {
1109 			snprintf(xtra, xsz, " Dialect#=%d", protodata[0]);
1110 		}
1111 		if ((flags & F_DTAIL) == 0)
1112 			return;
1113 		if (wordcount < 13)
1114 			return;
1115 		show_printf("WordCount = %d", wordcount);
1116 		show_printf("Dialect Index = %d", protodata[0]);
1117 		protodata += 2;
1118 		show_printf("Security Mode = 0x%x", protodata[0]);
1119 		protodata++;
1120 		show_printf("MaxMPXRequests = %d", get2(protodata));
1121 		protodata += 2;
1122 		show_printf("MaxVCs = %d", get2(protodata));
1123 		protodata += 2;
1124 		show_printf("MaxBufferSize = %d", get4(protodata));
1125 		protodata += 4;
1126 		show_printf("MaxRawBuffer = %d", get4(protodata));
1127 		protodata += 4;
1128 		show_printf("SessionKey = 0x%.8x", get4(protodata));
1129 		protodata += 4;
1130 
1131 		caps = get4(protodata);
1132 		protodata += 4;
1133 		show_printf("Capabilities = 0x%.8x", caps);
1134 
1135 		/* Server Time */
1136 		nttime = get8(protodata);
1137 		protodata += 8;
1138 		show_printf("Server Time = %s", format_nttime(nttime));
1139 
1140 		show_printf("Server TZ = %d", get2(protodata));
1141 		protodata += 2;
1142 
1143 		key_len = *protodata++;
1144 		show_printf("KeyLength = %d", key_len);
1145 		bytecount = get2(protodata);
1146 		protodata += 2;
1147 		show_printf("ByteCount = %d", bytecount);
1148 
1149 		if (smb_flags2 & FLAGS2_EXT_SEC) {
1150 			show_printf("Server GUID (16)");
1151 			output_bytes(protodata, 16);
1152 			protodata += 16;
1153 			show_printf("Security Blob (SPNEGO)");
1154 			output_bytes(protodata, bytecount - 16);
1155 		} else {
1156 			show_printf("NTLM Challenge: (%d)", key_len);
1157 			output_bytes(protodata, key_len);
1158 			protodata += key_len;
1159 			/*
1160 			 * Get Unicode from capabilities here,
1161 			 * as flags2 typically doesn't have it.
1162 			 * Also, this one is NOT aligned!
1163 			 */
1164 			tbuf[0] = '\0';
1165 			if (caps & 4) {
1166 				(void) unicode2ascii(tbuf, sizeof (tbuf),
1167 				    protodata, 2 * sizeof (tbuf));
1168 			} else {
1169 				(void) strlcpy(tbuf, (char *)protodata,
1170 				    sizeof (tbuf));
1171 			}
1172 			show_printf("Server Domain = %s", tbuf);
1173 		}
1174 	}
1175 }
1176 
1177 /*
1178  * LAN Manager remote admin function names.
1179  * [X/Open-SMB, Appendix B.8]
1180  */
1181 static const char *apiname_table[] = {
1182 	"RNetShareEnum",
1183 	"RNetShareGetInfo",
1184 	"NetShareSetInfo",
1185 	"NetShareAdd",
1186 	"NetShareDel",
1187 	"NetShareCheck",
1188 	"NetSessionEnum",
1189 	"NetSessionGetInfo",
1190 	"NetSessionDel",
1191 	"NetConnectionEnum",
1192 	"NetFileEnum",
1193 	"NetFileGetInfo",
1194 	"NetFileClose",
1195 	"RNetServerGetInfo",
1196 	"NetServerSetInfo",
1197 	"NetServerDiskEnum",
1198 	"NetServerAdminCommand",
1199 	"NetAuditOpen",
1200 	"NetAuditClear",
1201 	"NetErrorLogOpen",
1202 	"NetErrorLogClear",
1203 	"NetCharDevEnum",
1204 	"NetCharDevGetInfo",
1205 	"NetCharDevControl",
1206 	"NetCharDevQEnum",
1207 	"NetCharDevQGetInfo",
1208 	"NetCharDevQSetInfo",
1209 	"NetCharDevQPurge",
1210 	"RNetCharDevQPurgeSelf",
1211 	"NetMessageNameEnum",
1212 	"NetMessageNameGetInfo",
1213 	"NetMessageNameAdd",
1214 	"NetMessageNameDel",
1215 	"NetMessageNameFwd",
1216 	"NetMessageNameUnFwd",
1217 	"NetMessageBufferSend",
1218 	"NetMessageFileSend",
1219 	"NetMessageLogFileSet",
1220 	"NetMessageLogFileGet",
1221 	"NetServiceEnum",
1222 	"RNetServiceInstall",
1223 	"RNetServiceControl",
1224 	"RNetAccessEnum",
1225 	"RNetAccessGetInfo",
1226 	"RNetAccessSetInfo",
1227 	"RNetAccessAdd",
1228 	"RNetAccessDel",
1229 	"NetGroupEnum",
1230 	"NetGroupAdd",
1231 	"NetGroupDel",
1232 	"NetGroupAddUser",
1233 	"NetGroupDelUser",
1234 	"NetGroupGetUsers",
1235 	"NetUserEnum",
1236 	"RNetUserAdd",
1237 	"NetUserDel",
1238 	"NetUserGetInfo",
1239 	"RNetUserSetInfo",
1240 	"RNetUserPasswordSet",
1241 	"NetUserGetGroups",
1242 	"NetWkstaLogon",
1243 	"NetWkstaLogoff",
1244 	"NetWkstaSetUID",
1245 	"NetWkstaGetInfo",
1246 	"NetWkstaSetInfo",
1247 	"NetUseEnum",
1248 	"NetUseAdd",
1249 	"NetUseDel",
1250 	"NetUseGetInfo",
1251 	"DosPrintQEnum",
1252 	"DosPrintQGetInfo",
1253 	"DosPrintQSetInfo",
1254 	"DosPrintQAdd",
1255 	"DosPrintQDel",
1256 	"DosPrintQPause",
1257 	"DosPrintQContinue",
1258 	"DosPrintJobEnum",
1259 	"DosPrintJobGetInfo",
1260 	"RDosPrintJobSetInfo",
1261 	"DosPrintJobAdd",
1262 	"DosPrintJobSchedule",
1263 	"RDosPrintJobDel",
1264 	"RDosPrintJobPause",
1265 	"RDosPrintJobContinue",
1266 	"DosPrintDestEnum",
1267 	"DosPrintDestGetInfo",
1268 	"DosPrintDestControl",
1269 	"NetProfileSave",
1270 	"NetProfileLoad",
1271 	"NetStatisticsGet",
1272 	"NetStatisticsClear",
1273 	"NetRemoteTOD",
1274 	"NetBiosEnum",
1275 	"NetBiosGetInfo",
1276 	"NetServerEnum",
1277 	"I_NetServerEnum",
1278 	"NetServiceGetInfo",
1279 	"NetSplQmAbort",
1280 	"NetSplQmClose",
1281 	"NetSplQmEndDoc",
1282 	"NetSplQmOpen",
1283 	"NetSplQmStartDoc",
1284 	"NetSplQmWrite",
1285 	"DosPrintQPurge",
1286 	"NetServerEnum2"
1287 };
1288 static const int apinum_max = (
1289 	sizeof (apiname_table) /
1290 	sizeof (apiname_table[0]));
1291 
1292 static const char *
1293 pipeapi_name(int code)
1294 {
1295 	char *name;
1296 
1297 	switch (code) {
1298 	case 0x01:
1299 		name = "SetNmPipeState";
1300 		break;
1301 	case 0x11:
1302 		name = "RawReadNmPipe";
1303 		break;
1304 	case 0x21:
1305 		name = "QueryNmPipeState";
1306 		break;
1307 	case 0x22:
1308 		name = "QueryNmPipeInfo";
1309 		break;
1310 	case 0x23:
1311 		name = "PeekNmPipe";
1312 		break;
1313 	case 0x26:
1314 		name = "XactNmPipe";
1315 		break;
1316 	case 0x31:
1317 		name = "RawWriteNmPipe";
1318 		break;
1319 	case 0x36:
1320 		name = "ReadNmPipe";
1321 		break;
1322 	case 0x37:
1323 		name = "WriteNmPipe";
1324 		break;
1325 	case 0x53:
1326 		name = "WaitNmPipe";
1327 		break;
1328 	case 0x54:
1329 		name = "CallNmPipe";
1330 		break;
1331 	default:
1332 		name = "?";
1333 		break;
1334 	}
1335 	return (name);
1336 }
1337 
1338 /*
1339  * Interpret a "trans" SMB
1340  * [X/Open-SMB, Appendix B]
1341  *
1342  * This is very much like "trans2" below.
1343  */
1344 /* ARGSUSED */
1345 static void
1346 interpret_trans(int flags, uchar_t *data, int len, char *xtra, int xsz)
1347 {
1348 	struct smb *smb;
1349 	uchar_t *vwv; /* word parameters */
1350 	int wordcount;
1351 	uchar_t *byteparms;
1352 	int bytecount;
1353 	int parambytes;
1354 	int paramoffset;
1355 	int setupcount;
1356 	int subcode;
1357 	uchar_t *setupdata;
1358 	uchar_t *params;
1359 	int apinum;
1360 	int isunicode;
1361 	char filename[256];
1362 	const char *apiname;
1363 	const char *subcname;
1364 	ushort_t smb_flags2;
1365 
1366 	smb = (struct smb *)data;
1367 	smb_flags2 = get2(smb->flags2);
1368 	vwv = (uchar_t *)data + sizeof (struct smb);
1369 	wordcount = *vwv++;
1370 
1371 	/* Is the pathname in unicode? */
1372 	isunicode = smb_flags2 & FLAGS2_UNICODE;
1373 
1374 	byteparms = vwv + (2 * wordcount);
1375 	bytecount = get2(byteparms);
1376 	byteparms += 2;
1377 
1378 	/*
1379 	 * Print the lengths before we (potentially) bail out
1380 	 * due to lack of data (so the user knows why we did).
1381 	 */
1382 	if (flags & F_DTAIL)
1383 		show_printf("WordCount = %d", wordcount);
1384 
1385 	/* Get length and location of params and setup data. */
1386 	if (!(smb->flags & SERVER_RESPONSE)) {
1387 		/* CALL */
1388 		if (wordcount < 14)
1389 			return;
1390 		parambytes  = get2(vwv + (2 *  9));
1391 		paramoffset = get2(vwv + (2 * 10));
1392 		setupcount = *(vwv + (2 * 13));
1393 		setupdata  =   vwv + (2 * 14);
1394 	} else {
1395 		/* REPLY */
1396 		if (wordcount < 10)
1397 			return;
1398 		parambytes  = get2(vwv + (2 * 3));
1399 		paramoffset = get2(vwv + (2 * 4));
1400 		setupcount = *(vwv + (2 *  9));
1401 		setupdata  =   vwv + (2 * 10);
1402 	}
1403 
1404 	/* The parameters are offset from the SMB header. */
1405 	params = data + paramoffset;
1406 
1407 	if ((smb->flags & SERVER_RESPONSE) == 0) {
1408 		/* This is a CALL. */
1409 
1410 		if (setupcount > 0)
1411 			subcode = get2(setupdata);
1412 		else
1413 			subcode = -1; /* invalid */
1414 		subcname = pipeapi_name(subcode);
1415 
1416 		if (parambytes > 0)
1417 			apinum = params[0];
1418 		else
1419 			apinum = -1; /* invalid */
1420 		if (0 <= apinum && apinum < apinum_max)
1421 			apiname = apiname_table[apinum];
1422 		else
1423 			apiname = "?";
1424 
1425 		if (flags & F_SUM) {
1426 			int tl;
1427 			/* Only get one or the other */
1428 			if (*subcname != '?') {
1429 				tl = snprintf(xtra, xsz,
1430 				    " Func=%s", subcname);
1431 				xtra += tl;
1432 				xsz -= tl;
1433 			}
1434 			if (*apiname != '?')
1435 				snprintf(xtra, xsz,
1436 				    " Func=%s", apiname);
1437 			return;
1438 		}
1439 		if ((flags & F_DTAIL) == 0)
1440 			return;
1441 
1442 		/* print the word parameters */
1443 		show_printf("TotalParamBytes = %d", get2(vwv));
1444 		show_printf("TotalDataBytes = %d", get2(vwv+2));
1445 		show_printf("MaxParamBytes = %d", get2(vwv+4));
1446 		show_printf("MaxDataBytes = %d", get2(vwv+6));
1447 		show_printf("MaxSetupWords = %d", vwv[8]);
1448 		show_printf("TransFlags = 0x%.4x", get2(vwv+10));
1449 		show_printf("Timeout = 0x%.8x", get4(vwv+12));
1450 		/* skip Reserved2 */
1451 		show_printf("ParamBytes = %d", parambytes);
1452 		show_printf("ParamOffset = %d", paramoffset);
1453 		show_printf("DataBytes = %d", get2(vwv+22));
1454 		show_printf("DataOffset = %d", get2(vwv+24));
1455 		show_printf("SetupWords = %d", setupcount);
1456 		show_printf("ByteCount = %d", bytecount);
1457 
1458 		/* That finishes the VWV, now the misc. stuff. */
1459 		if (setupcount > 0)
1460 			show_printf("NmPipeFunc = 0x%x (%s)",
1461 			    subcode, subcname);
1462 		if (parambytes > 0)
1463 			show_printf("RAP_Func = %d (%s)",
1464 			    apinum, apiname);
1465 
1466 		/* Finally, print the byte parameters. */
1467 		GET_STRING(filename, byteparms, isunicode);
1468 		show_printf("FileName = %s", filename);
1469 	} else {
1470 		/* This is a REPLY. */
1471 		if (flags & F_SUM)
1472 			return;
1473 		if ((flags & F_DTAIL) == 0)
1474 			return;
1475 		/* print the word parameters */
1476 		show_printf("TotalParamBytes = %d", get2(vwv));
1477 		show_printf("TotalDataBytes = %d", get2(vwv+2));
1478 		/* skip Reserved */
1479 		show_printf("ParamBytes = 0x%.4x", parambytes);
1480 		show_printf("ParamOffset = 0x%.4x", paramoffset);
1481 		show_printf("ParamDispl. = 0x%.4x", get2(vwv+10));
1482 		show_printf("DataBytes = 0x%.4x", get2(vwv+12));
1483 		show_printf("DataOffset = 0x%.4x", get2(vwv+14));
1484 		show_printf("DataDispl. = 0x%.4x", get2(vwv+16));
1485 		show_printf("SetupWords = %d", setupcount);
1486 		show_printf("ByteCount = %d", bytecount);
1487 
1488 		show_printf("ParamVec (%d)", parambytes);
1489 		output_bytes(params, parambytes);
1490 	}
1491 }
1492 
1493 /*
1494  * Interpret a "TconX" SMB
1495  * [X/Open-SMB, Sec. 11.4]
1496  */
1497 /* ARGSUSED */
1498 static void
1499 interpret_tconX(int flags, uchar_t *data, int len, char *xtra, int xsz)
1500 {
1501 	int length;
1502 	int isunicode;
1503 	int bytecount;
1504 	int wordcount;
1505 	int andxcmd;
1506 	int andxoffset;
1507 	int tconflags;
1508 	int pw_len;
1509 	char path[256];
1510 	char tbuf[256];
1511 	char svc[8];
1512 	struct smb *smbdata;
1513 	uchar_t *tcondata;
1514 	ushort_t smb_flags2;
1515 
1516 	smbdata = (struct smb *)data;
1517 	smb_flags2 = get2(smbdata->flags2);
1518 	tcondata = (uchar_t *)data + sizeof (struct smb);
1519 	wordcount = *tcondata++;
1520 
1521 	isunicode = smb_flags2 & FLAGS2_UNICODE;
1522 
1523 	if ((smbdata->flags & SERVER_RESPONSE) == 0) {
1524 		/* Request */
1525 		if (wordcount < 4)
1526 			return;
1527 		andxcmd = get2(tcondata);
1528 		tcondata += 2;
1529 		andxoffset = get2(tcondata);
1530 		tcondata += 2;
1531 		tconflags = get2(tcondata);
1532 		tcondata += 2;
1533 		pw_len = get2(tcondata);
1534 		tcondata += 2;
1535 		bytecount = get2(tcondata);
1536 		tcondata += 2;
1537 
1538 		/* skip password */
1539 		if (pw_len > len)
1540 			pw_len = len;
1541 		tcondata += pw_len;
1542 
1543 		GET_STRING(path, tcondata, isunicode);
1544 		(void) strlcpy(svc, (char *)tcondata, sizeof (svc));
1545 
1546 		if (flags & F_SUM) {
1547 			snprintf(xtra, xsz, " Share=%s", path);
1548 			return;
1549 		}
1550 
1551 		if ((flags & F_DTAIL) == 0)
1552 			return;
1553 
1554 		show_printf("WordCount = %d", wordcount);
1555 		show_printf("ChainedCommand = 0x%.2x", andxcmd);
1556 		show_printf("NextOffset = 0x%.4x", andxoffset);
1557 		show_printf("TconFlags = 0x%.4x", tconflags);
1558 		show_printf("PasswordLength = 0x%.4x", pw_len);
1559 		show_printf("ByteCount = %d", bytecount);
1560 		show_printf("SharePath = %s", path);
1561 		show_printf("ServiceType = %s", svc);
1562 	} else {
1563 		/* response */
1564 		if (wordcount < 3)
1565 			return;
1566 		andxcmd = get2(tcondata);
1567 		tcondata += 2;
1568 		andxoffset = get2(tcondata);
1569 		tcondata += 2;
1570 		tconflags = get2(tcondata);
1571 		tcondata += 2;
1572 		bytecount = get2(tcondata);
1573 		tcondata += 2;
1574 
1575 		length = strlcpy(svc, (char *)tcondata, sizeof (svc));
1576 		tcondata += (length + 1);
1577 
1578 		if (flags & F_SUM) {
1579 			snprintf(xtra, xsz, " Type=%s", svc);
1580 			return;
1581 		}
1582 		if ((flags & F_DTAIL) == 0)
1583 			return;
1584 
1585 		show_printf("WordCount = %d", wordcount);
1586 		show_printf("ChainedCommand = 0x%.2x", andxcmd);
1587 		show_printf("NextOffset = 0x%.4x", andxoffset);
1588 		show_printf("OptionalSupport = 0x%.4x", tconflags);
1589 		show_printf("ByteCount = %d", bytecount);
1590 		show_printf("ServiceType = %s", svc);
1591 		GET_STRING(tbuf, tcondata, isunicode);
1592 		show_printf("NativeFS = %s", tbuf);
1593 	}
1594 }
1595 
1596 /*
1597  * Interpret a "SesssetupX" SMB
1598  * [X/Open-SMB, Sec. 11.3]
1599  */
1600 /* ARGSUSED */
1601 static void
1602 interpret_sesssetupX(int flags, uchar_t *data, int len, char *xtra, int xsz)
1603 {
1604 	int bytecount;
1605 	int lm_pw_len;
1606 	int ext_security;
1607 	int sec_blob_len;
1608 	int isunicode;
1609 	int nt_pw_len;
1610 	int wordcount;
1611 	int cap;
1612 	char tbuf[256];
1613 	struct smb *smbdata;
1614 	uchar_t *setupdata;
1615 	ushort_t smb_flags2;
1616 
1617 	smbdata  = (struct smb *)data;
1618 	smb_flags2 = get2(smbdata->flags2);
1619 	setupdata = (uchar_t *)data + sizeof (struct smb);
1620 	wordcount = *setupdata++;
1621 
1622 	isunicode = smb_flags2 & FLAGS2_UNICODE;
1623 	ext_security = smb_flags2 & FLAGS2_EXT_SEC;
1624 
1625 	if ((smbdata->flags & SERVER_RESPONSE) == 0) {
1626 		/* request summary */
1627 		if (flags & F_SUM) {
1628 			if (ext_security) {
1629 				/* No decoder for SPNEGO */
1630 				snprintf(xtra, xsz, " (SPNEGO)");
1631 				return;
1632 			}
1633 			if (wordcount != 13)
1634 				return;
1635 			setupdata += 14;
1636 			lm_pw_len = get2(setupdata);
1637 			setupdata += 2;
1638 			nt_pw_len = get2(setupdata);
1639 			setupdata += 6;
1640 			cap = get4(setupdata);
1641 			setupdata += 6 + lm_pw_len + nt_pw_len;
1642 
1643 			GET_STRING(tbuf, setupdata, isunicode);
1644 			snprintf(xtra, xsz, " Username=%s", tbuf);
1645 		}
1646 
1647 		if ((flags & F_DTAIL) == 0)
1648 			return;
1649 
1650 		/* request detail */
1651 		show_printf("WordCount = %d", wordcount);
1652 		if (wordcount < 7)
1653 			return;
1654 		/* words 0 - 6 */
1655 		show_printf("ChainedCommand = 0x%.2x", setupdata[0]);
1656 		setupdata += 2;
1657 		show_printf("NextOffset = 0x%.4x", get2(setupdata));
1658 		setupdata += 2;
1659 		show_printf("MaxBufferSize = %d", get2(setupdata));
1660 		setupdata += 2;
1661 		show_printf("MaxMPXRequests = %d", get2(setupdata));
1662 		setupdata += 2;
1663 		show_printf("VCNumber = %d", get2(setupdata));
1664 		setupdata += 2;
1665 		show_printf("SessionKey = 0x%.8x", get4(setupdata));
1666 		setupdata += 4;
1667 
1668 		if (ext_security) {
1669 			if (wordcount != 12)
1670 				return;
1671 			/* word 7 */
1672 			sec_blob_len = get2(setupdata);
1673 			setupdata += 2;
1674 			show_printf("Sec. blob len = %d", sec_blob_len);
1675 			/* words 8, 9 (reserved) */
1676 			setupdata += 4;
1677 		} else {
1678 			if (wordcount != 13)
1679 				return;
1680 			/* word 7 */
1681 			lm_pw_len = get2(setupdata);
1682 			setupdata += 2;
1683 			show_printf("LM_Hash_Len = %d", lm_pw_len);
1684 			/* word 8 */
1685 			nt_pw_len = get2(setupdata);
1686 			setupdata += 2;
1687 			show_printf("NT_Hash_Len = %d", nt_pw_len);
1688 			/* words 9, 10 (reserved) */
1689 			setupdata += 4;
1690 		}
1691 
1692 		cap = get4(setupdata);
1693 		show_printf("Capabilities = 0x%.8x", cap);
1694 		setupdata += 4;
1695 
1696 		bytecount = get2(setupdata);
1697 		setupdata += 2;
1698 		show_printf("ByteCount = %d", bytecount);
1699 
1700 		if (ext_security) {
1701 			/* No decoder for SPNEGO.  Just dump hex. */
1702 			show_printf("Security blob: (SPNEGO)");
1703 			output_bytes(setupdata, sec_blob_len);
1704 			setupdata += sec_blob_len;
1705 		} else {
1706 			/* Dump password hashes */
1707 			if (lm_pw_len > 0) {
1708 				show_printf("LM Hash (%d bytes)", lm_pw_len);
1709 				output_bytes(setupdata, lm_pw_len);
1710 				setupdata += lm_pw_len;
1711 			}
1712 			if (nt_pw_len > 0) {
1713 				show_printf("NT Hash (%d bytes)", nt_pw_len);
1714 				output_bytes(setupdata, nt_pw_len);
1715 				setupdata += nt_pw_len;
1716 			}
1717 
1718 			/* User */
1719 			GET_STRING(tbuf, setupdata, isunicode);
1720 			show_printf("AccountName = %s", tbuf);
1721 
1722 			/* Domain */
1723 			GET_STRING(tbuf, setupdata, isunicode);
1724 			show_printf("DomainName = %s", tbuf);
1725 		}
1726 
1727 		/*
1728 		 * Remainder is the same for etc. sec. or not
1729 		 * Native OS, Native LanMan
1730 		 */
1731 		GET_STRING(tbuf, setupdata, isunicode);
1732 		show_printf("NativeOS = %s", tbuf);
1733 
1734 		GET_STRING(tbuf, setupdata, isunicode);
1735 		show_printf("NativeLanman = %s", tbuf);
1736 	} else {
1737 		/* response summary */
1738 		if (flags & F_SUM) {
1739 			if (ext_security) {
1740 				/* No decoder for SPNEGO */
1741 				snprintf(xtra, xsz, " (SPNEGO)");
1742 			}
1743 			return;
1744 		}
1745 
1746 		if ((flags & F_DTAIL) == 0)
1747 			return;
1748 
1749 		/* response detail */
1750 		show_printf("WordCount = %d", wordcount);
1751 		if (wordcount < 3)
1752 			return;
1753 
1754 		show_printf("ChainedCommand = 0x%.2x", setupdata[0]);
1755 		setupdata += 2;
1756 		show_printf("NextOffset = 0x%.4x", get2(setupdata));
1757 		setupdata += 2;
1758 		show_printf("SetupAction = 0x%.4x", get2(setupdata));
1759 		setupdata += 2;
1760 
1761 		if (ext_security) {
1762 			if (wordcount != 4)
1763 				return;
1764 			sec_blob_len = get2(setupdata);
1765 			setupdata += 2;
1766 			show_printf("Sec. blob len = %d", sec_blob_len);
1767 		} else {
1768 			if (wordcount != 3)
1769 				return;
1770 		}
1771 
1772 		bytecount = get2(setupdata);
1773 		setupdata += 2;
1774 		show_printf("ByteCount = %d", bytecount);
1775 
1776 		if (ext_security) {
1777 			/* No decoder for SPNEGO.  Just dump hex. */
1778 			show_line("Security blob: (SPNEGO)");
1779 			output_bytes(setupdata, sec_blob_len);
1780 			setupdata += sec_blob_len;
1781 		}
1782 
1783 		/*
1784 		 * Native OS, Native LanMan
1785 		 */
1786 		GET_STRING(tbuf, setupdata, isunicode);
1787 		show_printf("NativeOS = %s", tbuf);
1788 
1789 		GET_STRING(tbuf, setupdata, isunicode);
1790 		show_printf("NativeLanman = %s", tbuf);
1791 
1792 		if (ext_security == 0) {
1793 			GET_STRING(tbuf, setupdata, isunicode);
1794 			show_printf("DomainName = %s", tbuf);
1795 		}
1796 	}
1797 }
1798 
1799 /*
1800  * Interpret "Trans2" SMB
1801  * [X/Open-SMB, Sec. 16]
1802  *
1803  * This is very much like "trans" above.
1804  */
1805 /* ARGSUSED */
1806 static void
1807 interpret_trans2(int flags, uchar_t *data, int len, char *xtra, int xsz)
1808 {
1809 	struct smb *smb;
1810 	uchar_t *vwv; /* word parameters */
1811 	int wordcount;
1812 	uchar_t *byteparms;
1813 	int bytecount;
1814 	int parambytes;
1815 	int paramoffset;
1816 	int setupcount;
1817 	int subcode;
1818 	uchar_t *setupdata;
1819 	uchar_t *params;
1820 	char *name;
1821 
1822 	smb  = (struct smb *)data;
1823 	vwv = (uchar_t *)data + sizeof (struct smb);
1824 	wordcount = *vwv++;
1825 
1826 	byteparms = vwv + (2 * wordcount);
1827 	bytecount = get2(byteparms);
1828 	byteparms += 2;
1829 
1830 	/*
1831 	 * Print the lengths before we (potentially) bail out
1832 	 * due to lack of data (so the user knows why we did).
1833 	 */
1834 	if (flags & F_DTAIL) {
1835 		show_printf("WordCount = %d", wordcount);
1836 		show_printf("ByteCount = %d", bytecount);
1837 	}
1838 
1839 	/* Get length and location of params and setup data. */
1840 	if (!(smb->flags & SERVER_RESPONSE)) {
1841 		/* CALL */
1842 		if (wordcount < 14)
1843 			return;
1844 		parambytes  = get2(vwv + (2 *  9));
1845 		paramoffset = get2(vwv + (2 * 10));
1846 		setupcount = *(vwv + (2 * 13));
1847 		setupdata  =   vwv + (2 * 14);
1848 	} else {
1849 		/* REPLY */
1850 		if (wordcount < 10)
1851 			return;
1852 		parambytes  = get2(vwv + (2 * 3));
1853 		paramoffset = get2(vwv + (2 * 4));
1854 		setupcount = *(vwv + (2 *  9));
1855 		setupdata  =   vwv + (2 * 10);
1856 	}
1857 	if (setupcount > 0)
1858 		subcode = get2(setupdata);
1859 	else
1860 		subcode = -1; /* invalid */
1861 
1862 	/* The parameters are offset from the SMB header. */
1863 	params = data + paramoffset;
1864 
1865 	if (flags & F_DTAIL && !(smb->flags & SERVER_RESPONSE)) {
1866 		/* This is a CALL. */
1867 		/* print the word parameters */
1868 		show_printf("TotalParamBytes = %d", get2(vwv));
1869 		show_printf("TotalDataBytes = %d", get2(vwv+2));
1870 		show_printf("MaxParamBytes = %d", get2(vwv+4));
1871 		show_printf("MaxDataBytes = %d", get2(vwv+6));
1872 		show_printf("MaxSetupWords = %d", vwv[8]);
1873 		show_printf("TransFlags = 0x%.4x", get2(vwv+10));
1874 		show_printf("Timeout = 0x%.8x", get4(vwv+12));
1875 		/* skip Reserved2 */
1876 		show_printf("ParamBytes = 0x%.4x", parambytes);
1877 		show_printf("ParamOffset = 0x%.4x", paramoffset);
1878 		show_printf("DataBytes = 0x%.4x", get2(vwv+22));
1879 		show_printf("DataOffset = 0x%.4x", get2(vwv+24));
1880 		show_printf("SetupWords = %d", setupcount);
1881 
1882 		/* That finishes the VWV, now the misc. stuff. */
1883 		show_printf("FunctionCode = %d", subcode);
1884 	}
1885 
1886 	if (!(smb->flags & SERVER_RESPONSE)) {
1887 		/* This is a CALL.  Do sub-function. */
1888 		switch (subcode) {
1889 		case TRANS2_OPEN:
1890 			name = "Open";
1891 			goto name_only;
1892 		case TRANS2_FIND_FIRST:
1893 			output_trans2_findfirst(flags, params, xtra, xsz);
1894 			break;
1895 		case TRANS2_FIND_NEXT2:
1896 			output_trans2_findnext(flags, params, xtra, xsz);
1897 			break;
1898 		case TRANS2_QUERY_FS_INFORMATION:
1899 			name = "QueryFSInfo";
1900 			goto name_only;
1901 		case TRANS2_QUERY_PATH_INFORMATION:
1902 			output_trans2_querypath(flags, params, xtra, xsz);
1903 			break;
1904 		case TRANS2_SET_PATH_INFORMATION:
1905 			name = "SetPathInfo";
1906 			goto name_only;
1907 		case TRANS2_QUERY_FILE_INFORMATION:
1908 			output_trans2_queryfile(flags, params, xtra, xsz);
1909 			break;
1910 		case TRANS2_SET_FILE_INFORMATION:
1911 			output_trans2_setfile(flags, params, xtra, xsz);
1912 			break;
1913 		case TRANS2_CREATE_DIRECTORY:
1914 			name = "CreateDir";
1915 			goto name_only;
1916 
1917 		default:
1918 			name = "Unknown";
1919 			/* fall through */
1920 		name_only:
1921 			if (flags & F_SUM)
1922 				snprintf(xtra, xsz, " %s", name);
1923 			if (flags & F_DTAIL)
1924 				show_printf("FunctionName = %s", name);
1925 			break;
1926 		}
1927 	}
1928 
1929 	if (flags & F_DTAIL && smb->flags & SERVER_RESPONSE) {
1930 		/* This is a REPLY. */
1931 		/* print the word parameters */
1932 		show_printf("TotalParamBytes = %d", get2(vwv));
1933 		show_printf("TotalDataBytes = %d",  get2(vwv+2));
1934 		/* skip Reserved */
1935 		show_printf("ParamBytes = 0x%.4x", parambytes);
1936 		show_printf("ParamOffset = 0x%.4x", paramoffset);
1937 		show_printf("ParamDispl. = 0x%.4x", get2(vwv+10));
1938 		show_printf("DataBytes = 0x%.4x", get2(vwv+12));
1939 		show_printf("DataOffset = 0x%.4x", get2(vwv+14));
1940 		show_printf("DataDispl. = 0x%.4x", get2(vwv+16));
1941 		show_printf("SetupWords = %d", setupcount);
1942 
1943 		output_bytes(byteparms, bytecount);
1944 	}
1945 }
1946 
1947 
1948 static void
1949 interpret_default(int flags, uchar_t *data, int len, char *xtra, int xsz)
1950 {
1951 	int slength;
1952 	int i, tl;
1953 	int isunicode;
1954 	int printit;
1955 	int wordcount;
1956 	int outsz;
1957 	char *outstr;
1958 	char *format;
1959 	char valuetype;
1960 	char word[10];
1961 	char *label;
1962 	char tempstr[256];
1963 	uchar_t *comdata, *limit;
1964 	char buff[80];
1965 	struct smb *smbdata;
1966 	struct decode *decoder;
1967 	uchar_t bval;
1968 	ushort_t wval;
1969 	ushort_t smb_flags2;
1970 	uint_t lval;
1971 
1972 	smbdata  = (struct smb *)data;
1973 	smb_flags2 = get2(smbdata->flags2);
1974 	comdata = (uchar_t *)data + sizeof (struct smb);
1975 	wordcount = *comdata++;
1976 	limit = data + len;
1977 
1978 	isunicode = smb_flags2 & FLAGS2_UNICODE;
1979 	decoder = &SMBtable[smbdata->com & 255];
1980 
1981 	if (smbdata->flags & SERVER_RESPONSE)
1982 		format = decoder->replyfmt;
1983 	else
1984 		format = decoder->callfmt;
1985 
1986 	if (!format || strlen(format) == 0) {
1987 		if (flags & F_SUM)
1988 			return;
1989 		show_printf("WordCount = %d", wordcount);
1990 		if (wordcount == 0)
1991 			return;
1992 		show_line("Word values (in hex):");
1993 		buff[0] = '\0';
1994 		for (i = 0; i < wordcount; i++) {
1995 			snprintf(word, sizeof (word), "%.4x ", get2(comdata));
1996 			comdata += 2;
1997 			if (comdata >= limit)
1998 				wordcount = i+1; /* terminate */
1999 			(void) strlcat(buff, word, sizeof (buff));
2000 			if (((i+1) & 7) == 0 || i == (wordcount-1)) {
2001 				show_line(buff);
2002 				strcpy(buff, "");
2003 			}
2004 		}
2005 		return;
2006 	}
2007 
2008 	if (flags & F_DTAIL)
2009 		show_printf("WordCount = %d", wordcount);
2010 
2011 	outstr = xtra;
2012 	outsz = xsz;
2013 
2014 	valuetype = format[0];
2015 	while (valuetype != '\0') {
2016 		if (comdata >= limit)
2017 			break;
2018 		label = format+1;
2019 		printit = (flags & F_DTAIL) || (valuetype <= 'Z');
2020 
2021 		switch (valuetype) {
2022 		case 'W':
2023 		case 'w':
2024 			wval = get2(comdata);
2025 			comdata += 2;
2026 			if (!printit)
2027 				break;
2028 			if (flags & F_DTAIL)
2029 				show_printf(
2030 				    "%s = 0x%.4x", label, wval);
2031 			else {
2032 				tl = snprintf(outstr, outsz,
2033 				    " %s=0x%x", label, wval);
2034 				outstr += tl;
2035 				outsz -= tl;
2036 			}
2037 			break;
2038 
2039 		case 'D':
2040 		case 'd':
2041 			wval = get2(comdata);
2042 			comdata += 2;
2043 			if (!printit)
2044 				break;
2045 			if (flags & F_DTAIL)
2046 				show_printf(
2047 				    "%s = %d", label, wval);
2048 			else {
2049 				tl = snprintf(outstr, outsz,
2050 				    " %s=%d", label, wval);
2051 				outstr += tl;
2052 				outsz -= tl;
2053 			}
2054 			break;
2055 
2056 		case 'L':
2057 		case 'l':
2058 			lval = get4(comdata);
2059 			comdata += 4;
2060 			if (!printit)
2061 				break;
2062 			if (flags & F_DTAIL)
2063 				show_printf(
2064 				    "%s = 0x%.8x", label, lval);
2065 			else {
2066 				tl = snprintf(outstr, outsz,
2067 				    " %s=0x%x", label, lval);
2068 				outstr += tl;
2069 				outsz -= tl;
2070 			}
2071 			break;
2072 
2073 		case 'B':
2074 		case 'b':
2075 			bval = comdata[0];
2076 			comdata += 1;
2077 			if (!printit)
2078 				break;
2079 			if (flags & F_DTAIL)
2080 				show_printf(
2081 				    "%s = 0x%.2x", label, bval);
2082 			else {
2083 				tl = snprintf(outstr, outsz,
2084 				    " %s=0x%x", label, bval);
2085 				outstr += tl;
2086 				outsz -= tl;
2087 			}
2088 			break;
2089 
2090 		case 'r':
2091 			comdata++;
2092 			break;
2093 
2094 		case 'R':
2095 			comdata += 2;
2096 			break;
2097 
2098 		case 'U':
2099 		case 'u':
2100 			/* Unicode or ASCII string. */
2101 			GET_STRING(tempstr, comdata, isunicode);
2102 			if (!printit)
2103 				break;
2104 			if (flags & F_DTAIL)
2105 				show_printf(
2106 				    "%s = %s", label, tempstr);
2107 			else {
2108 				tl = snprintf(outstr, outsz,
2109 				    " %s=%s", label, tempstr);
2110 				outstr += tl;
2111 				outsz -= tl;
2112 			}
2113 			break;
2114 
2115 		case 'S':
2116 		case 's':
2117 			slength = strlcpy(tempstr, (char *)comdata,
2118 			    sizeof (tempstr));
2119 			comdata += (slength+1);
2120 			if (!printit)
2121 				break;
2122 			if (flags & F_DTAIL)
2123 				show_printf(
2124 				    "%s = %s", label, tempstr);
2125 			else {
2126 				tl = snprintf(outstr, outsz,
2127 				    " %s=%s", label, tempstr);
2128 				outstr += tl;
2129 				outsz -= tl;
2130 			}
2131 			break;
2132 		}
2133 		format += (strlen(format) + 1);
2134 		valuetype = format[0];
2135 	}
2136 }
2137