Skip to content

Commit

Permalink
Listing shares
Browse files Browse the repository at this point in the history
  • Loading branch information
amosavian committed May 25, 2018
1 parent 7d61a58 commit 70bba0a
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 6 deletions.
242 changes: 236 additions & 6 deletions AMSMB2/AMSMB2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ public typealias SimpleCompletionHandler = ((_ error: Error?) -> Void)?
public class AMSMB2: NSObject {
fileprivate var context: SMB2Context
private let url: SMB2URL
private let _domain: String
private let _workstation: String
private let _user: String
private let _server: String
private let _password: String
private let q: DispatchQueue

/**
Expand All @@ -32,12 +35,10 @@ public class AMSMB2: NSObject {
guard let context = SMB2Context(), let smburl = SMB2URL(url.absoluteString, on: context), let host = url.host else {
return nil
}

self.context = context
self.url = smburl
let hostLabel = url.host.map({ "_" + $0 }) ?? ""
self.q = DispatchQueue(label: "smb2_queue\(hostLabel)", qos: .default, attributes: [])
context.set(securityMode: .enabled)

var workstation: String = ""
var user: String = "guest"
Expand All @@ -59,11 +60,12 @@ public class AMSMB2: NSObject {
}
}
_server = host
context.set(domain: domain)
context.set(workstation: workstation)
context.set(user: user)
_domain = domain
_workstation = workstation
_user = user
context.set(password: credential?.password ?? "")
_password = credential?.password ?? ""
super.init()
initContext(self.context)
}

/**
Expand Down Expand Up @@ -97,6 +99,52 @@ public class AMSMB2: NSObject {
}
}

@objc
public func listShares(completionHandler: @escaping (_ names: [String], _ comments: [String], _ error: Error?) -> Void) {
q.async {
do {
let server = self.url.server ?? self._server
guard let context = SMB2Context() else {
throw POSIXError(.EHOSTUNREACH)
}
self.initContext(context)

try context.connect(server: server, share: "IPC$", user: self._user)
defer {
try? context.disconnect()
}

let srvsvc = try SMB2FileHanle(forUpdatingAtPath: "srvsvc", on: context)

_ = try srvsvc.write(data: self.listSrvsvcBindData())
let recvBindData = try srvsvc.read()
if recvBindData.count < 68 {
try POSIXError.throwIfError(Int32.min, description: "Binding failure", default: .EBADMSG)
}

if recvBindData[44] > 0 {
try POSIXError.throwIfError(Int32.min, description:
"Binding failure: \(String(format: "%02x", recvBindData[68])):\(String(format: "%02x", recvBindData[69]))",
default: .EBADMSG)
}

let serverName = String(utf8String: context.context.pointee.server)!
_ = try srvsvc.write(data: self.listSrvsvcRequestData(server: serverName))
let recvData = try srvsvc.read()
let sharesArray = recvData.dropFirst(48)
guard let sharesCount: UInt32 = recvData.scanValue(start: 44) else {
throw POSIXError(.EBADMSG)
}
let shares = self.parseShares(count: Int(sharesCount), data: sharesArray)
completionHandler(shares.map({ $0.name }), shares.map({ $0.comment }), nil)
} catch {
completionHandler([], [], error)
}
}

}


/**
Enumerates directory contents in the give path
Expand Down Expand Up @@ -605,6 +653,14 @@ public class AMSMB2: NSObject {
}

extension AMSMB2 {
fileprivate func initContext(_ context: SMB2Context) {
context.set(securityMode: .enabled)
context.set(domain: _domain)
context.set(workstation: _workstation)
context.set(user: _user)
context.set(password: _password)
}

fileprivate func populateResourceValue(_ dic: inout [URLResourceKey: Any], stat: smb2_stat_64) {
dic[.fileSizeKey] = NSNumber(value: stat.smb2_size)
dic[.fileResourceTypeKey] = stat.smb2_type == SMB2_TYPE_DIRECTORY ? URLFileResourceType.directory : URLFileResourceType.regular
Expand Down Expand Up @@ -658,4 +714,178 @@ extension AMSMB2 {
try fileWrite.fsync()
return shouldContinue
}

func parseShares(count: Int, data: Data) -> [(name: String, comment: String)] {
var shares = [(name: String, comment: String)]()

/*
Data Layout :
struct referant {
uint32 name;
uint32 type;
uint32 comment;
}
struct nameString {
uint32 maxCount;
uint32 offset;
uint32 actualCount;
char* name; // utf16 string with actualCount - 1 unicode scalars
}
*/

func typeOffset(_ i: Int) -> Int {
return i * 12 + 4
}

// start of nameString structs
var offset = count * 12
for i in 0..<count {
let type: UInt32 = data.scanValue(start: typeOffset(i)) ?? 0xffffffff

// Parse name part
guard let nameActualCount_32: UInt32 = data.scanValue(start: offset + 8) else {
break
}

offset += 12

let nameActualCount = Int(nameActualCount_32)
let nameStringData = data.dropFirst(offset).prefix((nameActualCount - 1) * 2)
let nameString = nameActualCount > 1 ? (String(data: nameStringData, encoding: .utf16LittleEndian) ?? "") : ""

offset += nameActualCount * 2
if nameActualCount % 2 == 1 {
offset += 2
}

// Parse comment part
guard let commentActualCount_32: UInt32 = data.scanValue(start: offset + 8) else {
break
}

offset += 12

let commentActualCount = Int(commentActualCount_32)
let commentStringData = data.dropFirst(offset).prefix((commentActualCount - 1) * 2)
let commentString = commentActualCount > 1 ? (String(data: commentStringData, encoding: .utf16LittleEndian) ?? "") : ""

offset += commentActualCount * 2

if commentActualCount % 2 == 1 {
offset += 2
}

if type == 0 {
// type is STYPE_DISKTREE
shares.append((name: nameString, comment: commentString))
}
}

return shares
}

