Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOSDC 2024 SMBファイル共有をSwiftで実装する

iOSDC 2024 SMBファイル共有をSwiftで実装する

Kishikawa Katsumi

August 23, 2024
Tweet

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. r "QQMF1MBUGPSN ‣ /FUXPSLGSBNFXPSL J04 cNBD04 cUW04 cWJTJPO04 cXBUDI04 

    ‣ /44USFBN r "QQMF-JOVY0UIFST ‣ 4XJGU/*0 5$14PDLFU$POOFDUJPO 5$14PDLFU௨৴
  2. 5$14PDLFU$POOFDUJPO /FUXPSLGSBNFXPSL import Network let endpoint = NWEndpoint.hostPort( host: NWEndpoint.Host("198.51.100.50"),

    port: NWEndpoint.Port(integerLiteral: 445) ) let connection = NWConnection(to: endpoint, using: .tcp)
  3. 5$14PDLFU$POOFDUJPO /FUXPSLGSBNFXPSL import Network let endpoint = NWEndpoint.hostPort( host: NWEndpoint.Host("198.51.100.50"),

    port: NWEndpoint.Port(integerLiteral: 445) ) let connection = NWConnection(to: endpoint, using: .tcp)
  4. 5$14PDLFU$POOFDUJPO /FUXPSLGSBNFXPSL import Network let endpoint = NWEndpoint.hostPort( host: NWEndpoint.Host("198.51.100.50"),

    port: NWEndpoint.Port(integerLiteral: 445) ) let connection = NWConnection(to: endpoint, using: .tcp) connection.start(queue: .global(qos: .userInitiated))
  5. 5$14PDLFU$POOFDUJPO /FUXPSLGSBNFXPSL import Network let endpoint = NWEndpoint.hostPort( host: NWEndpoint.Host("198.51.100.50"),

    port: NWEndpoint.Port(integerLiteral: 445) ) let connection = NWConnection(to: endpoint, using: .tcp) connection.stateUpdateHandler = { (state) in } connection.start(queue: .global(qos: .userInitiated))
  6. 5$14PDLFU$POOFDUJPO /FUXPSLGSBNFXPSL import Network let endpoint = NWEndpoint.hostPort( host: NWEndpoint.Host("198.51.100.50"),

    port: NWEndpoint.Port(integerLiteral: 445) ) let connection = NWConnection(to: endpoint, using: .tcp) connection.stateUpdateHandler = { (state) in switch state { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: // TCP connection established @unknown default: break } } connection.start(queue: .global(qos: .userInitiated))
  7. 5$14PDLFU$POOFDUJPO /FUXPSLGSBNFXPSL import Network let endpoint = NWEndpoint.hostPort( host: NWEndpoint.Host("198.51.100.50"),

    port: NWEndpoint.Port(integerLiteral: 445) ) let connection = NWConnection(to: endpoint, using: .tcp) connection.stateUpdateHandler = { (state) in switch state { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: // TCP connection established @unknown default: break } } connection.start(queue: .global(qos: .userInitiated))
  8. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = ... connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated)) 5$14PDLFU$POOFDUJPO /FUXPSLGSBNFXPSL
  9. sudo tcpdump -p -i en7 -w smb.pcap host 198.51.100.50 1BDLFU$BQUVSF

    UDQEVNQ ύέοτΩϟϓνϟ͸ωοτϫʔΫΠϯλʔϑΣʔεʹΞΫηε ͢Δඞཁ͕͋ΔͷͰSPPUݖݶ͕ඞཁ
  10. sudo tcpdump -p -i en7 -w smb.pcap host 198.51.100.50 1BDLFU$BQUVSF

    UDQEVNQ ϓϩϛεΩϟεϞʔυʢ͢΂ͯͷύέοτΛΩϟϓνϟʣ ʹ͠ͳ͍
  11. sudo tcpdump -p -i en7 -w smb.pcap host 198.51.100.50 1BDLFU$BQUVSF

    UDQEVNQ ΩϟϓνϟΛߦ͏ωοτϫʔΫΠϯλʔϑΣʔε
  12. sudo tcpdump -p -i en7 -w smb.pcap host 198.51.100.50 1BDLFU$BQUVSF

    UDQEVNQ ϑΟϧλͷࢦఆɻͱͷૹड৴ύέοτͷΈ Ωϟϓνϟ͢Δ
  13. r #JH&OEJBO ‣ ࠷্ҐόΠτ͕ઌʹདྷΔ ‣ 0xfe534d42 fe 53 4d 42

    r -JUUMF&OEJBO ‣ ࠷ԼҐόΠτ͕ઌʹདྷΔ ‣ 0xfe534d42 42 4d 53 fe &OEJBOOFTT #ZUF0SEFS
  14. %BUB4USVDUVSF /BNF 4J[F 7BMVF 1SPUPDPM*E CZUFT 0x424D53FE 4USVDUVSF4J[F CZUFT 64

    $SFEJU$IBSHF CZUFT 0 4UBUVT CZUFT 0 $PNNBOE CZUFT 0x0000 (SMB2 NEGOTIATE) $SFEJU3FRVFTU CZUFT 0 'MBHT CZUFT 0x00000000 /FYU$PNNBOE CZUFT 0 ʜ ʜ ʜ 4.#1BDLFU)FBEFS
  15. %BUB4USVDUVSF /BNF 4J[F 7BMVF 1SPUPDPM*E CZUFT 0x424D53FE 4USVDUVSF4J[F CZUFT 64

    $SFEJU$IBSHF CZUFT 0 4UBUVT CZUFT 0 $PNNBOE CZUFT 0x0000 (SMB2 NEGOTIATE) $SFEJU3FRVFTU CZUFT 0 'MBHT CZUFT 0x00000000 /FYU$PNNBOE CZUFT 0 ʜ ʜ ʜ 4.#1BDLFU)FBEFS
  16. %BUB4USVDUVSF /BNF 4J[F 7BMVF 1SPUPDPM*E CZUFT 0x424D53FE 4USVDUVSF4J[F CZUFT 64

    $SFEJU$IBSHF CZUFT 0 4UBUVT CZUFT 0 $PNNBOE CZUFT 0x0000 (SMB2 NEGOTIATE) $SFEJU3FRVFTU CZUFT 0 'MBHT CZUFT 0x00000000 /FYU$PNNBOE CZUFT 0 ʜ ʜ ʜ 4.#1BDLFU)FBEFS
  17. %BUB4USVDUVSF /BNF 4J[F 7BMVF 1SPUPDPM*E CZUFT 0x424D53FE 4USVDUVSF4J[F CZUFT 64

    $SFEJU$IBSHF CZUFT 0 4UBUVT CZUFT 0 $PNNBOE CZUFT 0x0000 (SMB2 NEGOTIATE) $SFEJU3FRVFTU CZUFT 0 'MBHT CZUFT 0x00000000 /FYU$PNNBOE CZUFT 0 ʜ ʜ ʜ 4.#1BDLFU)FBEFS
  18. %BUB4USVDUVSF /BNF 4J[F 7BMVF 1SPUPDPM*E CZUFT 0x424D53FE 4USVDUVSF4J[F CZUFT 64

    $SFEJU$IBSHF CZUFT 0 4UBUVT CZUFT 0 $PNNBOE CZUFT 0x0000 (SMB2 NEGOTIATE) $SFEJU3FRVFTU CZUFT 0 'MBHT CZUFT 0x00000000 /FYU$PNNBOE CZUFT 0 ʜ ʜ ʜ 4.#1BDLFU)FBEFS
  19. %BUB4USVDUVSF /BNF 4J[F 7BMVF 4USVDUVSF4J[F CZUFT 36 %JBMFDU$PVOU CZUFT 2

    4FDVSJUZ.PEF CZUFT 0x0001 (SMB2_NEGOTIATE_SIGNING_ENABLED) 3FTFSWFE CZUFT 0 $BQBCJMJUJFT CZUFT 0x0000 $MJFOU(VJE CZUFT UUID $MJFOU4UBSU5JNF CZUFT 0 %JBMFDUT WBSJBCMF [0x0202, 0x0210] 1BEEJOH WBSJBCMF 00 00 00 00 4.#/&(05*"5&3FRVFTU
  20. %BUB4USVDUVSF /BNF 4J[F 7BMVF 4USVDUVSF4J[F CZUFT 36 %JBMFDU$PVOU CZUFT 2

    4FDVSJUZ.PEF CZUFT 0x0001 (SMB2_NEGOTIATE_SIGNING_ENABLED) 3FTFSWFE CZUFT 0 $BQBCJMJUJFT CZUFT 0x0000 $MJFOU(VJE CZUFT UUID $MJFOU4UBSU5JNF CZUFT 0 %JBMFDUT WBSJBCMF [0x0202, 0x0210] 1BEEJOH WBSJBCMF 00 00 00 00 4.#/&(05*"5&3FRVFTU
  21. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = ... connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated))
  22. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data([0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, ...]) connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated))
  23. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated))
  24. extension Data { init?(_ hex: String) { let len =

    hex.count / 2 var data = Data(capacity: len) for i in 0..<len { let j = hex.index(hex.startIndex, offsetBy: i * 2) let k = hex.index(j, offsetBy: 2) let bytes = hex[j..<k] if var num = UInt8(bytes, radix: 16) { data.append(&num, count: 1) } else { return nil } } self = data } var hex: String { return reduce("") { $0 + String(format: "%02x", $1) } } }
  25. extension Data { init?(_ hex: String) { let len =

    hex.count / 2 var data = Data(capacity: len) for i in 0..<len { let j = hex.index(hex.startIndex, offsetBy: i * 2) let k = hex.index(j, offsetBy: 2) let bytes = hex[j..<k] if var num = UInt8(bytes, radix: 16) { data.append(&num, count: 1) } else { return nil } } self = data } var hex: String { return reduce("") { $0 + String(format: "%02x", $1) } } }
  26. extension Data { init?(_ hex: String) { let len =

    hex.count / 2 var data = Data(capacity: len) for i in 0..<len { let j = hex.index(hex.startIndex, offsetBy: i * 2) let k = hex.index(j, offsetBy: 2) let bytes = hex[j..<k] if var num = UInt8(bytes, radix: 16) { data.append(&num, count: 1) } else { return nil } } self = data } var hex: String { return reduce("") { $0 + String(format: "%02x", $1) } } }
  27. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated)) let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send( content: data, completion: .contentProcessed() { (error) in ... } )
  28. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated)) let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send( content: data, completion: .contentProcessed() { (error) in ... } )
  29. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated)) let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send( content: data, completion: .contentProcessed() { (error) in ... } )
  30. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated)) let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send( content: data, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } } )
  31. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated)) connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) }
  32. import Network ... connection.stateUpdateHandler = { (state) in switch state

    { case .setup, .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated)) connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) }
  33. public struct Header { public let protocolId: UInt32 public let

    structureSize: UInt16 public let creditCharge: UInt16 public let status: UInt32 public let command: UInt16 public let creditRequestResponse: UInt16 public let flags: Flags public let nextCommand: UInt32 public let messageId: UInt64 public let reserved: UInt32 public let treeId: UInt32 public let sessionId: UInt64 public let signature: Data } $POTUSVDU4.#%BUB4USVDUVSF 4.#1BDLFU)FBEFS
  34. let header = Header( creditCharge: 1, command: .negotiate, creditRequest: 0,

    flags: [], messageId: 0, treeId: 0, sessionId: 0 ) $POTUSVDU4.#%BUB4USVDUVSF 4.#1BDLFU)FBEFS
  35. let header = Header( creditCharge: 1, command: .negotiate, creditRequest: 0,

    flags: [], messageId: 0, treeId: 0, sessionId: 0 ) $POTUSVDU4.#%BUB4USVDUVSF 4.#1BDLFU)FBEFS
  36. let header = Header( creditCharge: 1, command: .negotiate, creditRequest: 0,

    flags: [], messageId: 0, treeId: 0, sessionId: 0 ) $POTUSVDU4.#%BUB4USVDUVSF 4.#1BDLFU)FBEFS
  37. public enum Command: UInt16 { case negotiate = 0x0000 case

    sessionSetup = 0x0001 case logoff = 0x0002 case treeConnect = 0x0003 case treeDisconnect = 0x0004 case create = 0x0005 case close = 0x0006 case flush = 0x0007 case read = 0x0008 case write = 0x0009 case lock = 0x000A case ioctl = 0x000B case cancel = 0x000C case echo = 0x000D case queryDirectory = 0x000E case changeNotify = 0x000F case queryInfo = 0x0010 case setInfo = 0x0011 case oplockBreak = 0x0012 case serverToClientNotification = 0x0013 }
  38. let header = Header( creditCharge: 1, command: .negotiate, creditRequest: 0,

    flags: [], messageId: 0, treeId: 0, sessionId: 0 ) $POTUSVDU4.#%BUB4USVDUVSF 4.#1BDLFU)FBEFS
  39. let header = Header( creditCharge: 1, command: .negotiate, creditRequest: 0,

    flags: [], messageId: 0, treeId: 0, sessionId: 0 ) $POTUSVDU4.#%BUB4USVDUVSF 4.#1BDLFU)FBEFS
  40. public struct Flags: OptionSet, Sendable { public let rawValue: UInt32

    public init(rawValue: UInt32) { self.rawValue = rawValue } public static let serverToRedir = Flags(rawValue: 0x00000001) public static let asyncCommand = Flags(rawValue: 0x00000002) public static let relatedOperations = Flags(rawValue: 0x00000004) public static let signed = Flags(rawValue: 0x00000008) public static let priorityMask = Flags(rawValue: 0x00000070) public static let dfsOperation = Flags(rawValue: 0x10000000) public static let replayOperation = Flags(rawValue: 0x20000000) }
  41. public struct NegotiateRequest { public let header: Header public let

    structureSize: UInt16 public let dialectCount: UInt16 public let securityMode: SecurityMode public let reserved: UInt16 public let capabilities: Capabilities public let clientGuid: UUID public let clientStartTime: UInt64 public let dialects: [Dialects] public let padding: Data public let negotiateContextList: Data } 4.#/&(05*"5&3FRVFTU $POTUSVDU4.#%BUB4USVDUVSF
  42. let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202,

    .smb210] ) // => fe534d42400000000000000000000000... 4.#/&(05*"5&3FRVFTU $POTUSVDU4.#%BUB4USVDUVSF
  43. public func encoded() -> Data { var data = Data()

    var protocolId = protocolId data += withUnsafeBytes(of: &protocolId) { Data($0) } var structureSize = structureSize data += withUnsafeBytes(of: &structureSize) { Data($0) } var creditCharge = creditCharge data += withUnsafeBytes(of: &creditCharge) { Data($0) } var status = status data += withUnsafeBytes(of: &status) { Data($0) } ... 4FSJBMJ[F4.#1BDLFU 4.#1BDLFU)FBEFS
  44. extension Data { init<T>(from value: T) { var value =

    value self = Swift.withUnsafeBytes(of: &value) { Data($0) } } } 4FSJBMJ[F4.#1BDLFU *OUFHFSUP%BUB
  45. protocol BinaryConvertible { static func +(lhs: Data, rhs: Self) ->

    Data static func +=(lhs: inout Data, rhs: Self) } extension BinaryConvertible { static func +(lhs: Data, rhs: Self) -> Data { lhs + Data(from: rhs) } static func +=(lhs: inout Data, rhs: Self) { lhs = lhs + rhs } } 4FSJBMJ[F4.#1BDLFU *OUFHFSUP%BUB
  46. extension UInt8: BinaryConvertible {} extension UInt16: BinaryConvertible {} extension UInt32:

    BinaryConvertible {} extension UInt64: BinaryConvertible {} 4FSJBMJ[F4.#1BDLFU *OUFHFSUP%BUB
  47. public func encoded() -> Data { var data = Data()

    data += protocolId data += structureSize data += creditCharge data += status data += command data += creditRequestResponse data += flags.rawValue data += nextCommand data += messageId data += reserved data += treeId data += sessionId data += signature return data }
  48. public func encoded() -> Data { var data = Data()

    data += protocolId data += structureSize data += creditCharge data += status data += command data += creditRequestResponse data += flags.rawValue data += nextCommand data += messageId data += reserved data += treeId data += sessionId data += signature return data }
  49. public func encoded() -> Data { var data = Data()

    data += header.encoded() data += structureSize data += UInt16(dialects.count) data += securityMode.rawValue data += reserved data += capabilities.rawValue data += Data(from: clientGuid) data += clientStartTime for dialect in dialects { data += dialect.rawValue } data += padding data += negotiateContextList return data }
  50. connection.stateUpdateHandler = { (state) in switch state { case .setup,

    .waiting, .preparing, .failed, .cancelled: break case .ready: let data = Data("00000100fe534d4240000000...6e6f7265")! connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated))
  51. connection.stateUpdateHandler = { (state) in switch state { case .setup,

    .waiting, .preparing, .failed, .cancelled: break case .ready: let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() connection.send(content: data, completion: .contentProcessed() { (error) in ... }) @unknown default: break } } connection.start(queue: .global(qos: .userInitiated))
  52. public struct DirectTCPPacket { public let zero: UInt8 public let

    streamProtocolLength: Data public let smb2Message: Data public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0x000000FF) let byte2 = UInt8((length >> 8) & 0x000000FF) let byte3 = UInt8(length & 0x000000FF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message } public func encoded() -> Data { var data = Data() data += zero data += streamProtocolLength data += smb2Message return data } }
  53. public struct DirectTCPPacket { public let zero: UInt8 public let

    streamProtocolLength: Data public let smb2Message: Data public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0xFF) let byte2 = UInt8((length >> 8) & 0xFF) let byte3 = UInt8(length & 0xFF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message } public func encoded() -> Data { var data = Data() data += zero data += streamProtocolLength data += smb2Message return data } } public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0x000000FF) let byte2 = UInt8((length >> 8) & 0x000000FF) let byte3 = UInt8(length & 0x000000FF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message }
  54. public struct DirectTCPPacket { public let zero: UInt8 public let

    streamProtocolLength: Data public let smb2Message: Data public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0xFF) let byte2 = UInt8((length >> 8) & 0xFF) let byte3 = UInt8(length & 0xFF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message } public func encoded() -> Data { var data = Data() data += zero data += streamProtocolLength data += smb2Message return data } } public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0x000000FF) let byte2 = UInt8((length >> 8) & 0x000000FF) let byte3 = UInt8(length & 0x000000FF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message }
  55. public struct DirectTCPPacket { public let zero: UInt8 public let

    streamProtocolLength: Data public let smb2Message: Data public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0xFF) let byte2 = UInt8((length >> 8) & 0xFF) let byte3 = UInt8(length & 0xFF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message } public func encoded() -> Data { var data = Data() data += zero data += streamProtocolLength data += smb2Message return data } } public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0x000000FF) let byte2 = UInt8((length >> 8) & 0x000000FF) let byte3 = UInt8(length & 0x000000FF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message }
  56. #JU.BTL#JU4IJGU #ZUFXJTF0QFSBUJPO 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 UInt8(length & 0x000000FF)
  57. #JU.BTL#JU4IJGU #ZUFXJTF0QFSBUJPO 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 UInt8(length & 0x000000FF) ϏοτϚεΫ
  58. #JU.BTL#JU4IJGU #ZUFXJTF0QFSBUJPO 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 UInt8(length & 0x000000FF) ԼҐόΠτ͕࢒Δ ্ҐͷϏοτ͸θϩʹͳΔ
  59. #JU.BTL#JU4IJGU #ZUFXJTF0QFSBUJPO 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 UInt8((length >> 8) & 0x000000FF)
  60. #JU.BTL#JU4IJGU #ZUFXJTF0QFSBUJPO 0 0 0 0 0 0 0 0

    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 UInt8((length >> 8) & 0x000000FF) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  61. public struct DirectTCPPacket { public let zero: UInt8 public let

    streamProtocolLength: Data public let smb2Message: Data public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0xFF) let byte2 = UInt8((length >> 8) & 0xFF) let byte3 = UInt8(length & 0xFF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message } public func encoded() -> Data { var data = Data() data += zero data += streamProtocolLength data += smb2Message return data } } public init(smb2Message: Data) { zero = 0x00 let length = UInt32(truncatingIfNeeded: smb2Message.count) var data = Data(capacity: 3) let byte1 = UInt8((length >> 16) & 0x000000FF) let byte2 = UInt8((length >> 8) & 0x000000FF) let byte3 = UInt8(length & 0x000000FF) data.append(byte1) data.append(byte2) data.append(byte3) streamProtocolLength = data self.smb2Message = smb2Message }
  62. connection.stateUpdateHandler = { (state) in switch state { case .setup,

    .waiting, .preparing, .failed, .cancelled: break case .ready: let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } }) @unknown default: break } }
  63. connection.stateUpdateHandler = { (state) in switch state { case .setup,

    .waiting, .preparing, .failed, .cancelled: break case .ready: let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } }) @unknown default: break } } let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } })
  64. connection.stateUpdateHandler = { (state) in switch state { case .setup,

    .waiting, .preparing, .failed, .cancelled: break case .ready: let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } }) @unknown default: break } } let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } })
  65. connection.stateUpdateHandler = { (state) in switch state { case .setup,

    .waiting, .preparing, .failed, .cancelled: break case .ready: let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } }) @unknown default: break } } let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } })
  66. connection.stateUpdateHandler = { (state) in switch state { case .setup,

    .waiting, .preparing, .failed, .cancelled: break case .ready: let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } }) @unknown default: break } } let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202, .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } })
  67. extension Data { init<T>(from value: T) { var value =

    value self = Swift.withUnsafeBytes(of: &value) { Data($0) } } func to<T>(type: T.Type) -> T { return self.withUnsafeBytes { $0.load(as: T.self) } } } %FTFSJBMJ[F4.#1BDLFU #JOBSZUP4USVDUVSF
  68. class ByteReader { private let data: Data private(set) var offset:

    Data.Index var availableBytes: Int { return data.count - offset } init(_ data: Data) { self.data = data offset = data.startIndex } func read<T>() -> T { let size = MemoryLayout<T>.size let value = data[offset..<(offset + size)].to(type: T.self) offset += size return value } }
  69. public struct NegotiateResponse { public let header: Header public let

    structureSize: UInt16 public let securityMode: SecurityMode public let dialectRevision: UInt16 public let negotiateContextCount: UInt16 public let serverGuid: UUID public let capabilities: Capabilities public let maxTransactSize: UInt32 public let maxReadSize: UInt32 public let maxWriteSize: UInt32 public let systemTime: UInt64 public let serverStartTime: UInt64 public let securityBufferOffset: UInt16 public let securityBufferLength: UInt16 public let negotiateContextOffset: UInt32 public let securityBuffer: Data }
  70. public init(data: Data) { let reader = ByteReader(data) header =

    reader.read() structureSize = reader.read() securityMode = SecurityMode(rawValue: reader.read()) dialectRevision = reader.read() negotiateContextCount = reader.read() serverGuid = reader.read() capabilities = Capabilities(rawValue: reader.read()) maxTransactSize = reader.read() maxReadSize = reader.read() maxWriteSize = reader.read() systemTime = reader.read() serverStartTime = reader.read() securityBufferOffset = reader.read() securityBufferLength = reader.read() negotiateContextOffset = reader.read() securityBuffer = reader.read( from: Int(securityBufferOffset), count: Int(securityBufferLength) ) }
  71. let negotiateRequest = NegotiateRequest( messageId: 0, securityMode: [.signingEnabled], dialects: [.smb202,

    .smb210] ) let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } print(content.hex) } })
  72. let data = negotiateRequest.encoded() let transportPacket = DirectTCPPacket(smb2Message: data) let

    packet = transportPacket.encoded() connection.send(content: packet, completion: .contentProcessed() { (error) in connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (content, contentContext, isComplete, error) in guard let content else { return } let transportPacket = DirectTCPPacket(response: content) let length = Int(transportPacket.protocolLength) let response = NegotiateResponse(data: transportPacket.smb2Message) print(response) } })
  73. extension NegotiateResponse: CustomDebugStringConvertible { public var debugDescription: String { """

    \(header) Negotiate Protocol Response (\(String(format: "0x%02x", header.command))) StructureSize: \(structureSize) Security mode: \(securityMode) Dialect: \(String(format: "0x%04x", dialectRevision)) NegotiateContextCount: \(negotiateContextCount) Server Guid: \(serverGuid) Capabilities: \(capabilities) Max Transaction Size: \(maxTransactSize) Max Read Size: \(maxReadSize) Max Write Size: \(maxWriteSize) Current Time: \(FileTime(systemTime)) Boot Time: \(FileTime(serverStartTime)) Blob Offset: \(securityBufferOffset) Blob Length: \(securityBufferLength) Security Blob: \(securityBuffer.hex) NegotiateContextOffset: \(String(format: "0x%08x", negotiateContextOffset)) """ } }
  74. r /&(05*"5& r 4&44*0/@4&561 r -0(0'' r 53&&@$0//&$5 r 53&&@%*4$0//&$5

    r $3&"5& r $-04& r '-64) r 3&"% 4.#.FTTBHFT r 83*5& r -0$, r &$)0 r $"/$&- r *0$5- r 26&3:@%*3&$503: r $)"/(&@/05*': r 26&3:@*/'0 r 4&5@*/'0
  75. r /&(05*"5& r 4&44*0/@4&561 r -0(0'' r 53&&@$0//&$5 r 53&&@%*4$0//&$5

    r $3&"5& r $-04& r '-64) r 3&"% r 83*5& r -0$, r &$)0 r $"/$&- r *0$5- r 26&3:@%*3&$503: r $)"/(&@/05*': r 26&3:@*/'0 r 4&5@*/'0 ϩάΠϯ ϩάΦϑ μ΢ϯϩʔυ Ξοϓϩʔυ σΟϨΫτϦҰཡ 4.#.FTTBHFT
  76. -JTU%JSFDUPSZ /BNF 4J[F 7BMVF 4USVDUVSF4J[F CZUFT 33 'JMF*OGPSNBUJPO$MBTT CZUFT Ϩεϙϯεʹؚ·ΕΔ৘ใ

    'MBHT CZUFT 0x01 'JMF*OEFY CZUFT 0 'JMF*E CZUFT ਌σΟϨΫτϦͷ*% 'JMF/BNF0GGTFU CZUFT ݕࡧύλʔϯͷ 'JMF/BNF-FOHUI CZUFT ݕࡧύλʔϯจࣈྻͷόΠτ਺ 0VUQVU#VGGFS-FOHUI CZUFT ҰճͰฦͬͯ͘ΔϨεϙϯεͷ࠷େαΠζ #VGGFS WBSJBCMF ݕࡧύλʔϯจࣈྻʢྫʣ* 4.#26&3:@%*3&$503:3FRVFTU
  77. public class Connection { let host: String var onDisconnected: (Error)

    -> Void private let connection: NWConnection public func connect() async throws { return try await withCheckedThrowingContinuation { (continuation) in connection.stateUpdateHandler = { (state) in switch state { ... case .ready: continuation.resume() self.connection.stateUpdateHandler = stateUpdateHandler case .failed(let error): continuation.resume(throwing: error) self.connection.stateUpdateHandler = nil ... } } connection.start(queue: .global(qos: .userInitiated)) } } }
  78. public class Connection { let host: String var onDisconnected: (Error)

    -> Void private let connection: NWConnection public func connect() async throws { return try await withCheckedThrowingContinuation { (continuation) in connection.stateUpdateHandler = { (state) in switch state { ... case .ready: continuation.resume() self.connection.stateUpdateHandler = stateUpdateHandler case .failed(let error): continuation.resume(throwing: error) self.connection.stateUpdateHandler = nil ... } } connection.start(queue: .global(qos: .userInitiated)) } } }
  79. public class Connection { let host: String var onDisconnected: (Error)

    -> Void private let connection: NWConnection public func connect() async throws { return try await withCheckedThrowingContinuation { (continuation) in connection.stateUpdateHandler = { (state) in switch state { ... case .ready: continuation.resume() self.connection.stateUpdateHandler = stateUpdateHandler case .failed(let error): continuation.resume(throwing: error) self.connection.stateUpdateHandler = nil ... } } connection.start(queue: .global(qos: .userInitiated)) } } }
  80. public class Connection { let host: String var onDisconnected: (Error)

    -> Void private let connection: NWConnection public func connect() async throws { return try await withCheckedThrowingContinuation { (continuation) in connection.stateUpdateHandler = { (state) in switch state { ... case .ready: continuation.resume() self.connection.stateUpdateHandler = stateUpdateHandler case .failed(let error): continuation.resume(throwing: error) self.connection.stateUpdateHandler = nil ... } } connection.start(queue: .global(qos: .userInitiated)) } } }
  81. public func send(_ data: Data) async throws -> Data {

    let transportPacket = DirectTCPPacket(smb2Message: data) let content = transportPacket.encoded() return try await withCheckedThrowingContinuation { (continuation) in connection.send(content: content, completion: .contentProcessed() { (error) in if let error { continuation.resume(throwing: error) return } connection.receive( minimumIncompleteLength: 0, maximumLength: 65536) { (content, contentContext, isComplete, error) in if let error = error { continuation.resume(throwing: error) return } continuation.resume(returning: content) } }) } }
  82. public func send(_ data: Data) async throws -> Data {

    let transportPacket = DirectTCPPacket(smb2Message: data) let content = transportPacket.encoded() return try await withCheckedThrowingContinuation { (continuation) in connection.send(content: content, completion: .contentProcessed() { (error) in if let error { continuation.resume(throwing: error) return } connection.receive( minimumIncompleteLength: 0, maximumLength: 65536) { (content, contentContext, isComplete, error) in if let error = error { continuation.resume(throwing: error) return } continuation.resume(returning: content) } }) } }
  83. public func send(_ data: Data) async throws -> Data {

    let transportPacket = DirectTCPPacket(smb2Message: data) let content = transportPacket.encoded() return try await withCheckedThrowingContinuation { (continuation) in connection.send(content: content, completion: .contentProcessed() { (error) in if let error { continuation.resume(throwing: error) return } connection.receive( minimumIncompleteLength: 0, maximumLength: 65536) { (content, contentContext, isComplete, error) in if let error = error { continuation.resume(throwing: error) return } continuation.resume(returning: content) } }) } }
  84. private func receive(completion: @escaping (Result<Data, Error>) -> Void) { connection.receive(

    minimumIncompleteLength: 0, maximumLength: 65536) { (content, contentContext, isComplete, error) in if let error = error { completion(.failure(error)) return } let transportPacket = DirectTCPPacket(response: content) let length = Int(transportPacket.protocolLength) self.buffer.append(Data(transportPacket.smb2Message)) self.receive(upTo: length) { (result) in switch result { case .success: let data = Data(self.buffer.prefix(length)) self.buffer = Data(self.buffer.suffix(from: length)) completion(.success(data)) case .failure(let error): completion(.failure(error)) } } } }
  85. private func receive(upTo byteCount: Int, completion: @escaping (Result<(), Error>) ->

    Void) { if self.buffer.count < byteCount { self.connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (data, _, isComplete, error) in if let error = error { completion(.failure(error)) return } guard let data else { if isComplete { completion(.failure(ConnectionError.disconnected)) } else { completion(.failure(ConnectionError.noData)) } return } self.buffer.append(data) self.receive(upTo: byteCount, completion: completion) } return } completion(.success(())) }
  86. private func receive(upTo byteCount: Int, completion: @escaping (Result<(), Error>) ->

    Void) { if self.buffer.count < byteCount { self.connection.receive( minimumIncompleteLength: 0, maximumLength: 65536 ) { (data, _, isComplete, error) in if let error = error { completion(.failure(error)) return } guard let data else { if isComplete { completion(.failure(ConnectionError.disconnected)) } else { completion(.failure(ConnectionError.noData)) } return } self.buffer.append(data) self.receive(upTo: byteCount, completion: completion) } return } completion(.success(())) }
  87. return try await withCheckedThrowingContinuation { (continuation) in connection.send(content: content, completion:

    .contentProcessed() { (error) in if let error { continuation.resume(throwing: error) return } self.receive() { (result) in switch result { case .success(let data): continuation.resume(returning: data) case .failure(let error): continuation.resume(throwing: error) } } }) } 4XJGU$PODVSSFODZ "TZOD"XBJU
  88. public class Session { private var messageId = SequenceNumber<UInt64>() private

    let connection: Connection public init(host: String, port: Int) { connection = Connection(host: host, port: port) } public func connect() async throws { try await connection.connect() } public func negotiate( securityMode: Negotiate.SecurityMode = [.signingEnabled], dialects: [Negotiate.Dialects] = [.smb202, .smb210] ) async throws -> Negotiate.Response { let request = Negotiate.Request( messageId: messageId.next(), securityMode: securityMode, dialects: dialects ) let data = try await send(request.encoded()) let response = Negotiate.Response(data: data) return response } public func sessionSetup() async throws -> SessionSetup.Response { ... } }
  89. public class Session { private var messageId = SequenceNumber<UInt64>() private

    let connection: Connection public init(host: String, port: Int) { connection = Connection(host: host, port: port) } public func connect() async throws { try await connection.connect() } public func negotiate( securityMode: Negotiate.SecurityMode = [.signingEnabled], dialects: [Negotiate.Dialects] = [.smb202, .smb210] ) async throws -> Negotiate.Response { let request = Negotiate.Request( messageId: messageId.next(), securityMode: securityMode, dialects: dialects ) let data = try await send(request.encoded()) let response = Negotiate.Response(data: data) return response } public func sessionSetup() async throws -> SessionSetup.Response { ... } }
  90. public class Session { private var messageId = SequenceNumber<UInt64>() private

    let connection: Connection public init(host: String, port: Int) { connection = Connection(host: host, port: port) } public func connect() async throws { try await connection.connect() } public func negotiate( securityMode: Negotiate.SecurityMode = [.signingEnabled], dialects: [Negotiate.Dialects] = [.smb202, .smb210] ) async throws -> Negotiate.Response { let request = Negotiate.Request( messageId: messageId.next(), securityMode: securityMode, dialects: dialects ) let data = try await send(request.encoded()) let response = Negotiate.Response(data: data) return response } public func sessionSetup() async throws -> SessionSetup.Response { ... } }
  91. public class Session { private var messageId = SequenceNumber<UInt64>() private

    let connection: Connection public init(host: String, port: Int) { connection = Connection(host: host, port: port) } public func connect() async throws { try await connection.connect() } public func negotiate( securityMode: Negotiate.SecurityMode = [.signingEnabled], dialects: [Negotiate.Dialects] = [.smb202, .smb210] ) async throws -> Negotiate.Response { let request = Negotiate.Request( messageId: messageId.next(), securityMode: securityMode, dialects: dialects ) let data = try await connection.send(request.encoded()) let response = Negotiate.Response(data: data) return response } public func sessionSetup() async throws -> SessionSetup.Response { ... } }
  92. public class Session { private var messageId = SequenceNumber<UInt64>() private

    let connection: Connection public init(host: String, port: Int) { connection = Connection(host: host, port: port) } public func connect() async throws { try await connection.connect() } public func negotiate( securityMode: Negotiate.SecurityMode = [.signingEnabled], dialects: [Negotiate.Dialects] = [.smb202, .smb210] ) async throws -> Negotiate.Response { let request = Negotiate.Request( messageId: messageId.next(), securityMode: securityMode, dialects: dialects ) let data = try await connection.send(request.encoded()) let response = Negotiate.Response(data: data) return response } public func sessionSetup() async throws -> SessionSetup.Response { ... } }
  93. public class Session { private var messageId = SequenceNumber<UInt64>() private

    let connection: Connection public init(host: String, port: Int) { connection = Connection(host: host, port: port) } public func connect() async throws { try await connection.connect() } public func negotiate( securityMode: Negotiate.SecurityMode = [.signingEnabled], dialects: [Negotiate.Dialects] = [.smb202, .smb210] ) async throws -> Negotiate.Response { let request = Negotiate.Request( messageId: messageId.next(), securityMode: securityMode, dialects: dialects ) let data = try await connection.send(request.encoded()) let response = Negotiate.Response(data: data) return response } public func sessionSetup() async throws -> SessionSetup.Response { ... } }
  94. public class Session { private var messageId = SequenceNumber<UInt64>() private

    let connection: Connection public init(host: String, port: Int) { connection = Connection(host: host, port: port) } public func connect() async throws { try await connection.connect() } public func negotiate( securityMode: Negotiate.SecurityMode = [.signingEnabled], dialects: [Negotiate.Dialects] = [.smb202, .smb210] ) async throws -> Negotiate.Response { let request = Negotiate.Request( messageId: messageId.next(), securityMode: securityMode, dialects: dialects ) let data = try await connection.send(request.encoded()) let response = Negotiate.Response(data: data) return response } public func sessionSetup() async throws -> SessionSetup.Response { ... } }
  95. import XCTest @testable import SMBClient final class SessionTests: XCTestCase {

    func testNegotiate() async throws { let session = Session(host: "198.51.100.50", port: 445) try await session.connect() let response = try await session.negotiate() ... } 6OJU5FTU%SJWFO 9$5FTU
  96. /BNF 4J[F 7BMVF 4USVDUVSF4J[F CZUFT 49 1BEEJOH CZUFT 'MBHT CZUFT

    -FOHUI CZUFT 65536 0GGTFU CZUFT 131072 'JMF*E CZUFT   3FBEJOH'JMF 4.#3&"%3FRVFTU
  97. 4USFBNJOH7JEFP'JMF "7"TTFU3FTPVSDF-PBEFS%FMFHBUF func resourceLoader( _ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest

    ) -> Bool { ... if let dataRequest = loadingRequest.dataRequest { let fileSize = try await fileReader.fileSize let data = try await fileReader.read( offset: UInt64(dataRequest.requestedOffset), length: UInt32(truncatingIfNeeded: length) ) dataRequest.respond(with: data) loadingRequest.finishLoading() } return true }
  98. 3FGFSFODFT r 4BNQMF$PEF IUUQTHJUIVCDPNLJTIJLBXBLBUTVNJ4.#$MJFOU r <.44.#>4FSWFS.FTTBHF#MPDL 4.# 1SPUPDPM7FSTJPOTBOE IUUQTMFBSONJDSPTPGUDPNFOVTPQFOTQFDTXJOEPXT@QSPUPDPMTNTTNCBEFFBFD r

    <.4/-.1>/5-"/.BOBHFS /5-. "VUIFOUJDBUJPO1SPUPDPM IUUQTMFBSONJDSPTPGUDPNFOVTPQFOTQFDTXJOEPXT@QSPUPDPMTNTOMNQCDFEBGGEEF r %$&3FNPUF1SPDFEVSF$BMM31$1%6&ODPEJOHT IUUQTQVCTPQFOHSPVQPSHPOMJOFQVCTDIBQIUN