diff --git a/Sources/SMBClient/FileTime.swift b/Sources/SMBClient/FileTime.swift index fbded2f..c06ab20 100644 --- a/Sources/SMBClient/FileTime.swift +++ b/Sources/SMBClient/FileTime.swift @@ -21,6 +21,10 @@ public struct FileTime { extension FileTime: CustomDebugStringConvertible { public var debugDescription: String { - date.debugDescription + if raw == 0 { + return "No time specified \(raw)" + } else { + return date.description + } } } diff --git a/Sources/SMBClient/Messages/Close+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/Close+CustomDebugStringConvertible.swift new file mode 100644 index 0000000..26797d3 --- /dev/null +++ b/Sources/SMBClient/Messages/Close+CustomDebugStringConvertible.swift @@ -0,0 +1,42 @@ +import Foundation + +extension Close.Request: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Close Request (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Close Flags: \(flags) + Reserved: \(String(format: "%04x", reserved)) + GUID handle File: \(UUID(data: fileId)) + """ + } +} + +extension Close.Response: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Close Response (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Flags: \(flags) + Reserved: \(String(format: "%04x", reserved)) + Create: \(FileTime(creationTime)) + Last Access: \(FileTime(lastAccessTime)) + Last Write: \(FileTime(lastWriteTime)) + Last Change: \(FileTime(changeTime)) + Allocation Size: \(allocationSize) + End Of File: \(endOfFile) + File Attributes: \(FileAttributes(rawValue: fileAttributes)) + """ + } +} + +extension Close.Flags: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .postQueryAttrib: + return "\(String(format: "0x%04x", rawValue)) (PostQuery Attrib: True)" + } + } +} diff --git a/Sources/SMBClient/Messages/Create+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/Create+CustomDebugStringConvertible.swift new file mode 100644 index 0000000..3947781 --- /dev/null +++ b/Sources/SMBClient/Messages/Create+CustomDebugStringConvertible.swift @@ -0,0 +1,262 @@ +import Foundation + +extension Create.Request: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Create Request (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Security Flags: \(securityFlags) + Oplock: \(Create.OplockLevel.debugDescription(rawValue: requestedOplockLevel)) + Impersonation level: \(Create.ImpersonationLevel.debugDescription(rawValue: impersonationLevel)) + Create Flags: \(String(format: "0x%016x", smbCreateFlags)) + Reserved: \(String(format: "0x%016x", reserved)) + Access Mask: \(desiredAccess) + File Attributes: \(fileAttributes) + Share Access: \(shareAccess) + Disposition: \(Create.CreateDisposition.debugDescription(rawValue: createDisposition)) + Create Options: \(createOptions) + Blob Offset: \(nameOffset) + Blob Length: \(nameLength) + Create Contexts Offset: \(createContextsOffset) + Create Contexts Length: \(createContextsLength) + Filename: \(String(data: buffer, encoding: .utf16LittleEndian) ?? buffer.hex) + """ + } +} + +extension Create.Response: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Create Response (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Oplock: \(Create.OplockLevel.debugDescription(rawValue: oplockLevel)) + Response Flags: \(flags) + Create Action: \(Create.CreateAction.debugDescription(rawValue: createAction)) + Create: \(FileTime(creationTime)) + Last Access: \(FileTime(lastAccessTime)) + Last Write: \(FileTime(lastWriteTime)) + Last Change: \(FileTime(changeTime)) + Allocation Size: \(allocationSize) + End Of File: \(endOfFile) + File Attributes: \(fileAttributes) + Reserved: \(String(format: "%04x", reserved2)) + GUID handle File: \(UUID(data: fileId)) + Blob Offset: \(createContextsOffset) + Blob Length: \(createContextsLength) + ExtraInfo: \(buffer.hex) + """ + } +} + +extension Create.OplockLevel: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .none: + return "No oplock" + case .ii: + return "Level2 oplock" + case .exclusive: + return "Exclusive oplock" + case .batch: + return "Batch oplock" + case .lease: + return "Lease" + + } + } + + static public func debugDescription(rawValue: UInt8) -> String { + if let oplockLevel = Create.OplockLevel(rawValue: rawValue) { + return oplockLevel.debugDescription + } else { + return String(format: "0x%02x", rawValue) + } + } +} + +extension Create.ImpersonationLevel: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .anonymous: + return "Anonymous" + case .identification: + return "Identification" + case .impersonation: + return "Impersonation" + case .delegation: + return "Delegation" + } + } + + static public func debugDescription(rawValue: UInt32) -> String { + if let impersonationLevel = Create.ImpersonationLevel(rawValue: rawValue) { + return impersonationLevel.debugDescription + } else { + return String(format: "0x%08x", rawValue) + } + } +} + +extension Create.ShareAccess: CustomDebugStringConvertible { + public var debugDescription: String { + var values = [String]() + if contains(.read) { + values.append("Read") + } + if contains(.write) { + values.append("Write") + } + if contains(.delete) { + values.append("Delete") + } + + if values.isEmpty { + return "\(String(format: "0x%08x", rawValue))" + } else { + return "\(String(format: "0x%08x", rawValue)) (\(values.joined(separator: ", ")))" + } + } +} + +extension Create.CreateDisposition: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .supersede: + return "Supersede (supersede existing file (if it exists))" + case .open: + return "Open (if file exists open it, else fail)" + case .create: + return "Create (if file exists fail, else create it)" + case .openIf: + return "Open If (if file exists open it, else create it)" + case .overwrite: + return "Overwrite (if file exists overwrite, else fail)" + case .overwriteIf: + return "Overwrite If (if file exists overwrite, else create it)" + } + } + + static public func debugDescription(rawValue: UInt32) -> String { + if let disposition = Create.CreateDisposition(rawValue: rawValue) { + return disposition.debugDescription + } else { + return String(format: "0x%08x", rawValue) + } + } +} + +extension Create.CreateOptions: CustomDebugStringConvertible { + public var debugDescription: String { + var values = [String]() + + if contains(.directoryFile) { + values.append("Directory") + } + if contains(.writeThrough) { + values.append("Write Through") + } + if contains(.sequentialOnly) { + values.append("Sequential Only") + } + if contains(.noIntermediateBuffering) { + values.append("Intermediate Buffering") + } + if contains(.synchronousIoAlert) { + values.append("Sync I/O Alert") + } + if contains(.synchronousIoNonAlert) { + values.append("Sync I/O Nonalert") + } + if contains(.nonDirectoryFile) { + values.append("Non-Directory") + } + if contains(.completeIfOplocked) { + values.append("Complete If Oplocked") + } + if contains(.noEaKnowledge) { + values.append("No EA Knowledge") + } + if contains(.randomAccess) { + values.append("Random Access") + } + if contains(.deleteOnClose) { + values.append("Delete On Close") + } + if contains(.openByFileId) { + values.append("Open By FileID") + } + if contains(.openForBackupIntent) { + values.append("Backup Intent") + } + if contains(.noCompression) { + values.append("No Compression") + } + if contains(.openRemoteInstance) { + values.append("Open Remote Instance") + } + if contains(.requiringOplock) { + values.append("Requiring Oplock") + } + if contains(.disallowExclusive) { + values.append("Disallow Exclusive") + } + if contains(.reserveOpfilter) { + values.append("Reserve Opfilter") + } + if contains(.openReparsePoint) { + values.append("Open Reparse Point") + } + if contains(.openNoRecall) { + values.append("Open No Recall") + } + if contains(.openForFreeSpaceQuery) { + values.append("Open For Free Space Query") + } + + if values.isEmpty { + return "\(String(format: "0x%08x", rawValue))" + } else { + return "\(String(format: "0x%08x", rawValue)) (\(values.joined(separator: ", ")))" + } + } +} + +extension Create.Flags: CustomDebugStringConvertible { + public var debugDescription: String { + var values = [String]() + if contains(.releasePoint) { + values.append("Release Point") + } + + if values.isEmpty { + return "\(String(format: "0x%02x", rawValue))" + } else { + return "\(String(format: "0x%02x", rawValue)) (\(values.joined(separator: ", ")))" + } + } +} + +extension Create.CreateAction: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .superseded: + return "No action taken?" + case .opened: + return "The file existed and was opened" + case .created: + return "The file did not exist but was created" + case .overwritten: + return "The file existed and was truncated" + } + } + + static public func debugDescription(rawValue: UInt32) -> String { + if let action = Create.CreateAction(rawValue: rawValue) { + return action.debugDescription + } else { + return String(format: "0x%08x", rawValue) + } + } +} diff --git a/Sources/SMBClient/Messages/Create.swift b/Sources/SMBClient/Messages/Create.swift index 18a6bbc..cf8c76e 100644 --- a/Sources/SMBClient/Messages/Create.swift +++ b/Sources/SMBClient/Messages/Create.swift @@ -112,23 +112,6 @@ public enum Create { public let createContextsLength: UInt32 public let buffer: Data - public struct Flags: OptionSet, Sendable { - public let rawValue: UInt8 - - public init(rawValue: UInt8) { - self.rawValue = rawValue - } - - public static let releasePoint = Flags(rawValue: 0x00000001) - } - - public enum CreateAction: UInt32 { - case superseded = 0x00000000 - case opened = 0x00000001 - case created = 0x00000002 - case overwritten = 0x00000003 - } - public init(data: Data) { let reader = ByteReader(data) @@ -222,4 +205,21 @@ public enum Create { public static let openNoRecall = CreateOptions(rawValue: 0x00400000) public static let openForFreeSpaceQuery = CreateOptions(rawValue: 0x00800000) } + + public struct Flags: OptionSet, Sendable { + public let rawValue: UInt8 + + public init(rawValue: UInt8) { + self.rawValue = rawValue + } + + public static let releasePoint = Flags(rawValue: 0x00000001) + } + + public enum CreateAction: UInt32 { + case superseded = 0x00000000 + case opened = 0x00000001 + case created = 0x00000002 + case overwritten = 0x00000003 + } } diff --git a/Sources/SMBClient/Messages/FileAttributes.swift b/Sources/SMBClient/Messages/FileAttributes.swift index c8a9f06..5f09a1f 100644 --- a/Sources/SMBClient/Messages/FileAttributes.swift +++ b/Sources/SMBClient/Messages/FileAttributes.swift @@ -28,67 +28,72 @@ public struct FileAttributes: OptionSet, Sendable { public static let recallOnDataAccess = FileAttributes(rawValue: 0x00400000) } -extension FileAttributes: CustomStringConvertible { - public var description: String { - var attributes = [String]() +extension FileAttributes: CustomDebugStringConvertible { + public var debugDescription: String { + var values = [String]() if contains(.readonly) { - attributes.append("readonly") + values.append("Read Only") } if contains(.hidden) { - attributes.append("hidden") + values.append("Hidden") } if contains(.system) { - attributes.append("system") + values.append("System") } if contains(.directory) { - attributes.append("directory") + values.append("Directory") } if contains(.archive) { - attributes.append("archive") + values.append("Requires archived") } if contains(.normal) { - attributes.append("normal") + values.append("Normal") } if contains(.temporary) { - attributes.append("temporary") + values.append("Temporary") } if contains(.sparseFile) { - attributes.append("sparseFile") + values.append("Sparse") } if contains(.reparsePoint) { - attributes.append("reparsePoint") + values.append("Reparse Point") } if contains(.compressed) { - attributes.append("compressed") + values.append("Compressed") } if contains(.offline) { - attributes.append("offline") + values.append("Offline") } if contains(.notContentIndexed) { - attributes.append("notContentIndexed") + values.append("Not Content Indexed") } if contains(.encrypted) { - attributes.append("encrypted") + values.append("Encrypted") } if contains(.integrityStream) { - attributes.append("integrityStream") + values.append("Integrity Stream") } if contains(.noScrubData) { - attributes.append("noScrubData") + values.append("No Scrub Data") } if contains(.recallOnOpen) { - attributes.append("recallOnOpen") + values.append("Recall On Open") } if contains(.pinned) { - attributes.append("pinned") + values.append("Pinned") } if contains(.unpinned) { - attributes.append("unpinned") + values.append("Unpinned") } if contains(.recallOnDataAccess) { - attributes.append("recallOnDataAccess") + values.append("Recall On Data Access") + } + + if values.isEmpty { + return "0x\(String(format: "%08x", rawValue))" + } else { + return "0x\(String(format: "%08x", rawValue)) (\(values.joined(separator: ", ")))" } - return attributes.joined(separator: ", ") } } diff --git a/Sources/SMBClient/Messages/FileInformationClasses/FileDispositionInformation.swift b/Sources/SMBClient/Messages/FileInformationClasses/FileDispositionInformation.swift index 8bf5903..d9db235 100644 --- a/Sources/SMBClient/Messages/FileInformationClasses/FileDispositionInformation.swift +++ b/Sources/SMBClient/Messages/FileInformationClasses/FileDispositionInformation.swift @@ -12,3 +12,9 @@ public struct FileDispositionInformation: FileInformationClass { Data() + deletePending } } + +extension FileDispositionInformation: CustomStringConvertible { + public var description: String { + "SMB2_FILE_DISPOSITION_INFO: Delete on close: \(deletePending == 1 ? "DELETE this file when closed" : "Normal access, do not delete on close")" + } +} diff --git a/Sources/SMBClient/Messages/FileInformationClasses/FileInfoClass.swift b/Sources/SMBClient/Messages/FileInformationClasses/FileInfoClass.swift index 6477747..05cd41b 100644 --- a/Sources/SMBClient/Messages/FileInformationClasses/FileInfoClass.swift +++ b/Sources/SMBClient/Messages/FileInformationClasses/FileInfoClass.swift @@ -49,3 +49,57 @@ public enum FileInfoClass: UInt8 { case fileTrackingInformation = 0x24 case fileValidDataLengthInformation = 0x27 } + +extension FileInfoClass: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .fileAccessInformation: return "SMB2_FILE_ACCESS_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileAlignmentInformation: return "SMB2_FILE_ALIGNMENT_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileAllInformation: return "SMB2_FILE_ALL_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileAllocationInformation: return "SMB2_FILE_ALLOCATION_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileAlternateNameInformation: return "SMB2_FILE_ALTERNATE_NAME_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileAttributeTagInformation: return "SMB2_FILE_ATTRIBUTE_TAG_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileBasicInformation: return "SMB2_FILE_BASIC_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileBothDirectoryInformation: return "SMB2_FILE_BOTH_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileCompressionInformation: return "SMB2_FILE_COMPRESSION_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileDirectoryInformation: return "SMB2_FILE_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileDispositionInformation: return "SMB2_FILE_DISPOSITION_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileEaInformation: return "SMB2_FILE_EA_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileEndOfFileInformation: return "SMB2_FILE_ENDOFFILE_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileFullDirectoryInformation: return "SMB2_FILE_FULL_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileFullEaInformation: return "SMB2_FILE_FULL_EA_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileHardLinkInformation: return "SMB2_FILE_HARD_LINK_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdBothDirectoryInformation: return "SMB2_FILE_ID_BOTH_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdExtdDirectoryInformation: return "SMB2_FILE_ID_EXTD_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdFullDirectoryInformation: return "SMB2_FILE_ID_FULL_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdGlobalTxDirectoryInformation: return "SMB2_FILE_ID_GLOBAL_TX_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdInformation: return "SMB2_FILE_ID_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileInternalInformation: return "SMB2_FILE_INTERNAL_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileLinkInformation: return "SMB2_FILE_LINK_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileMailslotQueryInformation: return "SMB2_FILE_MAIL_SLOT_QUERY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileMailslotSetInformation: return "SMB2_FILE_MAIL_SLOT_SET_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileModeInformation: return "SMB2_FILE_MODE_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileMoveClusterInformation: return "SMB2_FILE_MOVE_CLUSTER_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileNameInformation: return "SMB2_FILE_NAME_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileNamesInformation: return "SMB2_FILE_NAMES_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileNetworkOpenInformation: return "SMB2_FILE_NETWORK_OPEN_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileNormalizedNameInformation: return "SMB2_FILE_NORMALIZED_NAME_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileObjectIdInformation: return "SMB2_FILE_OBJECTID_INFO (\(String(format: "0x%02x", rawValue)))" + case .filePipeInformation: return "SMB2_FILE_PIPE_INFO (\(String(format: "0x%02x", rawValue)))" + case .filePipeLocalInformation: return "SMB2_FILE_PIPE_LOCAL_INFO (\(String(format: "0x%02x", rawValue)))" + case .filePipeRemoteInformation: return "SMB2_FILE_PIPE_REMOTE_INFO (\(String(format: "0x%02x", rawValue)))" + case .filePositionInformation: return "SMB2_FILE_POSITION_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileQuotaInformation: return "SMB2_FILE_QUOTA_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileRenameInformation: return "SMB2_FILE_RENAME_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileReparsePointInformation: return "SMB2_FILE_REPARSE_POINT_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileSfioReserveInformation: return "SMB2_FILE_SFIO_RESERVE_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileSfioVolumeInformation: return "SMB2_FILE_SFIO_VOLUME_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileShortNameInformation: return "SMB2_FILE_SHORT_NAME_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileStandardInformation: return "SMB2_FILE_STANDARD_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileStandardLinkInformation: return "SMB2_FILE_STANDARD_LINK_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileStreamInformation: return "SMB2_FILE_STREAM_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileTrackingInformation: return "SMB2_FILE_TRACKING_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileValidDataLengthInformation: return "SMB2_FILE_VALID_DATA_LENGTH_INFO (\(String(format: "0x%02x", rawValue)))" + } + } +} diff --git a/Sources/SMBClient/Messages/FileInformationClasses/InfoType.swift b/Sources/SMBClient/Messages/FileInformationClasses/InfoType.swift index 5546004..dfff404 100644 --- a/Sources/SMBClient/Messages/FileInformationClasses/InfoType.swift +++ b/Sources/SMBClient/Messages/FileInformationClasses/InfoType.swift @@ -6,3 +6,26 @@ public enum InfoType: UInt8 { case security = 0x03 case quota = 0x04 } + +extension InfoType: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .file: + return "FILE_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileSystem: + return "FS_INFO (\(String(format: "0x%02x", rawValue)))" + case .security: + return "SEC_INFO (\(String(format: "0x%02x", rawValue)))" + case .quota: + return "QUOTA_INFO (\(String(format: "0x%02x", rawValue)))" + } + } + + static public func debugDescription(rawValue: UInt8) -> String { + if let infoType = InfoType(rawValue: rawValue) { + return infoType.debugDescription + } else { + return String(format: "0x%02x", rawValue) + } + } +} diff --git a/Sources/SMBClient/Messages/IOCtl+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/IOCtl+CustomDebugStringConvertible.swift new file mode 100644 index 0000000..b453099 --- /dev/null +++ b/Sources/SMBClient/Messages/IOCtl+CustomDebugStringConvertible.swift @@ -0,0 +1,104 @@ +import Foundation + +extension IOCtl.Request: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Ioctl Request (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Reserved: \(String(format: "%04x", reserved)) + Function: \(IOCtl.CtlCode.debugDescription(rawValue: ctlCode)) (\(String(format: "0x%08x", ctlCode))) + GUID handle File: \(UUID(data: fileId)) + Blob Offset: \(inputOffset) + Blob Length: \(inputCount) + Max Ioctl In Size: \(maxInputResponse) + Blob Offset: \(outputOffset) + Blob Length: \(outputCount) + Max Ioctl Out Size: \(maxOutputResponse) + Flags: \(flags) + Reserved: \(String(format: "%08x", reserved)) + Buffer: \(buffer.hex) + """ + } +} + +extension IOCtl.Response: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Ioctl Response (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Reserved: \(String(format: "%04x", reserved)) + Function: \(IOCtl.CtlCode.debugDescription(rawValue: ctlCode)) (\(String(format: "0x%08x", ctlCode))) + GUID handle File: \(UUID(data: fileId)) + Blob Offset: \(inputOffset) + Blob Length: \(inputCount) + Blob Offset: \(outputOffset) + Blob Length: \(outputCount) + Flags: \(flags) + Reserved: \(String(format: "%08x", reserved)) + Buffer: \(buffer.hex) + """ + } +} + +extension IOCtl.CtlCode: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .dfsGetReferrals: + return "FSCTL_DFS_GET_REFERRALS" + case .pipePeek: + return "FSCTL_PIPE_PEEK" + case .pipeWait: + return "FSCTL_PIPE_WAIT" + case .pipeTransceive: + return "FSCTL_PIPE_TRANSCEIVE" + case .srvCopyChunk: + return "FSCTL_SRV_COPYCHUNK" + case .srvEnumerateSnapshots: + return "FSCTL_SRV_ENUMERATE_SNAPSHOTS" + case .srvRequestResumeKey: + return "FSCTL_SRV_REQUEST_RESUME_KEY" + case .srvReadHash: + return "FSCTL_SRV_READ_HASH" + case .srvCopyChunkWrite: + return "FSCTL_SRV_COPYCHUNK_WRITE" + case .lmrRequestResiliency: + return "FSCTL_LMR_REQUEST_RESILIENCY" + case .queryNetworkInterfaceInfo: + return "FSCTL_QUERY_NETWORK_INTERFACE_INFO" + case .setReleasePoint: + return "FSCTL_SET_REPARSE_POINT" + case .dfsGetReferralsEx: + return "FSCTL_DFS_GET_REFERRALS_EX" + case .fileLevelTrim: + return "FSCTL_FILE_LEVEL_TRIM" + case .validateNegotiateInfo: + return "FSCTL_VALIDATE_NEGOTIATE_INFO" + } + } + + static public func debugDescription(rawValue: UInt32) -> String { + if let ctlCode = IOCtl.CtlCode(rawValue: rawValue) { + return ctlCode.debugDescription + } else { + return String(format: "0x%08x", rawValue) + } + } +} + +extension IOCtl.Flags: CustomDebugStringConvertible { + public var debugDescription: String { + var values = [String]() + + if contains(.isFsctl) { + values.append("Is FSCTL: True") + } + + if values.isEmpty { + return "0x\(String(format: "%08x", rawValue))" + } else { + return "0x\(String(format: "%08x", rawValue)) (\(values.joined(separator: ", ")))" + } + } +} diff --git a/Sources/SMBClient/Messages/QueryDirectory+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/QueryDirectory+CustomDebugStringConvertible.swift new file mode 100644 index 0000000..fce1bac --- /dev/null +++ b/Sources/SMBClient/Messages/QueryDirectory+CustomDebugStringConvertible.swift @@ -0,0 +1,98 @@ +import Foundation + +extension QueryDirectory.Request: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Find Request (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Info Level: \(fileInformationClass) + Find Flags: \(flags) + File Index: \(String(format: "0x%08x", fileIndex)) + GUID handle File: \(UUID(data: fileId)) + Blob Offset: \(fileNameOffset) + Blob Length: \(fileNameLength) + Output Buffer Length: \(outputBufferLength) + Search Pattern: \(String(data: buffer, encoding: .utf16LittleEndian) ?? buffer.hex) + """ + } +} + +extension QueryDirectory.Response: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Find Response (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Blob Offset: \(outputBufferOffset) + Blob Length: \(outputBufferLength) + Info: + \(files.map { "\($0)".split(separator: "\n").map { " \($0)" }.joined(separator: "\n") }.joined(separator: "\n")) + """ + } +} + +extension QueryDirectory.FileInformationClass: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .fileDirectoryInformation: + return "SMB2_FILE_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileFullDirectoryInformation: + return "SMB2_FILE_FULL_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdFullDirectoryInformation: + return "SMB2_FILE_ID_FULL_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileBothDirectoryInformation: + return "SMB2_FILE_BOTH_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdBothDirectoryInformation: + return "SMB2_FILE_ID_BOTH_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileNamesInformation: + return "SMB2_FILE_NAME_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileIdExtdDirectoryInformation: + return "SMB2_FILE_ID_EXTD_DIRECTORY_INFO (\(String(format: "0x%02x", rawValue)))" + case .fileInfomationClass_Reserved: + return "SMB2_FILE_INFORMATION_CLASS_RESERVED (\(String(format: "0x%02x", rawValue)))" + } + } +} + +extension QueryDirectory.Flags: CustomDebugStringConvertible { + public var debugDescription: String { + var values = [String]() + if contains(.restartScans) { + values.append("Restart Scans") + } + if contains(.returnSingleEntry) { + values.append("Single Entry") + } + if contains(.indexSpecified) { + values.append("Index Specified") + } + if contains(.reopen) { + values.append("Reopen") + } + + if values.isEmpty { + return "\(String(format: "0x%02x", rawValue))" + } else { + return "\(String(format: "0x%02x", rawValue)) (\(values.joined(separator: ", ")))" + } + } +} + +extension QueryDirectory.FileIdBothDirectoryInformation: CustomDebugStringConvertible { + public var debugDescription: String { + """ + FileIdBothDirectoryInfo: + Next Offset: \(nextEntryOffset) + File Index: \(fileIndex) + Create: \(FileTime(creationTime)) + Last Access: \(FileTime(lastAccessTime)) + Last Write: \(FileTime(lastWriteTime)) + Last Change: \(FileTime(changeTime)) + End Of File: \(endOfFile) + Allocation Size: \(allocationSize) + File Attributes: \(fileAttributes) + File Name: \(fileName) + """ + } +} diff --git a/Sources/SMBClient/Messages/QueryInfo+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/QueryInfo+CustomDebugStringConvertible.swift new file mode 100644 index 0000000..372ee4f --- /dev/null +++ b/Sources/SMBClient/Messages/QueryInfo+CustomDebugStringConvertible.swift @@ -0,0 +1,54 @@ +import Foundation + +extension QueryInfo.Request: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + GetInfo Request (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Class: \(infoType) + InfoLevel: \(fileInfoClass) + Max Response Size: \(outputBufferLength) + Getinfo Input Offset: \(inputBufferOffset) + Reserved: \(String(format: "%04x", reserved)) + Getinfo Input Size: \(inputBufferLength) + Additional Info: \(String(format: "0x%08x", additionalInformation)) + Flags: \(flags) + GUID handle: \(UUID(data: fileId)) + """ + } +} + +extension QueryInfo.Response: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + GetInfo Response (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Blob Offset: \(outputBufferOffset) + Blob Length: \(outputBufferLength) + Data: \(buffer.hex) + """ + } +} + +extension QueryInfo.Flags: CustomDebugStringConvertible { + public var debugDescription: String { + var values = [String]() + if contains(.restartScans) { + values.append("Restart Scans") + } + if contains(.returnSingleEntry) { + values.append("Single Entry") + } + if contains(.indexSpecified) { + values.append("Index Specified") + } + + if values.isEmpty { + return "\(String(format: "0x%02x", rawValue))" + } else { + return "\(String(format: "0x%02x", rawValue)) (\(values.joined(separator: ", ")))" + } + } +} diff --git a/Sources/SMBClient/Messages/Read+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/Read+CustomDebugStringConvertible.swift new file mode 100644 index 0000000..b6fdf63 --- /dev/null +++ b/Sources/SMBClient/Messages/Read+CustomDebugStringConvertible.swift @@ -0,0 +1,62 @@ +import Foundation + +extension Read.Request: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Read Request (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Padding: \(String(format: "0x%02x", padding)) + Flags: \(String(format: "0x%02x", flags)) + Read Length: \(length) + File Offset: \(offset) + GUID handle File: \(UUID(data: fileId)) + Min Count: \(minimumCount) + Channel: \(channel) + Remaining Bytes: \(remainingBytes) + Blob Offset: \(readChannelInfoOffset) + Blob Length: \(readChannelInfoLength) + Channel Info Blob: \(buffer.hex) + """ + } +} + +extension Read.Response: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + Read Response (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Blob Offset: \(dataOffset) + Reserved: \(String(format: "%02x", reserved)) + Blob Length: \(dataLength) + Data Rmaining: \(dataRemaining) + Reserved2: \(String(format: "%08x", reserved2)) + Data: \(buffer.hex) + """ + } +} + +extension Read.Flags: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .readUnbuffered: + return "Unbuffered" + case .requestCompressed: + return "Compressed" + } + } +} + +extension Read.Channel: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .none: + return "None" + case .rdmaV1: + return "RDMA V1" + case .rdmaV1Invalidate: + return "RDMA V1_INVALIDATE" + } + } +} diff --git a/Sources/SMBClient/Messages/SetInfo+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/SetInfo+CustomDebugStringConvertible.swift new file mode 100644 index 0000000..338e0f0 --- /dev/null +++ b/Sources/SMBClient/Messages/SetInfo+CustomDebugStringConvertible.swift @@ -0,0 +1,29 @@ +import Foundation + +extension SetInfo.Request: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + SetInfo Request (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + Class: \(infoType) + InfoLevel: \(fileInfoClass) + Setinfo Size: \(bufferLength) + Setinfo Offset: \(bufferOffset) + Reserved: \(String(format: "%04x", reserved)) + Additional Info: \(String(format: "0x%08x", additionalInformation)) + GUID handle: \(UUID(data: fileId)) + Data: \(buffer.hex) + """ + } +} + +extension SetInfo.Response: CustomDebugStringConvertible { + public var debugDescription: String { + """ + \(header) + SetInfo Response (\(String(format: "0x%02x", header.command))) + StructureSize: \(structureSize) + """ + } +} diff --git a/docker-compose.yml b/docker-compose.yml index a4d6c7d..81fa612 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: - "4445:445" environment: AVAHI_DISABLE: 1 + SAMBA_CONF_LOG_LEVEL: 3 SAMBA_GLOBAL_CONFIG_client_SPACE_signing: mandatory SAMBA_GLOBAL_CONFIG_access_SPACE_based_SPACE_share_SPACE_enum: yes