private func listSrvsvcBindData() -> Data {
var reqData = Data()
// Version major, version minor, packet type = 'bind', packet flags
reqData.append(contentsOf: [0x05, 0, 0x0b, 0x03])
// Representation = little endian/ASCII.
reqData.append(uint32: 0x10)
// data length
reqData.append(uint16: 72)
// Auth len
reqData.append(uint16: 0)
// Call ID
reqData.append(uint32: 1)
// Max Xmit size
reqData.append(uint16: UInt16.max)
// Max Recv size
reqData.append(uint16: UInt16.max)
// Assoc group
reqData.append(uint32: 0)
// Num Ctx Item
reqData.append(uint32: 1)
// ContextID
reqData.append(uint16: 0)
// Num Trans Items
reqData.append(uint16: 1)
// SRVSVC UUID
let srvsvcUuid = UUID(uuidString: "4b324fc8-1670-01d3-1278-5a47bf6ee188")!
reqData.append(uuid: srvsvcUuid)
// Version major, version minor
reqData.append(uint16: 3)
reqData.append(uint16: 0)
// NDRv2 UUID
let ndruuid = UUID(uuidString: "8a885d04-1ceb-11c9-9fe8-08002b104860")!
reqData.append(uuid: ndruuid)
// Another version
reqData.append(uint16: 2)
reqData.append(uint16: 0)

return reqData
}

private func listSrvsvcRequestData(server serverName: String) -> Data {
let serverNameData = serverName.data(using: .utf16LittleEndian)!
let serverNameLen = UInt32(serverName.count + 1)

var reqData = Data()
// Version major, version minor, packet type = 'request', packet flags
reqData.append(contentsOf: [0x05, 0, 0x00, 0x03])
// Representation = little endian/ASCII.
reqData.append(uint32: 0x10)
// data length, set later
reqData.append(uint16: 0)
// Auth len
reqData.append(uint16: 0)
// Call ID
reqData.append(uint32: 0)
// Alloc hint
reqData.append(uint32: 72)
// Context ID
reqData.append(uint16: 0)
// OpNum = NetShareEnumAll
reqData.append(uint16: 0x0f)

// Pointer to server UNC
// Referent ID
reqData.append(uint32: 1)
// Max count
reqData.append(uint32: serverNameLen)
// Offset
reqData.append(uint32: 0)
// Max count
reqData.append(uint32: serverNameLen)

// The server name
reqData.append(serverNameData)
reqData.append(uint16: 0) // null termination padding
if serverNameLen % 2 == 1 {
reqData.append(uint16: 0) // null termination padding
}

// Level 1
reqData.append(uint32: 1)
// Ctr
reqData.append(uint32: 1)
// Referent ID
reqData.append(uint32: 1)
// Count/Null Pointer to NetShareInfo1
reqData.append(uint32: 0)
// Null Pointer to NetShareInfo1
reqData.append(uint32: 0)
// Max Buffer (0xffffffff required by smbX)
reqData.append(uint32: 0xffffffff)
// Referent ID
reqData.append(uint32: 1)
// Resume
reqData.append(uint32: 0)

let reqDataCount = reqData.count
reqData[8] = UInt8(reqDataCount & 0xff)
reqData[9] = UInt8((reqDataCount >> 8) & 0xff)

return reqData
}
}
25 changes: 25 additions & 0 deletions AMSMB2/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,28 @@ extension Dictionary where Key == URLResourceKey {
return self[.fileSizeKey] as? Int64
}
}

extension Data {
mutating func append(uint16 value: UInt16) {
self.append(contentsOf: [UInt8(value & 0xff), UInt8(value >> 8 & 0xff)])
}

mutating func append(uint32 value: UInt32) {
self.append(contentsOf: [UInt8(value & 0xff), UInt8(value >> 8 & 0xff), UInt8(value >> 16 & 0xff), UInt8(value >> 24 & 0xff)])
}

mutating func append(uuid: UUID) {
self.append(contentsOf: [uuid.uuid.3, uuid.uuid.2, uuid.uuid.1, uuid.uuid.0,
uuid.uuid.5, uuid.uuid.4, uuid.uuid.7, uuid.uuid.6,
uuid.uuid.8, uuid.uuid.9, uuid.uuid.10, uuid.uuid.11,
uuid.uuid.12, uuid.uuid.13, uuid.uuid.14, uuid.uuid.15])
}

func scanValue<T: FixedWidthInteger>(start: Int) -> T? {
let length = MemoryLayout<T>.size
guard self.count >= start + length else { return nil }
var result: T = 0
(self as NSData).getBytes(&result, range: NSRange(location: start, length: length))
return result
}
}

0 comments on commit 70bba0a

Please sign in to comment.