// // IntercommSystem.swift // io.privado.main.hive // // Created by Juraldinio on 6/21/20. // Copyright © 2020 Privado. All rights reserved. // import Foundation final class IntercommSystem { enum Constants { static let sudoPath = "/usr/bin/sudo" static let ifconfigPath = "/sbin/ifconfig" static let networksetupPath = "/usr/sbin/networksetup" static let dscacheutilPath = "/usr/bin/dscacheutil" } private weak var client: HiveIntercommConsole? init(client: HiveIntercommConsole?) { self.client = client } // MARK: - Extractor methods private func extractHardwarePorts(from stdout: String) -> [HardwarePort] { guard let regExp = try? NSRegularExpression(pattern: #"(\([\d]+\)) ([\w]+)\n\(Hardware Port: ([\w]+), Device: ([\w]+)\)"#, options: []) else { return [] } return stdout .components(separatedBy: "\n\n") .map { String($0) } .compactMap { value -> HardwarePort? in guard let match = regExp.firstMatch(in: value, options: [], range: NSRange(location: 0, length: value.count)) , match.numberOfRanges > 4 , let port = value.substring(using: match.range(at: 3)) , let device = value.substring(using: match.range(at: 4)) else { return nil } return HardwarePort(port: port, device: device) } .filter { $0.isValid } } private func extractIp(domain: String, from stdout: String) -> String? { let results = stdout .split(separator: "\n") .map { String($0) } guard results.contains(where: { $0.contains(domain) }) else { return nil } guard let ipAddress = results.filter({ $0.contains("ip_address") }).first?.split(separator: " ") , ipAddress.count > 1 else { return nil } return String(ipAddress[1]) } /*private func extractIFConfigSettings(from stdout: String) -> [InterfaceConfiguration] { guard let regExp = try? NSRegularExpression(pattern: #"([\w\d]+): .+\n([ ]+inet ([\d\.]+) netmask .+\n|[ ]+inet6 [\w:]+.+\n|.+\n)+.+"#, options: []) else { return [] } return [] }*/ } extension IntercommSystem: IntercommSystemType { func updateRequired(short: String, bundle: String, completion: @escaping (Bool) -> Void) { completion(false) } func version(completion: @escaping (String, String) -> Void) { let shortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0" let bundleVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1" completion(shortVersion, bundleVersion) } func ping(completion: @escaping () -> Void) { completion() } func ifconfig(completion: @escaping (String) -> Void) { let result = main.run(Constants.ifconfigPath) completion(result.stdout) } func hardwarePorts(completion: @escaping ([HardwarePort]) -> Void) { let result = main.run(Constants.networksetupPath, ["-listnetworkserviceorder"]) self.client?.console(message: "hardwarePorts stdout: \(result.stdout)") let ports = self.extractHardwarePorts(from: result.stdout) completion(ports) } func flushDNS(completion: @escaping (Bool) -> Void) { let arguments: [String] let os = ProcessInfo().operatingSystemVersion switch (os.majorVersion, os.minorVersion, os.patchVersion) { case let (_, minor, _) where minor >= 12: arguments = ["killall -HUP mDNSResponder", "killall mDNSResponderHelper", "dscacheutil -flushcache"] case let (_, minor, _) where minor >= 11: arguments = ["killall -HUP mDNSResponder"] case let (_, 10, patch) where patch >= 4: arguments = ["dscacheutil -flushcache", "killall -HUP mDNSResponder"] default: arguments = [] } guard !arguments.isEmpty else { completion(false) return } do { try arguments.forEach { let result = main.run(Constants.sudoPath, $0.split(separator: " ").map({ String($0) })) guard result.succeeded else { throw NSError() } //self.client?.console(message: "flushDNS stdout: \(result.stdout)") if !result.stderror.isEmpty { self.client?.console(message: "flushDNS stderror: \(result.stderror)") } } } catch { completion(false) return } completion(true) } func resolveDNS(name: String, completion: @escaping (String) -> Void) { let arguments = ["-q", "host", "-a", "name", name] let result = main.run(Constants.dscacheutilPath, arguments) let ipAddress = self.extractIp(domain: name, from: result.stdout) ?? "" completion(ipAddress) } func changeDNS(servers: [String], hardware: String, completion: @escaping (Bool) -> Void) { var arguments = ["-setdnsservers", hardware] arguments.append(contentsOf: servers) let result = main.run(Constants.networksetupPath, arguments) completion(result.error == nil) } func currentDNS(hardware: [String], completion: @escaping ([String: [String]]) -> Void) { let dns = hardware.reduce([String: [String]]()) { dns, hardware in var dns = dns let arguments = ["-getdnsservers", hardware] let result = main.run(Constants.networksetupPath, arguments) if result.error == nil { if result.stdout.contains("aren't any DNS") { dns[hardware] = [] } else { dns[hardware] = result.stdout .split(separator: "\n") .map { String($0) } } } return dns } completion(dns) } func removeUninstaller(completion: @escaping () -> Void) { [ ["launchctl", "unload", "/Library/LaunchDaemons/io.privado.main.uninstaller.plist"], ["rm", "/Library/LaunchDaemons/io.privado.main.uninstaller.plist"], ["rm", "/Library/PrivilegedHelperTools/io.privado.main.uninstaller"] ].forEach { arguments in main.run(Constants.sudoPath, arguments) } completion() } }