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 */
52struct 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
80static void interpret_sesssetupX(int, uchar_t *, int, char *, int);
81static void interpret_tconX(int, uchar_t *, int, char *, int);
82static void interpret_trans(int, uchar_t *, int, char *, int);
83static void interpret_trans2(int, uchar_t *, int, char *, int);
84static void interpret_negprot(int, uchar_t *, int, char *, int);
85static 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
102struct 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 */
113static 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). */
616static uint16_t
617get2(uchar_t *p) {
618	return (p[0] + (p[1]<<8));
619}
620static uint32_t
621get4(uchar_t *p) {
622	return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
623}
624static uint64_t
625get8(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 */
634static const uint64_t DIFF1970TO1601 = 11644473600ULL;
635static const uint32_t TEN_MIL = 10000000UL;
636static char *
637format_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
662out:
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 */
670void
671interpret_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
788static void
789output_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 */
810static int
811unicode2ascii(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 */
856static void
857get_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 */
893static void
894output_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 */
922static void
923output_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 */
947static void
948output_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 */
972static void
973output_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 */
1010static void
1011output_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 */
1049static void
1050interpret_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 */
1181static 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};
1288static const int apinum_max = (
1289	sizeof (apiname_table) /
1290	sizeof (apiname_table[0]));
1291
1292static const char *
1293pipeapi_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 */
1345static void
1346interpret_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 */
1498static void
1499interpret_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 */
1601static void
1602interpret_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 */
1806static void
1807interpret_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
1948static void
1949interpret_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