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
get2(uchar_t * p)617 get2(uchar_t *p) {
618 return (p[0] + (p[1]<<8));
619 }
620 static uint32_t
get4(uchar_t * p)621 get4(uchar_t *p) {
622 return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
623 }
624 static uint64_t
get8(uchar_t * p)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 *
format_nttime(uint64_t nt_time)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
interpret_smb(int flags,uchar_t * data,int len)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
output_bytes(uchar_t * data,int bytecount)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
unicode2ascii(char * outstr,int outlen,uchar_t * instr,int inlen)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
get_info_level(char * outstr,int outsz,int value)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
output_trans2_querypath(int flags,uchar_t * data,char * xtra,int xsz)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
output_trans2_queryfile(int flags,uchar_t * data,char * xtra,int xsz)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
output_trans2_setfile(int flags,uchar_t * data,char * xtra,int xsz)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
output_trans2_findfirst(int flags,uchar_t * data,char * xtra,int xsz)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
output_trans2_findnext(int flags,uchar_t * data,char * xtra,int xsz)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
interpret_negprot(int flags,uchar_t * data,int len,char * xtra,int xsz)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 *
pipeapi_name(int code)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
interpret_trans(int flags,uchar_t * data,int len,char * xtra,int xsz)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
interpret_tconX(int flags,uchar_t * data,int len,char * xtra,int xsz)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
interpret_sesssetupX(int flags,uchar_t * data,int len,char * xtra,int xsz)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
interpret_trans2(int flags,uchar_t * data,int len,char * xtra,int xsz)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
interpret_default(int flags,uchar_t * data,int len,char * xtra,int xsz)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