Files
2021-09-08 22:25:59 +03:00

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()
}
}
}