188 lines
5.6 KiB
Swift
188 lines
5.6 KiB
Swift
//
|
|
// KillSwitchClient.swift
|
|
// PrivadoVPN
|
|
//
|
|
// Created by Juraldinio on 7/30/20.
|
|
// Copyright © 2020 Privado LLC. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
protocol KillSwitchClientDelegate: AnyObject {
|
|
func clientChangeState(state: XPCClientState)
|
|
}
|
|
|
|
final class KillSwitchClient {
|
|
|
|
enum KillSwitchError: Error {
|
|
case unowned
|
|
case writeError
|
|
}
|
|
|
|
weak var delegate: KillSwitchClientDelegate?
|
|
private weak var client: (XPCFirewallClient & XPCSystemClient)?
|
|
|
|
var state: XPCClientState { self.client?.state ?? .undefined }
|
|
|
|
// MARK: - Init
|
|
|
|
init(client: (XPCFirewallClient & XPCSystemClient)?) {
|
|
|
|
self.client = client
|
|
|
|
self.client?.stateEmitter.addReaction { [weak self] state -> ShouldContinueReceiveNotifications in
|
|
self?.delegate?.clientChangeState(state: state)
|
|
return self.isExist
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Client operations
|
|
|
|
@discardableResult
|
|
func flushDNS() -> Future<Bool> {
|
|
|
|
return Future(value: self.client)
|
|
.flatMap { client in
|
|
client.flushDNS()
|
|
}
|
|
}
|
|
|
|
func resolveDNS(name: String) -> Future<String> {
|
|
|
|
return Future(value: self.client)
|
|
.flatMap { client in
|
|
client.resolveDNS(name: name)
|
|
}
|
|
}
|
|
|
|
@discardableResult
|
|
func changeDNS(dns: [String], port: String) -> Future<Bool> {
|
|
|
|
return Future(value: self.client)
|
|
.flatMap { client in
|
|
client.changeDNS(servers: dns, hardware: port)
|
|
}
|
|
}
|
|
|
|
@discardableResult
|
|
func changeDNSs(dns: [String], ports: [String]) -> Future<Bool> {
|
|
return ports.reduce(nil as Future<Bool>?) { result, port in
|
|
if let future = result {
|
|
return future.flatMap { _ in self.changeDNS(dns: dns, port: port) }
|
|
} else {
|
|
return self.changeDNS(dns: dns, port: port)
|
|
}
|
|
} ?? Future(value: true)
|
|
}
|
|
|
|
@discardableResult
|
|
func currentDNS(hardware: [String]) -> Future<[String: [String]]> {
|
|
|
|
return Future(value: self.client)
|
|
.flatMap { client in
|
|
client.currentDNS(hardware: hardware)
|
|
}
|
|
}
|
|
|
|
@discardableResult
|
|
func hardwarePorts() -> Future<[HardwarePort]> {
|
|
|
|
return Future(value: self.client)
|
|
.flatMap { client in
|
|
client.hardwarePorts()
|
|
}
|
|
}
|
|
|
|
func firewall(check rules: [String]) -> Future<Bool> {
|
|
|
|
let content = rules.joined(separator: "\n") + "\n"
|
|
|
|
return Future(value: URL.randomFile())
|
|
.flatMap { [weak self] url in
|
|
|
|
do {
|
|
try content.write(to: url, atomically: true, encoding: .utf8)
|
|
} catch {
|
|
return Future(error: KillSwitchError.writeError)
|
|
}
|
|
|
|
guard let client = self?.client else {
|
|
return Future(error: KillSwitchError.unowned)
|
|
}
|
|
|
|
return client
|
|
.firewallActivate(rulesPath: url.absoluteString, validate: true)
|
|
.onResult { _ in
|
|
url.remove()
|
|
}
|
|
}
|
|
}
|
|
|
|
@discardableResult
|
|
func firewall(activate rules: [String]) -> Future<Bool> {
|
|
|
|
let content = rules.joined(separator: "\n") + "\n"
|
|
return Future(value: URL.randomFile())
|
|
.flatMap { [weak self] url in
|
|
|
|
do {
|
|
try content.write(to: url, atomically: true, encoding: .utf8)
|
|
} catch {
|
|
return Future(error: KillSwitchError.writeError)
|
|
}
|
|
|
|
guard let client = self?.client else {
|
|
return Future(error: KillSwitchError.unowned)
|
|
}
|
|
|
|
return client
|
|
.firewallActivate(rulesPath: url.absoluteString, validate: false)
|
|
.onResult { _ in
|
|
url.remove()
|
|
}
|
|
}
|
|
}
|
|
|
|
@discardableResult
|
|
func firewallDeactivate() -> Future<Bool> {
|
|
|
|
return Future(value: self.client)
|
|
.flatMap { client in
|
|
client.firewallDeactivate()
|
|
}
|
|
}
|
|
|
|
func firewallEnable(_ enable: Bool) -> Future<Bool> {
|
|
|
|
return Future(value: self.client)
|
|
.map({ success in
|
|
Future(value: success)
|
|
}, failure: { _ in
|
|
let error = NSError(domain: PrivadoConstants.Hive.error,
|
|
code: PrivadoConstants.Hive.ErrorCode.xpcEmptyRemoteObject,
|
|
userInfo: nil)
|
|
return Future(error: error)
|
|
}).flatMap { client in
|
|
client.firewallEnable(enable)
|
|
}
|
|
}
|
|
|
|
@discardableResult
|
|
func firewallState() -> Future<Bool> {
|
|
|
|
return Future(value: self.client)
|
|
.map({ success in
|
|
Future(value: success)
|
|
}, failure: { _ in
|
|
let error = NSError(domain: PrivadoConstants.Hive.error,
|
|
code: PrivadoConstants.Hive.ErrorCode.xpcEmptyRemoteObject,
|
|
userInfo: nil)
|
|
return Future(error: error)
|
|
}).flatMap { client in
|
|
client.firewallState()
|
|
}
|
|
}
|
|
|
|
}
|