App updates

This commit is contained in:
Neil Alexander 2024-04-16 21:35:51 +01:00
parent 2cb5eebcd2
commit 5fbb735f56
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
9 changed files with 204 additions and 91 deletions

View file

@ -13,6 +13,8 @@ import AppKit
import SwiftUI import SwiftUI
import Yggdrasil import Yggdrasil
import NetworkExtension import NetworkExtension
import Foundation
import CoreData
#if os(iOS) #if os(iOS)
class PlatformItemSource: NSObject, UIActivityItemSource { class PlatformItemSource: NSObject, UIActivityItemSource {
@ -31,9 +33,10 @@ class PlatformItemSource: NSObject {}
class ConfigurationProxy: PlatformItemSource { class ConfigurationProxy: PlatformItemSource {
private var manager: NETunnelProviderManager? private var manager: NETunnelProviderManager?
private var json: Data? = nil private var json: Data? = nil
private var dict: [String: Any]? = nil private var dict: [String: Any]? = [:]
private var timer: Timer?
init(manager: NETunnelProviderManager? = nil) { init(manager: NETunnelProviderManager) {
self.manager = manager self.manager = manager
super.init() super.init()
@ -64,14 +67,13 @@ class ConfigurationProxy: PlatformItemSource {
self.set("Listen", to: [] as [String]) self.set("Listen", to: [] as [String])
self.set("AdminListen", to: "none") self.set("AdminListen", to: "none")
self.set("IfName", to: "dummy") self.set("IfName", to: "dummy")
self.set("Peers", to: ["tcp://172.22.97.1.5190", "tls://172.22.97.1:5191"])
if self.get("AutoStart") == nil { if self.get("AutoStart") == nil {
self.set("AutoStart", to: ["Any": false, "WiFi": false, "Mobile": false, "Ethernet": false] as [String: Bool]) self.set("AutoStart", to: ["Any": false, "WiFi": false, "Mobile": false, "Ethernet": false] as [String: Bool])
} }
let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
if multicastInterfaces.count == 0 { if multicastInterfaces.count != 1 {
self.set("MulticastInterfaces", to: [ self.set("MulticastInterfaces", to: [
[ [
"Regex": "en.*", "Regex": "en.*",
@ -94,7 +96,7 @@ class ConfigurationProxy: PlatformItemSource {
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
multicastInterfaces[0]["Beacon"] = newValue multicastInterfaces[0]["Beacon"] = newValue
self.set("MulticastInterfaces", to: multicastInterfaces) self.set("MulticastInterfaces", to: multicastInterfaces)
self.trySave() self.saveSoon()
} }
} }
@ -110,7 +112,23 @@ class ConfigurationProxy: PlatformItemSource {
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? [] var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
multicastInterfaces[0]["Listen"] = newValue multicastInterfaces[0]["Listen"] = newValue
self.set("MulticastInterfaces", to: multicastInterfaces) self.set("MulticastInterfaces", to: multicastInterfaces)
self.trySave() self.saveSoon()
}
}
public var multicastPassword: String {
get {
let multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
if multicastInterfaces.count == 0 {
return ""
}
return multicastInterfaces[0]["Password"] as? String ?? ""
}
set {
var multicastInterfaces = self.get("MulticastInterfaces") as? [[String: Any]] ?? []
multicastInterfaces[0]["Password"] = newValue
self.set("MulticastInterfaces", to: multicastInterfaces)
self.saveSoon()
} }
} }
@ -120,7 +138,7 @@ class ConfigurationProxy: PlatformItemSource {
} }
set { set {
self.set("Any", inSection: "AutoStart", to: newValue) self.set("Any", inSection: "AutoStart", to: newValue)
self.trySave() self.saveSoon()
} }
} }
@ -130,7 +148,7 @@ class ConfigurationProxy: PlatformItemSource {
} }
set { set {
self.set("WiFi", inSection: "AutoStart", to: newValue) self.set("WiFi", inSection: "AutoStart", to: newValue)
self.trySave() self.saveSoon()
} }
} }
@ -140,7 +158,7 @@ class ConfigurationProxy: PlatformItemSource {
} }
set { set {
self.set("Ethernet", inSection: "AutoStart", to: newValue) self.set("Ethernet", inSection: "AutoStart", to: newValue)
self.trySave() self.saveSoon()
} }
} }
@ -150,7 +168,17 @@ class ConfigurationProxy: PlatformItemSource {
} }
set { set {
self.set("Mobile", inSection: "AutoStart", to: newValue) self.set("Mobile", inSection: "AutoStart", to: newValue)
self.trySave() self.saveSoon()
}
}
public var deviceName: String {
get {
return self.get("name", inSection: "NodeInfo") as? String ?? ""
}
set {
self.set("name", inSection: "NodeInfo", to: newValue)
self.saveSoon()
} }
} }
@ -160,7 +188,7 @@ class ConfigurationProxy: PlatformItemSource {
} }
set { set {
self.set("Peers", to: newValue) self.set("Peers", to: newValue)
self.trySave() self.saveSoon()
} }
} }
@ -239,10 +267,22 @@ class ConfigurationProxy: PlatformItemSource {
} }
} }
private func trySave() { private func saveSoon() {
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(
timeInterval: 1.0,
target: self,
selector: #selector(saveFromTimer),
userInfo: nil,
repeats: false
)
}
@objc private func saveFromTimer() {
if var manager = self.manager { if var manager = self.manager {
try? self.save(to: &manager) try? self.save(to: &manager)
} }
self.timer = nil
} }
func save(to manager: inout NETunnelProviderManager) throws { func save(to manager: inout NETunnelProviderManager) throws {
@ -254,6 +294,8 @@ class ConfigurationProxy: PlatformItemSource {
providerProtocol.serverAddress = "yggdrasil" providerProtocol.serverAddress = "yggdrasil"
providerProtocol.username = self.get("PublicKey") as? String ?? self.get("SigningPublicKey") as? String ?? "(unknown public key)" providerProtocol.username = self.get("PublicKey") as? String ?? self.get("SigningPublicKey") as? String ?? "(unknown public key)"
NSLog(String(data: data, encoding: .utf8) ?? "(unknown)")
let disconnectrule = NEOnDemandRuleDisconnect() let disconnectrule = NEOnDemandRuleDisconnect()
var rules: [NEOnDemandRule] = [disconnectrule] var rules: [NEOnDemandRule] = [disconnectrule]
if self.get("Any", inSection: "AutoStart") as? Bool ?? false { if self.get("Any", inSection: "AutoStart") as? Bool ?? false {
@ -283,10 +325,10 @@ class ConfigurationProxy: PlatformItemSource {
manager.onDemandRules = rules manager.onDemandRules = rules
manager.isOnDemandEnabled = rules.count > 1 manager.isOnDemandEnabled = rules.count > 1
providerProtocol.disconnectOnSleep = rules.count > 1 providerProtocol.disconnectOnSleep = rules.count > 1
manager.protocolConfiguration = providerProtocol manager.protocolConfiguration = providerProtocol
manager.saveToPreferences(completionHandler: { (error:Error?) in manager.saveToPreferences(completionHandler: { error in
if let error = error { if let error = error {
print(error) print(error)
} else { } else {

View file

@ -22,11 +22,12 @@ typealias ApplicationDelegateAdaptor = NSApplicationDelegateAdaptor
class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject { class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
var vpnManager: NETunnelProviderManager = NETunnelProviderManager() var vpnManager: NETunnelProviderManager = NETunnelProviderManager()
var yggdrasilConfig: ConfigurationProxy = ConfigurationProxy() var yggdrasilConfig: ConfigurationProxy
let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension" let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension"
private var adminTimer: DispatchSourceTimer? private var adminTimer: DispatchSourceTimer?
override init() { override init() {
self.yggdrasilConfig = ConfigurationProxy(manager: self.vpnManager)
super.init() super.init()
NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in
@ -34,7 +35,6 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
switch conn.status { switch conn.status {
case .connected: case .connected:
self.requestSummaryIPC() self.requestSummaryIPC()
self.requestStatusIPC()
case .disconnecting, .disconnected: case .disconnecting, .disconnected:
self.clearStatus() self.clearStatus()
default: default:
@ -73,9 +73,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
@Published var yggdrasilSubnet: String = "N/A" @Published var yggdrasilSubnet: String = "N/A"
@Published var yggdrasilCoords: String = "[]" @Published var yggdrasilCoords: String = "[]"
@Published var yggdrasilPeers: [[String: Any]] = [[:]] @Published var yggdrasilPeers: [YggdrasilPeer] = []
@Published var yggdrasilDHT: [[String: Any]] = [[:]]
@Published var yggdrasilNodeInfo: [String: Any] = [:]
func yggdrasilVersion() -> String { func yggdrasilVersion() -> String {
return Yggdrasil.MobileGetVersion() return Yggdrasil.MobileGetVersion()
@ -111,7 +109,7 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
func updateStatus(conn: NEVPNConnection) { func updateStatus(conn: NEVPNConnection) {
if conn.status == .connected { if conn.status == .connected {
self.requestStatusIPC() self.requestSummaryIPC()
} else if conn.status == .disconnecting || conn.status == .disconnected { } else if conn.status == .disconnecting || conn.status == .disconnected {
self.clearStatus() self.clearStatus()
} }
@ -183,30 +181,14 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
if let session = self.vpnManager.connection as? NETunnelProviderSession { if let session = self.vpnManager.connection as? NETunnelProviderSession {
try? session.sendProviderMessage("summary".data(using: .utf8)!) { js in try? session.sendProviderMessage("summary".data(using: .utf8)!) { js in
if let js = js, let summary = try? JSONDecoder().decode(YggdrasilSummary.self, from: js) { if let js = js, let summary = try? JSONDecoder().decode(YggdrasilSummary.self, from: js) {
self.yggdrasilEnabled = true self.yggdrasilEnabled = summary.enabled
self.yggdrasilIP = summary.address self.yggdrasilIP = summary.address
self.yggdrasilSubnet = summary.subnet self.yggdrasilSubnet = summary.subnet
self.yggdrasilPublicKey = summary.publicKey self.yggdrasilPublicKey = summary.publicKey
} self.yggdrasilPeers = summary.peers
} self.yggdrasilConnected = summary.peers.count > 0
}
} print(self.yggdrasilPeers)
func requestStatusIPC() {
if self.vpnManager.connection.status != .connected {
return
}
if let session = self.vpnManager.connection as? NETunnelProviderSession {
try? session.sendProviderMessage("status".data(using: .utf8)!) { js in
if let js = js, let status = try? JSONDecoder().decode(YggdrasilStatus.self, from: js) {
self.yggdrasilCoords = status.coords
if let jsonResponse = try? JSONSerialization.jsonObject(with: status.peers, options: []) as? [[String: Any]] {
self.yggdrasilPeers = jsonResponse
}
if let jsonResponse = try? JSONSerialization.jsonObject(with: status.dht, options: []) as? [[String: Any]] {
self.yggdrasilDHT = jsonResponse
}
self.yggdrasilConnected = self.yggdrasilEnabled && self.yggdrasilPeers.count > 0 && self.yggdrasilDHT.count > 0
} }
} }
} }
@ -218,6 +200,5 @@ class CrossPlatformAppDelegate: PlatformAppDelegate, ObservableObject {
self.yggdrasilSubnet = "N/A" self.yggdrasilSubnet = "N/A"
self.yggdrasilCoords = "[]" self.yggdrasilCoords = "[]"
self.yggdrasilPeers = [] self.yggdrasilPeers = []
self.yggdrasilDHT = []
} }
} }

View file

@ -11,11 +11,29 @@ struct YggdrasilSummary: Codable {
var address: String var address: String
var subnet: String var subnet: String
var publicKey: String var publicKey: String
var enabled: Bool
var peers: [YggdrasilPeer]
func list() -> [String] {
return peers.map { $0.remote }
}
} }
struct YggdrasilStatus: Codable { struct YggdrasilPeer: Codable, Identifiable {
var enabled: Bool var id: String { remote } // For Identifiable protocol
var coords: String let remote: String
var peers: Data let up: Bool
var dht: Data let address: String
let key: String
let priority: UInt8
let cost: UInt16?
enum CodingKeys: String, CodingKey {
case remote = "URI"
case up = "Up"
case address = "IP"
case key = "Key"
case priority = "Priority"
case cost = "Cost"
}
} }

View file

@ -98,25 +98,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
let request = String(data: messageData, encoding: .utf8) let request = String(data: messageData, encoding: .utf8)
switch request { switch request {
case "summary": case "summary":
let pj = self.yggdrasil.getPeersJSON()
var peers: [YggdrasilPeer] = []
do {
peers = try JSONDecoder().decode(
[YggdrasilPeer].self,
from: pj.data(using: .utf8)!
)
} catch {
NSLog("JSON Error: \(error)")
}
let summary = YggdrasilSummary( let summary = YggdrasilSummary(
address: self.yggdrasil.getAddressString(), address: self.yggdrasil.getAddressString(),
subnet: self.yggdrasil.getSubnetString(), subnet: self.yggdrasil.getSubnetString(),
publicKey: self.yggdrasil.getPublicKeyString() publicKey: self.yggdrasil.getPublicKeyString(),
enabled: true,
peers: peers.sorted(by: { a, b in
a.remote < b.remote
})
) )
if let json = try? JSONEncoder().encode(summary) { if let json = try? JSONEncoder().encode(summary) {
completionHandler?(json) completionHandler?(json)
} }
case "status":
let status = YggdrasilStatus(
enabled: true,
coords: self.yggdrasil.getCoordsString(),
peers: self.yggdrasil.getPeersJSON().data(using: .utf8) ?? Data(),
dht: self.yggdrasil.getDHTJSON().data(using: .utf8) ?? Data()
)
if let json = try? JSONEncoder().encode(status) {
completionHandler?(json)
}
default: default:
completionHandler?(nil) completionHandler?(nil)

View file

@ -3,17 +3,18 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 55; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
391B72A82A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391B72A62A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift */; }; 391B72A82A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391B72A62A2FD90100896278 /* PacketTunnelProvider+FileDescriptor.swift */; };
3952ADB829945AF700B3835D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; }; 3952ADB829945AF700B3835D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; };
3996AF39270328080070947D /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; };
39B51A4A2997062E0059D29D /* PeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39B51A492997062E0059D29D /* PeersView.swift */; }; 39B51A4A2997062E0059D29D /* PeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39B51A492997062E0059D29D /* PeersView.swift */; };
39B51A4B29994F240059D29D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; }; 39B51A4B29994F240059D29D /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB629945AF700B3835D /* ConfigurationProxy.swift */; };
39B51A4C29994F350059D29D /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */; }; 39B51A4C29994F350059D29D /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3952ADB929945AFA00B3835D /* CrossPlatformAppDelegate.swift */; };
39B51A4D299951D60059D29D /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; }; 39B51A4D299951D60059D29D /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; };
39B9173C2BBD637200899F81 /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; };
39B9173D2BBD637200899F81 /* Yggdrasil.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
39BF9FC72A2F6FFD000E7269 /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; }; 39BF9FC72A2F6FFD000E7269 /* Yggdrasil.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3996AF37270328080070947D /* Yggdrasil.xcframework */; };
39CC924D221DEDD3004960DC /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; }; 39CC924D221DEDD3004960DC /* IPCResponses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* IPCResponses.swift */; };
39F0205E2996CD760093F603 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0205D2996CD760093F603 /* Application.swift */; }; 39F0205E2996CD760093F603 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0205D2996CD760093F603 /* Application.swift */; };
@ -22,7 +23,7 @@
39F020662996CD770093F603 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 39F020652996CD770093F603 /* Preview Assets.xcassets */; }; 39F020662996CD770093F603 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 39F020652996CD770093F603 /* Preview Assets.xcassets */; };
39F0206B2996CF260093F603 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0206A2996CF260093F603 /* SettingsView.swift */; }; 39F0206B2996CF260093F603 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0206A2996CF260093F603 /* SettingsView.swift */; };
39F99B342A48F6E50045BD10 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */; }; 39F99B342A48F6E50045BD10 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */; };
39F99B382A48F74F0045BD10 /* YggdrasilNetworkExtension.appex in CopyFiles */ = {isa = PBXBuildFile; fileRef = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; platformFilters = (ios, macos, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 39F99B382A48F74F0045BD10 /* YggdrasilNetworkExtension.appex in CopyFiles */ = {isa = PBXBuildFile; fileRef = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */; }; E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -37,6 +38,17 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
39B9173E2BBD637200899F81 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
39B9173D2BBD637200899F81 /* Yggdrasil.xcframework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
39F99B372A48F7380045BD10 /* CopyFiles */ = { 39F99B372A48F7380045BD10 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -87,7 +99,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
3996AF39270328080070947D /* Yggdrasil.xcframework in Frameworks */, 39B9173C2BBD637200899F81 /* Yggdrasil.xcframework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -199,6 +211,7 @@
E593CE931DF905AF00D7265D /* Sources */, E593CE931DF905AF00D7265D /* Sources */,
E593CE941DF905AF00D7265D /* Frameworks */, E593CE941DF905AF00D7265D /* Frameworks */,
E593CE951DF905AF00D7265D /* Resources */, E593CE951DF905AF00D7265D /* Resources */,
39B9173E2BBD637200899F81 /* Embed Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -309,10 +322,6 @@
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
39F99B362A48F70F0045BD10 /* PBXTargetDependency */ = { 39F99B362A48F70F0045BD10 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
platformFilters = (
ios,
macos,
);
target = E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */; target = E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */;
targetProxy = 39F99B352A48F70F0045BD10 /* PBXContainerItemProxy */; targetProxy = 39F99B352A48F70F0045BD10 /* PBXContainerItemProxy */;
}; };
@ -363,6 +372,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -413,6 +423,7 @@
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -548,9 +559,13 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements"; CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 32; CURRENT_PROJECT_VERSION = 32;
DEVELOPMENT_TEAM = R9AV23TXF2; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = R9AV23TXF2;
"DEVELOPMENT_TEAM[sdk=macosx*]" = R9AV23TXF2;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -566,8 +581,12 @@
PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.extension; PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.extension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTS_MACCATALYST = YES; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Provisioning eu.neilalexander.yggdrasil.extension";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "Provisioning2 eu.neilalexander.yggdrasil.extension";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -580,9 +599,13 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements"; CODE_SIGN_ENTITLEMENTS = "Yggdrasil Network Extension/YggdrasilNetworkExtension.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 32; CURRENT_PROJECT_VERSION = 32;
DEVELOPMENT_TEAM = R9AV23TXF2; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = R9AV23TXF2;
"DEVELOPMENT_TEAM[sdk=macosx*]" = R9AV23TXF2;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -599,8 +622,12 @@
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "8ce353d5-fd5f-4d5e-b664-8ad294091125"; PROVISIONING_PROFILE = "8ce353d5-fd5f-4d5e-b664-8ad294091125";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTS_MACCATALYST = YES; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Provisioning eu.neilalexander.yggdrasil.extension";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "Provisioning2 eu.neilalexander.yggdrasil.extension";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";

View file

@ -23,7 +23,7 @@ struct Application: App {
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
NavigationSplitView { NavigationSplitView {
ZStack { VStack {
List(selection: $selection) { List(selection: $selection) {
NavigationLink(destination: StatusView()) { NavigationLink(destination: StatusView()) {
HStack { HStack {

View file

@ -8,22 +8,25 @@
import SwiftUI import SwiftUI
struct PeersView: View { struct PeersView: View {
// @Binding public var yggdrasilConfiguration: ConfigurationProxy
@ObservedObject private var appDelegate = Application.appDelegate @ObservedObject private var appDelegate = Application.appDelegate
#if os(iOS)
@Environment(\.editMode) var editMode
#endif
var body: some View { var body: some View {
Form { Form {
Section(content: { Section(content: {
ForEach(Array(appDelegate.yggdrasilConfig.peers.enumerated()), id: \.offset) { index, peer in ForEach(Array(appDelegate.yggdrasilConfig.peers.enumerated()), id: \.offset) { index, peer in
HStack() { HStack() {
Text(peer) TextField("Peer", text: $appDelegate.yggdrasilConfig.peers[index])
//TextField("", text: $yggdrasilConfiguration.peers[index]) .labelsHidden()
// .multilineTextAlignment(.leading) #if os(iOS)
.disabled(!editMode!.wrappedValue.isEditing)
#endif
#if os(macOS) #if os(macOS)
Spacer() Spacer()
Button(role: .destructive) { Button(role: .destructive) {
// appDelegate.yggdrasilConfig.peers.remove { $0 == peerURI } appDelegate.yggdrasilConfig.peers.remove(at: index)
// self.delete(at: appDelegate.yggdrasilConfig.peers.firstIndex { $0 == peerURI })
} label: { } label: {
Image(systemName: "xmark.circle.fill") Image(systemName: "xmark.circle.fill")
} }
@ -38,13 +41,15 @@ struct PeersView: View {
appDelegate.yggdrasilConfig.peers.remove(atOffsets: indexSet) appDelegate.yggdrasilConfig.peers.remove(atOffsets: indexSet)
} }
#if os(macOS)
Button { Button {
appDelegate.yggdrasilConfig.peers.append("foo") appDelegate.yggdrasilConfig.peers.append("")
} label: { } label: {
Label("Add peer", systemImage: "plus") Label("Add peer", systemImage: "plus")
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.foregroundColor(.accentColor) .foregroundColor(.accentColor)
#endif
Text("Yggdrasil will automatically attempt to connect to configured peers when started. If you configure more than one peer, your device may carry traffic on behalf of other network nodes. Avoid this by configuring only a single peer. Data charges may apply when using mobile data.") Text("Yggdrasil will automatically attempt to connect to configured peers when started. If you configure more than one peer, your device may carry traffic on behalf of other network nodes. Avoid this by configuring only a single peer. Data charges may apply when using mobile data.")
.font(.system(size: 11)) .font(.system(size: 11))
@ -70,20 +75,27 @@ struct PeersView: View {
.foregroundColor(.gray) .foregroundColor(.gray)
} }
} }
TextField("Multicast password", text: $appDelegate.yggdrasilConfig.multicastPassword)
}, header: { }, header: {
Text("Local connectivity") Text("Local connectivity")
}) })
} }
#if os(iOS)
.toolbar {
// EditButton()
}
#endif
.formStyle(.grouped) .formStyle(.grouped)
.navigationTitle("Peers") .navigationTitle("Peers")
#if os(iOS) #if os(iOS)
.toolbar {
Button("Add", systemImage: "plus") {
appDelegate.yggdrasilConfig.peers.append("")
}
EditButton()
.onChange(of: editMode!.wrappedValue) { edit in
if !edit.isEditing {
try? appDelegate.yggdrasilConfig.save(to: &appDelegate.vpnManager)
}
}
}
.navigationBarTitleDisplayMode(.large) .navigationBarTitleDisplayMode(.large)
#endif #endif
} }
} }

View file

@ -16,7 +16,7 @@ struct SettingsView: View {
var body: some View { var body: some View {
Form { Form {
Section(content: { Section(content: {
TextField("Device Name", text: $deviceName) TextField("Device Name", text: $appDelegate.yggdrasilConfig.deviceName)
}, header: { }, header: {
Text("Public Identity") Text("Public Identity")
}) })

View file

@ -93,6 +93,8 @@ struct StatusView: View {
Text(appDelegate.yggdrasilVersion()) Text(appDelegate.yggdrasilVersion())
.foregroundColor(Color.gray) .foregroundColor(Color.gray)
} }
}, header: {
Text("Status")
}) })
Section(content: { Section(content: {
@ -114,7 +116,7 @@ struct StatusView: View {
.lineLimit(1) .lineLimit(1)
.textSelection(.enabled) .textSelection(.enabled)
} }
HStack { /*HStack {
Text("Coordinates") Text("Coordinates")
Spacer() Spacer()
Text(appDelegate.yggdrasilCoords) Text(appDelegate.yggdrasilCoords)
@ -122,7 +124,7 @@ struct StatusView: View {
.truncationMode(.tail) .truncationMode(.tail)
.lineLimit(1) .lineLimit(1)
.textSelection(.enabled) .textSelection(.enabled)
} }*/
HStack { HStack {
Text("Public Key") Text("Public Key")
Spacer() Spacer()
@ -133,7 +135,35 @@ struct StatusView: View {
.lineLimit(1) .lineLimit(1)
.textSelection(.enabled) .textSelection(.enabled)
} }
}, header: {
Text("Details")
}) })
if self.appDelegate.yggdrasilEnabled {
Section(content: {
List(self.appDelegate.yggdrasilPeers.sorted(by: { a, b in
a.key < a.key
}), id: \.remote) { peer in
VStack {
Text(peer.remote)
.frame(maxWidth: .infinity, alignment: .leading)
.truncationMode(.tail)
.lineLimit(1)
.textSelection(.enabled)
Text(peer.address)
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(Color.gray)
.font(.system(size: 11, design: .monospaced))
.truncationMode(.tail)
.lineLimit(1)
.textSelection(.enabled)
}
.padding(.all, 2)
}
}, header: {
Text("Peers")
})
}
} }
.formStyle(.grouped) .formStyle(.grouped)
.navigationTitle("Yggdrasil") .navigationTitle("Yggdrasil")