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

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

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

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

Avatar for Kishikawa Katsumi

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