mirror of
https://github.com/yggdrasil-network/yggdrasil-ios.git
synced 2025-04-28 22:25:10 +03:00
206 lines
7.6 KiB
Swift
206 lines
7.6 KiB
Swift
//
|
|
// AppDelegateExtension.swift
|
|
// Yggdrasil Network
|
|
//
|
|
// Created by Neil Alexander on 11/01/2019.
|
|
//
|
|
|
|
import Foundation
|
|
import NetworkExtension
|
|
import Yggdrasil
|
|
import SwiftUI
|
|
|
|
#if os(iOS)
|
|
class PlatformAppDelegate: UIResponder, UIApplicationDelegate {}
|
|
typealias PlatformApplication = UIApplication
|
|
typealias ApplicationDelegateAdaptor = UIApplicationDelegateAdaptor
|
|
#elseif os(macOS)
|
|
class PlatformAppDelegate: NSObject, NSApplicationDelegate {}
|
|
typealias PlatformApplication = NSApplication
|
|
typealias ApplicationDelegateAdaptor = NSApplicationDelegateAdaptor
|
|
#endif
|
|
|
|
class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
|
|
var vpnManager: NETunnelProviderManager = NETunnelProviderManager()
|
|
var yggdrasilConfig: ConfigurationProxy
|
|
let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension"
|
|
private var adminTimer: DispatchSourceTimer?
|
|
|
|
override init() {
|
|
self.yggdrasilConfig = ConfigurationProxy(manager: self.vpnManager)
|
|
super.init()
|
|
|
|
NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in
|
|
if let conn = notification.object as? NEVPNConnection {
|
|
switch conn.status {
|
|
case .connected:
|
|
self.requestSummaryIPC()
|
|
case .disconnecting, .disconnected:
|
|
self.clearStatus()
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
})
|
|
|
|
self.vpnTunnelProviderManagerInit()
|
|
}
|
|
|
|
@Published var yggdrasilEnabled: Bool = false {
|
|
didSet {
|
|
if yggdrasilEnabled {
|
|
if vpnManager.connection.status != .connected && vpnManager.connection.status != .connecting {
|
|
do {
|
|
try self.vpnManager.connection.startVPNTunnel()
|
|
} catch {
|
|
print("Failed to start VPN tunnel: \(error.localizedDescription)")
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
if vpnManager.connection.status != .disconnected && vpnManager.connection.status != .disconnecting {
|
|
self.vpnManager.connection.stopVPNTunnel()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Published var yggdrasilSupported: Bool = true
|
|
@Published var yggdrasilConnected: Bool = false
|
|
|
|
@Published var yggdrasilPublicKey: String = "N/A"
|
|
@Published var yggdrasilIP: String = "N/A"
|
|
@Published var yggdrasilSubnet: String = "N/A"
|
|
@Published var yggdrasilCoords: String = "[]"
|
|
|
|
@Published var yggdrasilPeers: [YggdrasilPeer] = []
|
|
|
|
func yggdrasilVersion() -> String {
|
|
return Yggdrasil.MobileGetVersion()
|
|
}
|
|
|
|
func becameActive() {
|
|
print("Application became active")
|
|
|
|
if self.adminTimer == nil {
|
|
self.adminTimer = DispatchSource.makeTimerSource(flags: .strict, queue: .main)
|
|
self.adminTimer!.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(2), leeway: DispatchTimeInterval.seconds(1))
|
|
self.adminTimer!.setEventHandler {
|
|
self.updateStatus(conn: self.vpnManager.connection)
|
|
}
|
|
}
|
|
if self.adminTimer != nil {
|
|
self.adminTimer!.resume()
|
|
}
|
|
|
|
self.requestSummaryIPC()
|
|
self.updateStatus(conn: self.vpnManager.connection)
|
|
}
|
|
|
|
func becameInactive() {
|
|
print("Application became inactive")
|
|
|
|
if self.adminTimer != nil {
|
|
self.adminTimer!.suspend()
|
|
}
|
|
}
|
|
|
|
func becameBackground() {}
|
|
|
|
func updateStatus(conn: NEVPNConnection) {
|
|
if conn.status == .connected || conn.status == .connecting {
|
|
self.yggdrasilEnabled = true
|
|
self.requestSummaryIPC()
|
|
} else if conn.status == .disconnecting || conn.status == .disconnected {
|
|
self.yggdrasilEnabled = false
|
|
self.clearStatus()
|
|
}
|
|
}
|
|
|
|
func vpnTunnelProviderManagerInit() {
|
|
print("Loading saved managers...")
|
|
|
|
NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in
|
|
guard error == nil else {
|
|
print("Failed to load VPN managers: \(error?.localizedDescription ?? "(no error)")")
|
|
self.yggdrasilSupported = false
|
|
return
|
|
}
|
|
|
|
guard let savedManagers else {
|
|
print("Expected to find saved managers but didn't")
|
|
// self.yggdrasilSupported = false
|
|
return
|
|
}
|
|
|
|
print("Found \(savedManagers.count) saved VPN managers")
|
|
for manager in savedManagers {
|
|
guard let proto = manager.protocolConfiguration as? NETunnelProviderProtocol else {
|
|
continue
|
|
}
|
|
guard let identifier = proto.providerBundleIdentifier else {
|
|
continue
|
|
}
|
|
guard identifier == self.yggdrasilComponent else {
|
|
continue
|
|
}
|
|
print("Found saved VPN Manager")
|
|
self.vpnManager = manager
|
|
break
|
|
}
|
|
|
|
self.vpnManager.loadFromPreferences(completionHandler: { (error: Error?) in
|
|
var loadedConfig = false
|
|
if error == nil {
|
|
if let vpnConfig = self.vpnManager.protocolConfiguration as? NETunnelProviderProtocol,
|
|
let confJson = vpnConfig.providerConfiguration!["json"] as? Data {
|
|
if let loaded = try? ConfigurationProxy(json: confJson, manager: self.vpnManager) {
|
|
print("Found existing protocol configuration")
|
|
self.yggdrasilConfig = loaded
|
|
loadedConfig = true
|
|
} else {
|
|
print("Existing protocol configuration is invalid, ignoring")
|
|
}
|
|
}
|
|
}
|
|
|
|
if !loadedConfig {
|
|
print("Generating new protocol configuration")
|
|
self.yggdrasilConfig = ConfigurationProxy(manager: self.vpnManager)
|
|
try? self.yggdrasilConfig.save(to: &self.vpnManager)
|
|
}
|
|
|
|
self.vpnManager.localizedDescription = "Yggdrasil"
|
|
self.vpnManager.isEnabled = true
|
|
})
|
|
}
|
|
}
|
|
|
|
func requestSummaryIPC() {
|
|
if self.vpnManager.connection.status != .connected {
|
|
return
|
|
}
|
|
if let session = self.vpnManager.connection as? NETunnelProviderSession {
|
|
try? session.sendProviderMessage("summary".data(using: .utf8)!) { js in
|
|
if let js = js, let summary = try? JSONDecoder().decode(YggdrasilSummary.self, from: js) {
|
|
self.yggdrasilEnabled = summary.enabled
|
|
self.yggdrasilIP = summary.address
|
|
self.yggdrasilSubnet = summary.subnet
|
|
self.yggdrasilPublicKey = summary.publicKey
|
|
self.yggdrasilPeers = summary.peers
|
|
self.yggdrasilConnected = summary.peers.filter { $0.up }.count > 0
|
|
|
|
print("Response: \(String(data: js, encoding: .utf8))")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func clearStatus() {
|
|
self.yggdrasilConnected = false
|
|
self.yggdrasilIP = "N/A"
|
|
self.yggdrasilSubnet = "N/A"
|
|
self.yggdrasilCoords = "[]"
|
|
self.yggdrasilPeers = []
|
|
}
|
|
}
|