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