// // FirewallRepair.swift // Lockdown // // Created by Oleg Dreyman on 21.10.2020. // Copyright © 2020 Confirmed Inc. All rights reserved. // import Foundation import CocoaLumberjackSwift import BackgroundTasks enum FirewallRepair { static let identifier = "com.confirmed.lockdown.firewallscheduler" enum Result { case repairAttempted case failed(Swift.Error?) case noAction } enum Context: CustomDebugStringConvertible { case backgroundRefresh case homeScreenDidLoad var debugDescription: String { switch self { case .backgroundRefresh: return "Background Check" case .homeScreenDidLoad: return "Home" } } } @available(iOS 13.0, *) static func handleAppRefresh(_ task: BGTask) { DDLogInfo("BGTask: Handle App Refresh called") let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 let operation = BlockOperation { DDLogInfo("BGTask: Operation started") FirewallRepair.run(context: .backgroundRefresh) { (result) in DDLogInfo("BGTask: Result: \(result)") } } operation.completionBlock = { DDLogInfo("BGTask: Operation completion block") let success = !operation.isCancelled DDLogInfo("BGTask: Operation completion success: \(success)") task.setTaskCompleted(success: success) } queue.addOperation(operation) task.expirationHandler = { DDLogError("BGTask: Expiration Handler called") queue.cancelAllOperations() } FirewallRepair.reschedule() } static func reschedule() { DDLogInfo("BGTask: Cancelling all task requests") BGTaskScheduler.shared.cancelAllTaskRequests() let timeIntervalSeconds: Double = 3600 let request = BGAppRefreshTaskRequest(identifier: FirewallRepair.identifier) request.earliestBeginDate = Date(timeIntervalSinceNow: timeIntervalSeconds) let earliestDate = Date(timeIntervalSinceNow: TimeInterval(timeIntervalSeconds)) DDLogInfo("BGTask: Scheduling app refresh id: \(FirewallRepair.identifier), earliest date \(earliestDate)") do { try BGTaskScheduler.shared.submit(request) } catch { DDLogError("Could not schedule app refresh: \(error)") } } static func run(context: Context, completion: @escaping (FirewallRepair.Result) -> Void = { _ in }) { DDLogInfo("Repair \(context): Starting") // Check 2 conditions for firewall restart, but reload manager first to get non-stale one FirewallController.shared.refreshManager(completion: { error in if let e = error { DDLogError("Repair \(context): Error refreshing Manager in \(context): \(e)") completion(.failed(e)) return } DDLogInfo("Repair \(context): refreshed manager") DDLogInfo("Repair \(context): userWantsFirewallEnabled \(getUserWantsFirewallEnabled())") DDLogInfo("Repair \(context): firewallStatus \(FirewallController.shared.status())") if getUserWantsFirewallEnabled() && [.connected, .invalid, .disconnected].contains(FirewallController.shared.status()) { DDLogInfo("Repair \(context): user wants enabled") if appHasJustBeenUpgradedOrIsNewInstall() { DDLogInfo("Repair \(context): APP UPGRADED, REFRESHING DEFAULT BLOCK LISTS, WHITELISTS, RESTARTING FIREWALL") setupFirewallDefaultBlockLists() setupLockdownWhitelistedDomains() FirewallController.shared.restart(completion: { error in if error != nil { DDLogError("Error restarting firewall on \(context): \(error!)") } completion(.repairAttempted) }) } else { Client.getBlockedDomainTest().done { DDLogError("Repair \(context): Repair Fetch Test Failed: Connected to \(testFirewallDomain) " + "even though it's supposed to be blocked") DDLogError("Repair \(context): Doing repair") FirewallController.shared.restart(completion: { error in if error != nil { DDLogError("Repair \(context): Error restarting firewall on \(context): \(error!)") } DDLogError("Repair \(context): Returned from restart, no error") completion(.repairAttempted) }) }.catch { error in let nsError = error as NSError if nsError.domain == NSURLErrorDomain { DDLogInfo("Repair \(context): Successful blocking of \(testFirewallDomain) with NSURLErrorDomain error: \(nsError)") } else { DDLogInfo("Repair \(context): Successful blocking of \(testFirewallDomain), but seeing non-NSURLErrorDomain error: \(error)") } completion(.repairAttempted) } } } else { completion(.noAction) } }) } }