mirror of https://github.com/oxen-io/lokinet
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
7.1 KiB
Swift
187 lines
7.1 KiB
Swift
import AppKit
|
|
import Foundation
|
|
import NetworkExtension
|
|
import SystemExtensions
|
|
|
|
let app = NSApplication.shared
|
|
|
|
let START = "--start"
|
|
let STOP = "--stop"
|
|
|
|
let HELP_STRING = "usage: lokinet [--start|--stop]"
|
|
|
|
class LokinetMain: NSObject, NSApplicationDelegate {
|
|
var vpnManager = NETunnelProviderManager()
|
|
var mode = START
|
|
let netextBundleId = "org.lokinet.network-extension"
|
|
|
|
func applicationDidFinishLaunching(_: Notification) {
|
|
if self.mode == START {
|
|
startNetworkExtension()
|
|
} else if self.mode == STOP {
|
|
tearDownVPNTunnel()
|
|
} else {
|
|
self.result(msg: HELP_STRING)
|
|
}
|
|
|
|
}
|
|
|
|
func bail() {
|
|
app.terminate(self)
|
|
}
|
|
|
|
func result(msg: String) {
|
|
NSLog(msg)
|
|
// TODO: does lokinet continue after this?
|
|
self.bail()
|
|
}
|
|
|
|
func tearDownVPNTunnel() {
|
|
NSLog("Stopping Lokinet")
|
|
NETunnelProviderManager.loadAllFromPreferences { [self] (savedManagers: [NETunnelProviderManager]?, error: Error?) in
|
|
if let error = error {
|
|
self.result(msg: error.localizedDescription)
|
|
return
|
|
}
|
|
|
|
if let savedManagers = savedManagers {
|
|
for manager in savedManagers {
|
|
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.netextBundleId {
|
|
manager.isEnabled = false
|
|
self.result(msg: "Lokinet Down")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
self.result(msg: "Lokinet is not up")
|
|
}
|
|
}
|
|
|
|
func startNetworkExtension() {
|
|
#if MACOS_SYSTEM_EXTENSION
|
|
NSLog("Loading Lokinet network extension")
|
|
// Start by activating the system extension
|
|
let activationRequest = OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: netextBundleId, queue: .main)
|
|
activationRequest.delegate = self
|
|
OSSystemExtensionManager.shared.submitRequest(activationRequest)
|
|
#else
|
|
setupVPNTunnel()
|
|
#endif
|
|
}
|
|
|
|
func setupVPNTunnel() {
|
|
|
|
NSLog("Starting up Lokinet tunnel")
|
|
NETunnelProviderManager.loadAllFromPreferences { [self] (savedManagers: [NETunnelProviderManager]?, error: Error?) in
|
|
if let error = error {
|
|
self.result(msg: error.localizedDescription)
|
|
return
|
|
}
|
|
|
|
if let savedManagers = savedManagers {
|
|
for manager in savedManagers {
|
|
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.netextBundleId {
|
|
NSLog("Found saved VPN Manager")
|
|
self.vpnManager = manager
|
|
}
|
|
}
|
|
}
|
|
let providerProtocol = NETunnelProviderProtocol()
|
|
providerProtocol.serverAddress = "loki.loki" // Needs to be set to some non-null dummy value
|
|
providerProtocol.username = "anonymous"
|
|
providerProtocol.providerBundleIdentifier = self.netextBundleId
|
|
providerProtocol.enforceRoutes = true
|
|
// macos seems to have trouble when this is true, and reports are that this breaks and
|
|
// doesn't do what it says on the tin in the first place. Needs more testing.
|
|
providerProtocol.includeAllNetworks = false
|
|
self.vpnManager.protocolConfiguration = providerProtocol
|
|
self.vpnManager.isEnabled = true
|
|
// self.vpnManager.isOnDemandEnabled = true
|
|
self.vpnManager.localizedDescription = "lokinet"
|
|
self.vpnManager.saveToPreferences(completionHandler: { [self] error -> Void in
|
|
if error != nil {
|
|
NSLog("Error saving to preferences")
|
|
self.result(msg: error!.localizedDescription)
|
|
} else {
|
|
self.vpnManager.loadFromPreferences(completionHandler: { error in
|
|
if error != nil {
|
|
NSLog("Error loading from preferences")
|
|
self.result(msg: error!.localizedDescription)
|
|
} else {
|
|
do {
|
|
NSLog("Trying to start")
|
|
self.initializeConnectionObserver()
|
|
try self.vpnManager.connection.startVPNTunnel()
|
|
} catch let error as NSError {
|
|
self.result(msg: error.localizedDescription)
|
|
} catch {
|
|
self.result(msg: "There was a fatal error")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func initializeConnectionObserver() {
|
|
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: vpnManager.connection, queue: OperationQueue.main) { [self] _ -> Void in
|
|
if self.vpnManager.connection.status == .invalid {
|
|
self.result(msg: "VPN configuration is invalid")
|
|
} else if self.vpnManager.connection.status == .disconnected {
|
|
self.result(msg: "VPN is disconnected.")
|
|
} else if self.vpnManager.connection.status == .connecting {
|
|
NSLog("VPN is connecting...")
|
|
} else if self.vpnManager.connection.status == .reasserting {
|
|
NSLog("VPN is reasserting...")
|
|
} else if self.vpnManager.connection.status == .disconnecting {
|
|
NSLog("VPN is disconnecting...")
|
|
} else if self.vpnManager.connection.status == .connected {
|
|
self.result(msg: "VPN Connected")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if MACOS_SYSTEM_EXTENSION
|
|
|
|
extension LokinetMain: OSSystemExtensionRequestDelegate {
|
|
|
|
func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
|
|
guard result == .completed else {
|
|
NSLog("Unexpected result %d for system extension request", result.rawValue)
|
|
return
|
|
}
|
|
NSLog("Lokinet system extension loaded")
|
|
setupVPNTunnel()
|
|
}
|
|
|
|
func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
|
|
NSLog("System extension request failed: %@", error.localizedDescription)
|
|
}
|
|
|
|
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
|
|
NSLog("Extension %@ requires user approval", request.identifier)
|
|
}
|
|
|
|
func request(_ request: OSSystemExtensionRequest,
|
|
actionForReplacingExtension existing: OSSystemExtensionProperties,
|
|
withExtension extension: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
|
|
NSLog("Replacing extension %@ version %@ with version %@", request.identifier, existing.bundleShortVersion, `extension`.bundleShortVersion)
|
|
return .replace
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
let args = CommandLine.arguments
|
|
|
|
if args.count <= 2 {
|
|
let delegate = LokinetMain()
|
|
delegate.mode = args.count > 1 ? args[1] : START
|
|
app.delegate = delegate
|
|
app.run()
|
|
} else {
|
|
NSLog(HELP_STRING)
|
|
}
|