Use libbox for RootHelper process lookup
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
import Darwin
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
private let PROC_PIDPATHINFO_MAXSIZE: Int32 = 4096
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ConnectionOwnerLookup")
|
||||
import Libbox
|
||||
|
||||
enum ConnectionOwnerLookup {
|
||||
struct Result {
|
||||
@@ -19,173 +15,21 @@ enum ConnectionOwnerLookup {
|
||||
destinationAddress: String,
|
||||
destinationPort: Int32
|
||||
) -> Result? {
|
||||
let sourceAddr = parseAddress(sourceAddress)
|
||||
let destAddr = parseAddress(destinationAddress)
|
||||
|
||||
guard let sourceAddr, let destAddr else {
|
||||
logger.error("find: failed to parse addresses")
|
||||
var error: NSError?
|
||||
guard let result = LibboxFindConnectionOwner(
|
||||
ipProtocol,
|
||||
sourceAddress,
|
||||
sourcePort,
|
||||
destinationAddress,
|
||||
destinationPort,
|
||||
&error
|
||||
) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let pidCount = proc_listpids(UInt32(PROC_ALL_PIDS), 0, nil, 0)
|
||||
guard pidCount > 0 else {
|
||||
logger.error("find: no processes found")
|
||||
return nil
|
||||
}
|
||||
|
||||
let pidBufferSize = Int(pidCount) * MemoryLayout<pid_t>.size
|
||||
let pids = UnsafeMutablePointer<pid_t>.allocate(capacity: Int(pidCount))
|
||||
defer { pids.deallocate() }
|
||||
|
||||
let actualCount = proc_listpids(UInt32(PROC_ALL_PIDS), 0, pids, Int32(pidBufferSize))
|
||||
guard actualCount > 0 else {
|
||||
logger.error("find: failed to list processes")
|
||||
return nil
|
||||
}
|
||||
|
||||
let numPids = Int(actualCount) / MemoryLayout<pid_t>.size
|
||||
|
||||
for i in 0 ..< numPids {
|
||||
let pid = pids[i]
|
||||
if pid == 0 { continue }
|
||||
|
||||
if let result = checkProcessForConnection(
|
||||
pid: pid,
|
||||
ipProtocol: ipProtocol,
|
||||
sourceAddr: sourceAddr,
|
||||
sourcePort: UInt16(sourcePort),
|
||||
destAddr: destAddr,
|
||||
destPort: UInt16(destinationPort)
|
||||
) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func checkProcessForConnection(
|
||||
pid: pid_t,
|
||||
ipProtocol: Int32,
|
||||
sourceAddr: Data,
|
||||
sourcePort: UInt16,
|
||||
destAddr: Data,
|
||||
destPort: UInt16
|
||||
) -> Result? {
|
||||
let bufferSize = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, nil, 0)
|
||||
guard bufferSize > 0 else { return nil }
|
||||
|
||||
let fdBuffer = UnsafeMutableRawPointer.allocate(byteCount: Int(bufferSize), alignment: MemoryLayout<proc_fdinfo>.alignment)
|
||||
defer { fdBuffer.deallocate() }
|
||||
|
||||
let actualSize = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdBuffer, bufferSize)
|
||||
guard actualSize > 0 else { return nil }
|
||||
|
||||
let fdCount = Int(actualSize) / MemoryLayout<proc_fdinfo>.size
|
||||
|
||||
for i in 0 ..< fdCount {
|
||||
let fd = fdBuffer.load(fromByteOffset: i * MemoryLayout<proc_fdinfo>.size, as: proc_fdinfo.self)
|
||||
|
||||
guard fd.proc_fdtype == PROX_FDTYPE_SOCKET else { continue }
|
||||
|
||||
var socketInfo = socket_fdinfo()
|
||||
let socketInfoSize = Int32(MemoryLayout<socket_fdinfo>.size)
|
||||
|
||||
let result = proc_pidfdinfo(pid, fd.proc_fd, PROC_PIDFDSOCKETINFO, &socketInfo, socketInfoSize)
|
||||
guard result == socketInfoSize else { continue }
|
||||
|
||||
let soi: in_sockinfo
|
||||
if ipProtocol == IPPROTO_TCP {
|
||||
guard socketInfo.psi.soi_kind == SOCKINFO_TCP else { continue }
|
||||
soi = socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini
|
||||
} else if ipProtocol == IPPROTO_UDP {
|
||||
guard socketInfo.psi.soi_kind == SOCKINFO_IN else { continue }
|
||||
soi = socketInfo.psi.soi_proto.pri_in
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if matchesConnection(
|
||||
socketInfo: soi,
|
||||
sourceAddr: sourceAddr,
|
||||
sourcePort: sourcePort,
|
||||
destAddr: destAddr,
|
||||
destPort: destPort
|
||||
) {
|
||||
return getProcessInfo(pid: pid)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func matchesConnection(
|
||||
socketInfo: in_sockinfo,
|
||||
sourceAddr: Data,
|
||||
sourcePort: UInt16,
|
||||
destAddr: Data,
|
||||
destPort: UInt16
|
||||
) -> Bool {
|
||||
let localPort = UInt16(bigEndian: UInt16(truncatingIfNeeded: socketInfo.insi_lport))
|
||||
let remotePort = UInt16(bigEndian: UInt16(truncatingIfNeeded: socketInfo.insi_fport))
|
||||
|
||||
guard localPort == sourcePort, remotePort == destPort else {
|
||||
return false
|
||||
}
|
||||
|
||||
var localAddr = socketInfo.insi_laddr
|
||||
var remoteAddr = socketInfo.insi_faddr
|
||||
|
||||
let localData: Data
|
||||
let remoteData: Data
|
||||
|
||||
if sourceAddr.count == 4 {
|
||||
localData = Data(bytes: &localAddr.ina_46.i46a_addr4, count: 4)
|
||||
remoteData = Data(bytes: &remoteAddr.ina_46.i46a_addr4, count: 4)
|
||||
} else {
|
||||
localData = Data(bytes: &localAddr.ina_6, count: 16)
|
||||
remoteData = Data(bytes: &remoteAddr.ina_6, count: 16)
|
||||
}
|
||||
|
||||
return localData == sourceAddr && remoteData == destAddr
|
||||
}
|
||||
|
||||
private static func getProcessInfo(pid: pid_t) -> Result? {
|
||||
let pathBuffer = UnsafeMutablePointer<CChar>.allocate(capacity: Int(PROC_PIDPATHINFO_MAXSIZE))
|
||||
defer { pathBuffer.deallocate() }
|
||||
|
||||
let pathLength = proc_pidpath(pid, pathBuffer, UInt32(PROC_PIDPATHINFO_MAXSIZE))
|
||||
let processPath = pathLength > 0 ? String(cString: pathBuffer) : ""
|
||||
|
||||
var info = proc_bsdinfo()
|
||||
let infoSize = Int32(MemoryLayout<proc_bsdinfo>.size)
|
||||
let result = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &info, infoSize)
|
||||
|
||||
guard result == infoSize else { return nil }
|
||||
|
||||
let uid = Int32(info.pbi_uid)
|
||||
let userName: String
|
||||
|
||||
if let pw = getpwuid(info.pbi_uid) {
|
||||
userName = String(cString: pw.pointee.pw_name)
|
||||
} else {
|
||||
userName = String(uid)
|
||||
}
|
||||
|
||||
return Result(userId: uid, userName: userName, processPath: processPath)
|
||||
}
|
||||
|
||||
private static func parseAddress(_ address: String) -> Data? {
|
||||
var addr4 = in_addr()
|
||||
if inet_pton(AF_INET, address, &addr4) == 1 {
|
||||
return Data(bytes: &addr4, count: 4)
|
||||
}
|
||||
|
||||
var addr6 = in6_addr()
|
||||
if inet_pton(AF_INET6, address, &addr6) == 1 {
|
||||
return Data(bytes: &addr6, count: 16)
|
||||
}
|
||||
|
||||
return nil
|
||||
return Result(
|
||||
userId: result.userId,
|
||||
userName: result.userName,
|
||||
processPath: result.processPath
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user