From 52ca049b50bd835348ca0b44f56b7aaef120cfb0 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Sun, 19 Jul 2020 14:20:15 +0100 Subject: [PATCH] Sanitised --- .gitignore | 69 ++ .../ConfigurationProxy.swift | 220 ++++++ .../CrossPlatformAppDelegate.swift | 151 ++++ Yggdrasil Network Extension/Info.plist | 31 + .../PacketTunnelProvider.swift | 136 ++++ ...ggdrasilNetworkExtension-Bridging-Header.h | 5 + .../AppDelegate+AppDelegateExtension.h | 16 + .../Application/AppDelegate.swift | 21 + .../Application/NSNotification.swift | 19 + .../AppIcon.appiconset/Contents.json | 104 +++ .../AppIcon.appiconset/drawing copy-1.png | Bin 0 -> 2734 bytes .../AppIcon.appiconset/drawing copy-2.png | Bin 0 -> 1755 bytes .../AppIcon.appiconset/drawing copy-3.png | Bin 0 -> 3487 bytes .../AppIcon.appiconset/drawing copy-4.png | Bin 0 -> 26573 bytes .../AppIcon.appiconset/drawing copy-5.png | Bin 0 -> 3815 bytes .../AppIcon.appiconset/drawing copy.png | Bin 0 -> 4132 bytes .../Assets.xcassets/Contents.json | 6 + .../YggdrasilNetwork.imageset/Contents.json | 23 + .../YggdrasilNetwork.imageset/img1.png | Bin 0 -> 11936 bytes .../YggdrasilNetwork.imageset/img2.png | Bin 0 -> 26071 bytes .../YggdrasilNetwork.imageset/img3.png | Bin 0 -> 40186 bytes .../Extensions/UIDevice.swift | 28 + Yggdrasil Network iOS/Info.plist | 76 ++ .../Base.lproj/LaunchScreen.storyboard | 55 ++ .../Storyboards/Base.lproj/Main.storyboard | 704 ++++++++++++++++++ .../UI Components/CopyableLabel.swift | 52 ++ .../UI Components/ToggleTableViewCell.swift | 26 + .../PeersViewController.swift | 312 ++++++++ .../SettingsViewController.swift | 159 ++++ .../SplitViewController.swift | 18 + .../TableViewController.swift | 133 ++++ Yggdrasil Network.xcodeproj/project.pbxproj | 670 +++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 5 + .../xcschemes/YggdrasilNetwork.xcscheme | 87 +++ .../YggdrasilNetworkExtension.xcscheme | 106 +++ YggdrasilNetwork.entitlements | 18 + YggdrasilNetworkExtension.entitlements | 18 + 39 files changed, 3283 insertions(+) create mode 100644 .gitignore create mode 100644 Yggdrasil Network Cross-Platform/ConfigurationProxy.swift create mode 100644 Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift create mode 100644 Yggdrasil Network Extension/Info.plist create mode 100644 Yggdrasil Network Extension/PacketTunnelProvider.swift create mode 100644 Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h create mode 100644 Yggdrasil Network iOS/Application/AppDelegate+AppDelegateExtension.h create mode 100644 Yggdrasil Network iOS/Application/AppDelegate.swift create mode 100644 Yggdrasil Network iOS/Application/NSNotification.swift create mode 100644 Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-1.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-2.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-3.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-4.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-5.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/Contents.json create mode 100644 Yggdrasil Network iOS/Assets.xcassets/YggdrasilNetwork.imageset/Contents.json create mode 100644 Yggdrasil Network iOS/Assets.xcassets/YggdrasilNetwork.imageset/img1.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/YggdrasilNetwork.imageset/img2.png create mode 100644 Yggdrasil Network iOS/Assets.xcassets/YggdrasilNetwork.imageset/img3.png create mode 100644 Yggdrasil Network iOS/Extensions/UIDevice.swift create mode 100644 Yggdrasil Network iOS/Info.plist create mode 100644 Yggdrasil Network iOS/Storyboards/Base.lproj/LaunchScreen.storyboard create mode 100644 Yggdrasil Network iOS/Storyboards/Base.lproj/Main.storyboard create mode 100644 Yggdrasil Network iOS/UI Components/CopyableLabel.swift create mode 100644 Yggdrasil Network iOS/UI Components/ToggleTableViewCell.swift create mode 100644 Yggdrasil Network iOS/View Controllers/PeersViewController.swift create mode 100644 Yggdrasil Network iOS/View Controllers/SettingsViewController.swift create mode 100644 Yggdrasil Network iOS/View Controllers/SplitViewController.swift create mode 100644 Yggdrasil Network iOS/View Controllers/TableViewController.swift create mode 100644 Yggdrasil Network.xcodeproj/project.pbxproj create mode 100644 Yggdrasil Network.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme create mode 100644 Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetworkExtension.xcscheme create mode 100644 YggdrasilNetwork.entitlements create mode 100644 YggdrasilNetworkExtension.entitlements diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f44e680 --- /dev/null +++ b/.gitignore @@ -0,0 +1,69 @@ +# macOS +.DS_Store +*.framework/ + +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.moved-aside +*.xcuserstate + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output diff --git a/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift b/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift new file mode 100644 index 0000000..6c02443 --- /dev/null +++ b/Yggdrasil Network Cross-Platform/ConfigurationProxy.swift @@ -0,0 +1,220 @@ +// +// ConfigurationProxy.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 07/01/2019. +// + +#if canImport(UIKit) +import UIKit +#elseif canImport(AppKit) +import AppKit +#endif +import Yggdrasil +import NetworkExtension + +#if os(iOS) +class PlatformItemSource: NSObject, UIActivityItemSource { + func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return "yggdrasil.conf" + } + + func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + return nil + } +} +#elseif os(OSX) +class PlatformItemSource: NSObject {} +#endif + +class ConfigurationProxy: PlatformItemSource { + + private var json: Data? = nil + private var dict: [String: Any]? = nil + + override init() { + super.init() + self.json = MobileGenerateConfigJSON() + do { + try self.convertToDict() + } catch { + NSLog("ConfigurationProxy: Error deserialising JSON (\(error))") + } + #if os(iOS) + self.set("name", inSection: "NodeInfo", to: UIDevice.current.name) + #elseif os(OSX) + self.set("name", inSection: "NodeInfo", to: Host.current().localizedName ?? "") + #endif + self.set("MulticastInterfaces", to: ["en*"] as [String]) + self.set("AllowFromDirect", inSection: "SessionFirewall", to: true) + self.set("AllowFromRemote", inSection: "SessionFirewall", to: false) + self.set("AlwaysAllowOutbound", inSection: "SessionFirewall", to: true) + self.set("Enable", inSection: "SessionFirewall", to: true) + } + + init(json: Data) throws { + super.init() + self.json = json + try self.convertToDict() + } + + private func fix() { + self.set("Listen", to: [] as [String]) + self.set("AdminListen", to: "none") + self.set("IfName", to: "dummy") + self.set("Enable", inSection: "SessionFirewall", to: true) + self.set("MaxTotalQueueSize", inSection: "SwitchOptions", to: 1048576) + + if self.get("AutoStart") == nil { + self.set("AutoStart", to: ["WiFi": false, "Mobile": false] as [String: Bool]) + } + let interfaces = self.get("MulticastInterfaces") as? [String] ?? [] + if interfaces.contains(where: { $0 == "lo0" }) { + self.add("lo0", in: "MulticastInterfaces") + } + } + + func get(_ key: String) -> Any? { + if let dict = self.dict { + if dict.keys.contains(key) { + return dict[key] + } + } + return nil + } + + func get(_ key: String, inSection section: String) -> Any? { + if let dict = self.get(section) as? [String: Any] { + if dict.keys.contains(key) { + return dict[key] + } + } + return nil + } + + func add(_ value: Any, in key: String) { + if self.dict != nil { + if self.dict![key] as? [Any] != nil { + var temp = self.dict![key] as? [Any] ?? [] + temp.append(value) + self.dict!.updateValue(temp, forKey: key) + } + } + } + + func remove(_ value: String, from key: String) { + if self.dict != nil { + if self.dict![key] as? [String] != nil { + var temp = self.dict![key] as? [String] ?? [] + if let index = temp.firstIndex(of: value) { + temp.remove(at: index) + } + self.dict!.updateValue(temp, forKey: key) + } + } + } + + func remove(index: Int, from key: String) { + if self.dict != nil { + if self.dict![key] as? [Any] != nil { + var temp = self.dict![key] as? [Any] ?? [] + temp.remove(at: index) + self.dict!.updateValue(temp, forKey: key) + } + } + } + + func set(_ key: String, to value: Any) { + if self.dict != nil { + self.dict![key] = value + } + } + + func set(_ key: String, inSection section: String, to value: Any?) { + if self.dict != nil { + if self.dict!.keys.contains(section), let value = value { + var temp = self.dict![section] as? [String: Any] ?? [:] + temp.updateValue(value, forKey: key) + self.dict!.updateValue(temp, forKey: section) + } + } + } + + func data() -> Data? { + do { + try self.convertToJson() + return self.json + } catch { + return nil + } + } + + func save(to manager: inout NETunnelProviderManager) throws { + self.fix() + if let data = self.data() { + let providerProtocol = NETunnelProviderProtocol() + #if os(iOS) + providerProtocol.providerBundleIdentifier = "eu.neilalexander.yggdrasil.extension" + #elseif os(OSX) + providerProtocol.providerBundleIdentifier = "eu.neilalexander.yggdrasilmac.extension" + #endif + providerProtocol.providerConfiguration = [ "json": data ] + providerProtocol.serverAddress = "yggdrasil" + providerProtocol.username = self.get("EncryptionPublicKey") as? String ?? "(unknown public key)" + + let disconnectrule = NEOnDemandRuleDisconnect() + var rules: [NEOnDemandRule] = [disconnectrule] + if self.get("WiFi", inSection: "AutoStart") as? Bool ?? false { + let wifirule = NEOnDemandRuleConnect() + wifirule.interfaceTypeMatch = .wiFi + rules.insert(wifirule, at: 0) + } + #if canImport(UIKit) + if self.get("Mobile", inSection: "AutoStart") as? Bool ?? false { + let mobilerule = NEOnDemandRuleConnect() + mobilerule.interfaceTypeMatch = .cellular + rules.insert(mobilerule, at: 0) + } + #endif + manager.onDemandRules = rules + manager.isOnDemandEnabled = rules.count > 1 + providerProtocol.disconnectOnSleep = rules.count > 1 + + manager.protocolConfiguration = providerProtocol + + manager.saveToPreferences(completionHandler: { (error:Error?) in + if let error = error { + print(error) + } else { + print("Save successfully") + NotificationCenter.default.post(name: NSNotification.Name.YggdrasilSettingsUpdated, object: self) + } + }) + } + } + + private func convertToDict() throws { + self.dict = try JSONSerialization.jsonObject(with: self.json!, options: []) as? [String: Any] + } + + private func convertToJson() throws { + self.json = try JSONSerialization.data(withJSONObject: self.dict as Any, options: .prettyPrinted) + } + + #if canImport(UIKit) + override func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return "yggdrasil.conf" + } + + override func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + return self.data() + } + + func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String { + if let pubkey = self.get("EncryptionPublicKey") as? String, pubkey.count > 56 { + return "yggdrasil-\(pubkey.dropFirst(56)).conf.json" + } + return "yggdrasil.conf.json" + } + #endif +} diff --git a/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift new file mode 100644 index 0000000..249bd47 --- /dev/null +++ b/Yggdrasil Network Cross-Platform/CrossPlatformAppDelegate.swift @@ -0,0 +1,151 @@ +// +// AppDelegateExtension.swift +// Yggdrasil Network +// +// Created by Neil Alexander on 11/01/2019. +// + +import Foundation +import NetworkExtension +import Yggdrasil +import UIKit + +class CrossPlatformAppDelegate: PlatformAppDelegate { + var vpnManager: NETunnelProviderManager = NETunnelProviderManager() + + #if os(iOS) + let yggdrasilComponent = "eu.neilalexander.yggdrasil.extension" + #elseif os(OSX) + let yggdrasilComponent = "eu.neilalexander.yggdrasilmac.extension" + #endif + + var yggdrasilConfig: ConfigurationProxy? = nil + + var yggdrasilAdminTimer: DispatchSourceTimer? + + var yggdrasilSelfIP: String = "N/A" + var yggdrasilSelfSubnet: String = "N/A" + var yggdrasilSelfCoords: String = "[]" + + var yggdrasilPeers: [[String: Any]] = [[:]] + var yggdrasilSwitchPeers: [[String: Any]] = [[:]] + var yggdrasilNodeInfo: [String: Any] = [:] + + func applicationDidBecomeActive(_ application: UIApplication) { + if self.yggdrasilAdminTimer == nil { + self.yggdrasilAdminTimer = DispatchSource.makeTimerSource(flags: .strict, queue: DispatchQueue(label: "Admin Queue")) + self.yggdrasilAdminTimer!.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(2), leeway: DispatchTimeInterval.seconds(1)) + self.yggdrasilAdminTimer!.setEventHandler { + self.makeIPCRequests() + } + } + if self.yggdrasilAdminTimer != nil { + self.yggdrasilAdminTimer!.resume() + } + + NotificationCenter.default.addObserver(forName: .NEVPNStatusDidChange, object: nil, queue: nil, using: { notification in + if let conn = notification.object as? NEVPNConnection { + self.updateStatus(conn: conn) + } + }) + + self.updateStatus(conn: self.vpnManager.connection) + } + + func updateStatus(conn: NEVPNConnection) { + if conn.status == .connected { + self.makeIPCRequests() + } else if conn.status == .disconnecting || conn.status == .disconnected { + self.clearStatus() + } + } + + func applicationWillResignActive(_ application: UIApplication) { + if self.yggdrasilAdminTimer != nil { + self.yggdrasilAdminTimer!.suspend() + } + } + + func vpnTunnelProviderManagerInit() { + NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in + if let error = error { + print(error) + } + + if let savedManagers = savedManagers { + for manager in savedManagers { + if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.yggdrasilComponent { + print("Found saved VPN Manager") + self.vpnManager = manager + } + } + } + + self.vpnManager.loadFromPreferences(completionHandler: { (error: Error?) in + if let error = error { + print(error) + } + + if let vpnConfig = self.vpnManager.protocolConfiguration as? NETunnelProviderProtocol, + let confJson = vpnConfig.providerConfiguration!["json"] as? Data { + print("Found existing protocol configuration") + self.yggdrasilConfig = try? ConfigurationProxy(json: confJson) + } else { + print("Generating new protocol configuration") + self.yggdrasilConfig = ConfigurationProxy() + } + + self.vpnManager.localizedDescription = "Yggdrasil" + self.vpnManager.isEnabled = true + + + if let config = self.yggdrasilConfig { + try? config.save(to: &self.vpnManager) + } + }) + } + } + + func makeIPCRequests() { + if self.vpnManager.connection.status != .connected { + return + } + if let session = self.vpnManager.connection as? NETunnelProviderSession { + try? session.sendProviderMessage("address".data(using: .utf8)!) { (address) in + self.yggdrasilSelfIP = String(data: address!, encoding: .utf8)! + NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) + } + try? session.sendProviderMessage("subnet".data(using: .utf8)!) { (subnet) in + self.yggdrasilSelfSubnet = String(data: subnet!, encoding: .utf8)! + NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) + } + try? session.sendProviderMessage("coords".data(using: .utf8)!) { (coords) in + self.yggdrasilSelfCoords = String(data: coords!, encoding: .utf8)! + NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) + } + try? session.sendProviderMessage("peers".data(using: .utf8)!) { (peers) in + if let jsonResponse = try? JSONSerialization.jsonObject(with: peers!, options: []) as? [[String: Any]] { + self.yggdrasilPeers = jsonResponse + NotificationCenter.default.post(name: .YggdrasilPeersUpdated, object: nil) + } + } + try? session.sendProviderMessage("switchpeers".data(using: .utf8)!) { (switchpeers) in + if let jsonResponse = try? JSONSerialization.jsonObject(with: switchpeers!, options: []) as? [[String: Any]] { + self.yggdrasilSwitchPeers = jsonResponse + NotificationCenter.default.post(name: .YggdrasilSwitchPeersUpdated, object: nil) + } + } + } + } + + func clearStatus() { + self.yggdrasilSelfIP = "N/A" + self.yggdrasilSelfSubnet = "N/A" + self.yggdrasilSelfCoords = "[]" + self.yggdrasilPeers = [] + self.yggdrasilSwitchPeers = [] + NotificationCenter.default.post(name: .YggdrasilSelfUpdated, object: nil) + NotificationCenter.default.post(name: .YggdrasilPeersUpdated, object: nil) + NotificationCenter.default.post(name: .YggdrasilSwitchPeersUpdated, object: nil) + } +} diff --git a/Yggdrasil Network Extension/Info.plist b/Yggdrasil Network Extension/Info.plist new file mode 100644 index 0000000..adbd0a7 --- /dev/null +++ b/Yggdrasil Network Extension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Yggdrasil Network Extension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 0.1 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + diff --git a/Yggdrasil Network Extension/PacketTunnelProvider.swift b/Yggdrasil Network Extension/PacketTunnelProvider.swift new file mode 100644 index 0000000..768da36 --- /dev/null +++ b/Yggdrasil Network Extension/PacketTunnelProvider.swift @@ -0,0 +1,136 @@ +import NetworkExtension +import Foundation +import Yggdrasil + +class PacketTunnelProvider: NEPacketTunnelProvider { + + var yggdrasil: MobileYggdrasil = MobileYggdrasil() + var conduit: DummyConduitEndpoint? = nil + var yggdrasilConfig: ConfigurationProxy? + + @objc func readPacketsFromTun() { + if let conduit = self.conduit { + autoreleasepool { + self.packetFlow.readPackets { (packets: [Data], protocols: [NSNumber]) in + for packet in packets { + conduit.send(packet) + } + self.readPacketsFromTun() + } + } + } + } + + @objc func writePacketsToTun() { + while let conduit = self.conduit { + autoreleasepool { + if let data = conduit.recv() { + self.packetFlow.writePackets([data], withProtocols: [NSNumber](repeating: AF_INET6 as NSNumber, count: 1)) + } + } + } + } + + func startYggdrasil() -> Error? { + var err: Error? = nil + + self.setTunnelNetworkSettings(nil) { (error: Error?) -> Void in + NSLog("Starting Yggdrasil") + + if let error = error { + NSLog("Failed to clear Yggdrasil tunnel network settings: " + error.localizedDescription) + err = error + } + if self.yggdrasilConfig == nil { + NSLog("No configuration proxy!") + return + } + if let config = self.yggdrasilConfig { + NSLog("Configuration loaded") + + do { + self.conduit = try self.yggdrasil.startJSON(config.data()) + } catch { + NSLog("Starting Yggdrasil process produced an error: " + error.localizedDescription) + return + } + + let address = self.yggdrasil.getAddressString() + let subnet = self.yggdrasil.getSubnetString() + + NSLog("Yggdrasil IPv6 address: " + address) + NSLog("Yggdrasil IPv6 subnet: " + subnet) + + let tunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: address) + tunnelNetworkSettings.ipv6Settings = NEIPv6Settings(addresses: [address], networkPrefixLengths: [7]) + tunnelNetworkSettings.ipv6Settings?.includedRoutes = [NEIPv6Route(destinationAddress: "0200::", networkPrefixLength: 7)] + + NSLog("Setting tunnel network settings...") + + self.setTunnelNetworkSettings(tunnelNetworkSettings) { (error: Error?) -> Void in + NSLog("setTunnelNetworkSettings completed successfully") + if let error = error { + NSLog("Failed to set Yggdrasil tunnel network settings: " + error.localizedDescription) + err = error + } else { + NSLog("Yggdrasil tunnel settings set successfully") + + let readthread: Thread = Thread(target: self, selector: #selector(self.readPacketsFromTun), object: nil) + readthread.name = "TUN Packet Reader" + readthread.qualityOfService = .utility + + let writethread: Thread = Thread(target: self, selector: #selector(self.writePacketsToTun), object: nil) + writethread.name = "TUN Packet Writer" + writethread.qualityOfService = .utility + + readthread.start() + writethread.start() + } + } + } + } + return err + } + + override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { + if let conf = (self.protocolConfiguration as! NETunnelProviderProtocol).providerConfiguration { + if let json = conf["json"] as? Data { + do { + self.yggdrasilConfig = try ConfigurationProxy(json: json) + } catch { + NSLog("Error in Yggdrasil startTunnel: Configuration is invalid") + return + } + if let error = self.startYggdrasil() { + NSLog("Error in Yggdrasil startTunnel: " + error.localizedDescription) + } else { + NSLog("Yggdrasil completion handler called") + completionHandler(nil) + } + } + } + } + + override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + try? self.yggdrasil.stop() + super.stopTunnel(with: reason, completionHandler: completionHandler) + } + + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) { + let request = String(data: messageData, encoding: .utf8) + switch request { + case "address": + completionHandler?(self.yggdrasil.getAddressString().data(using: .utf8)) + case "subnet": + completionHandler?(self.yggdrasil.getSubnetString().data(using: .utf8)) + case "coords": + completionHandler?(self.yggdrasil.getCoordsString().data(using: .utf8)) + case "peers": + completionHandler?(self.yggdrasil.getPeersJSON().data(using: .utf8)) + case "switchpeers": + completionHandler?(self.yggdrasil.getSwitchPeersJSON().data(using: .utf8)) + default: + completionHandler?(nil) + } + } +} diff --git a/Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h b/Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h new file mode 100644 index 0000000..cab69e9 --- /dev/null +++ b/Yggdrasil Network Extension/YggdrasilNetworkExtension-Bridging-Header.h @@ -0,0 +1,5 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "GCDAsyncSocket.h" diff --git a/Yggdrasil Network iOS/Application/AppDelegate+AppDelegateExtension.h b/Yggdrasil Network iOS/Application/AppDelegate+AppDelegateExtension.h new file mode 100644 index 0000000..3809560 --- /dev/null +++ b/Yggdrasil Network iOS/Application/AppDelegate+AppDelegateExtension.h @@ -0,0 +1,16 @@ +// +// AppDelegate+AppDelegateExtension.h +// Yggdrasil Network +// +// Created by Neil Alexander on 11/01/2019. +// + + + +NS_ASSUME_NONNULL_BEGIN + +@interface AppDelegate () + +@end + +NS_ASSUME_NONNULL_END diff --git a/Yggdrasil Network iOS/Application/AppDelegate.swift b/Yggdrasil Network iOS/Application/AppDelegate.swift new file mode 100644 index 0000000..3e85676 --- /dev/null +++ b/Yggdrasil Network iOS/Application/AppDelegate.swift @@ -0,0 +1,21 @@ +import UIKit + +#if os(iOS) +class PlatformAppDelegate: UIResponder, UIApplicationDelegate {} +#elseif os(OSX) +class PlatformAppDelegate: NSObject, NSApplicationDelegate {} +#endif + +@UIApplicationMain +class AppDelegate: CrossPlatformAppDelegate { + var window: UIWindow? + + let configDir = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true)[0] + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch + self.vpnTunnelProviderManagerInit() + return true + } +} + diff --git a/Yggdrasil Network iOS/Application/NSNotification.swift b/Yggdrasil Network iOS/Application/NSNotification.swift new file mode 100644 index 0000000..537668c --- /dev/null +++ b/Yggdrasil Network iOS/Application/NSNotification.swift @@ -0,0 +1,19 @@ +// +// NSNotification.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 20/02/2019. +// + +#if canImport(UIKit) +import UIKit +#elseif canImport(AppKit) +import AppKit +#endif + +extension Notification.Name { + static let YggdrasilSelfUpdated = Notification.Name("YggdrasilSelfUpdated") + static let YggdrasilPeersUpdated = Notification.Name("YggdrasilPeersUpdated") + static let YggdrasilSwitchPeersUpdated = Notification.Name("YggdrasilSwitchPeersUpdated") + static let YggdrasilSettingsUpdated = Notification.Name("YggdrasilSettingsUpdated") +} diff --git a/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/Contents.json b/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..bef1a29 --- /dev/null +++ b/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,104 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "drawing copy-1.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "drawing copy.png", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "drawing copy-2.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "drawing copy-3.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "drawing copy-5.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "drawing copy-4.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-1.png b/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-1.png new file mode 100644 index 0000000000000000000000000000000000000000..eee5a75518207af0d7b982f88780889f5973d8c8 GIT binary patch literal 2734 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 z z&tD^_kx(<#W|Rea9&C%FdUhTxhYP70{5yIP73n8}t|kwW%{DKs0SuGXa(HRtI8>7f z`x_xIIP^(;h4xy!J~M>NX*M?VEf-P`<`AW-+ds8f#?vwUJ$#m0!E6{(4d&(xI#xx| zRJ<5E21Qe;P1!J{nS77L2iR)7JaGb-Qta(BQ4XXb(k<`-ww$A}F}xG~irS2dK`QbE zffpCv$V4WNmqMSKU3TR`x-K%4%aXOt8VNOn7e+tCf=LsqYeD)5m(H1A#8!f=mc#EZ ze~eH%Mr}&PAr<*)?!lk|_`~=oIGva(__t<+ROF{s?!jJ}IEfGDMySou4PEnKh3>ql zv2h|86@Q*MfqzEKRiRocq^pWDs*0qVfv3u*i!=a#y7D>Rikzo5MR&;OX~Ig9+5|(< zpy7|9<5e-Z00wIEC({YEIjiFsn8jQczaKq{Ph;cMrWgkK{$Q6*e( zoZ>STV4x-+0OHv+wFwpBMC=M)9{c0xi}(Z| zRs|?OL{{jNy(h4>GB?r@4I0iR!WfK>;-lyY=1j7?APbO+e7h)Npmtl{wZ&W(c0qzj zTu!ZO8qOxBFc=-h(dgx>Op;atQjtGUyA3t+_3)Td75wDvn@Fk(_5@n7uf7v~zMvr+ z11p+}3&|Orj!)rCVhZOHVJz}A!-gT9i#*uallS`XlczDAUfLe=y~stp7rBTAy9f8x zwc%F(CT#Zx;S@LiotV!qVk|X>@x>S}r{*w{isC{tf($R=22fd5PPVG3%hP}^Ps6p> z^VvnbF?Dvun+s=>cy00wUYk4vNf6NN@L{VffS}!rdb=9|*$JB{t+>CEQxVIi5znSE zpIuz~Q5G@2IFFb`@@{5;OoJ`=UT;sIN2WjtwW?_drD6!Bcp}p*kQ5yHWe4u9YrFPZ z(Nz3vx;hH*s*Mr^P32tOHygWPzagxB5*@>wlB(GKIRvaRJPa540tD=D?9RLPpYR3h zd@n~Q3>W#k0$b4JxL!FC$s}=No@d0Zis2&vV6Zpu^+z)!rrVmEQ#=MHS~WB~e7HTZ zIq&M=>jdy?xRWyL$Pp)AF$QC!+IE^!8$$l=<((s z88bLD0u9x0xkK0wa; z=064Hr+ylU=xhp(-UnS^TuG3$$2`#Rg71z(k zr}b=>!w|ZL$o);-E3V}<4Hpv;J)1Qi?u^$&H<9m^9T=!@TXB7KA&OMitb4QakyI+W z<-xul?8qxByvl_{1iYiF2i-i_{#C?101PK)$i^2R*2bet!C{A|4(*=R&*J_v9^oBb zMd%{(_cnH{dOfFUxRfN^qm+DPlN(?-y42*aHNeJFb4aUZ75JQT_#dgr#H4o#F|_~y N002ovPDHLkV1l#?mjnO+ literal 0 HcmV?d00001 diff --git a/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-4.png b/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-4.png new file mode 100644 index 0000000000000000000000000000000000000000..cc8b151ef1b617508ec3816a4fc623e4b3f45251 GIT binary patch literal 26573 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 Jg8t3_{twQoX<7gP literal 0 HcmV?d00001 diff --git a/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-5.png b/Yggdrasil Network iOS/Assets.xcassets/AppIcon.appiconset/drawing copy-5.png new file mode 100644 index 0000000000000000000000000000000000000000..d658df22e3a49db791a98d67028bf50c66260c83 GIT binary patch literal 3815 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 literal 0 HcmV?d00001 diff --git a/Yggdrasil Network iOS/Assets.xcassets/YggdrasilNetwork.imageset/img3.png b/Yggdrasil Network iOS/Assets.xcassets/YggdrasilNetwork.imageset/img3.png new file mode 100644 index 0000000000000000000000000000000000000000..cf5a726958622c8493dc7546184bc86f7e8ad84a GIT binary patch literal 40186 zcmYgY1y~c@*I%$9ty0p`UD6E#!blw<4T7Z7-JAH*jUY%0Qye8oNHdj^)JaKB>F$#H z=KmGnj|Uze?rxlW?m52`_grG`>S>S>(-Q*#05X`S8XN$C6aWCgu`5t~OH-b{KmHei z_idQr75rDom50&zzll6FO}zmCDxKdyz&i={|8?70DpgfA!oNo z-gXZ>?1kLD9J4p%=>dS70GOJJVL;B>lz*nB(eTmsCMxA=@@;Am9HnVMfVxu128U4d z^WC&W5u_9@@gKz!OSbQT3K0cw-W2c@7Eq};Ytb%0;_M7MDiXEOjaMr zEQTo!R_C`%Pu31J*bi7bZq}-#a=?Cnqekc;7Q|&aa@YR%CkG7P!bs)=l3UbM`_sda zg&$N6gDmMMb?3zYJBS0Vm`ajZypYN`GAI!ys%B207jKgRma?dhkgMhgP67^?Q=QoKra2E`88UI|8VLSq% z!CsIqb?v<3`ZImWyWQosqe&w96k&-=Rv5Vw2-xC4d*8mlOZzj_XrzPG0zZ9TZ$|Fr zY^l>7xQ<0V8;1&l%N-1hLF>~xiN6Rx?74TzM->gM$#w%}fT%&@CDUR}$um`EAxjN` z*k_ju^y@n}9N+IVSF~8IH5>mJpB$G#R8W*f5LKW@AvfViuRW~|K1YygF0Uh z>G-KSRP3w(&bznb@F$S=p+WmktJa#6nZfT9AQ`ay(_{$G z2JSjp0)KrS+q-0ueGP|5?#S;&qSz^vFFPu-BjTK0f3rzs=jNZ4Ve`A{X4d!_H5;>@@eY!jig?fQZBa-{h6 zBi>4*PR9R8?e_r`1DhNMKK)g!;XWdaVnZ6{zlI)KWRVy5*LlwW)}x35TKD9tQ3#tM z{+u9cMec?)umAOyzAC^arNakAxVcH#-Uqr%MK*j83{-;ln5@?R?L=KGyv5#Q8UoEs zlLKBdXO|p61Ip!OmZS39`@?_KZN@7CY~jej%I$+EiPo2=f+*^?HgWTLZ1zRwQdVon zzRn5rNr{aJFNkaY>1Sro;lX!I($k+fO6_^;^6<(F&d9M30 zDStYXO%Wn#KU>SYb!1MD8?gV`D}5|EP8-9vLz`$W;Re1j{P7YDU@ij$I04*Q)~-`+ zmd)RD^)e-@2aNIfvq3T;B5eLNe>ON2Sx0aVQ-~YI@o!wL8^ zPQ*7P#iAYuLg5k;dAr9kZ;`%U+}Gy<{}VlGR|O6T=BC>{UmKwJW}6>6b=LX!`4nA5w|z?Fvz@VAS4gzk(B4VRdd;-J7n z)~t5^(cQFj=aX#FpAB(L6HIEJKXK3MWP{(c`qL;*FpHo>K<0#xGF;`w;&=J}Fwu2= z3KKdfyqDoS`8~Ao4%z(LA5elbn2||m86u1?^48AdAeV4YKPHXkiqHVti7mn*G?#op z1Le{j6yCm&OCb&{IT2>}a=6qS&iy;M>$uwjic+h~8*L_kh5orNWPrepU^-pqc}Y;= ziSU1bX~HOd<4C?zZNZ{s18us4Os{_IKn^Fx!Gw^ z4o3WunBg8tOk210oATL!)|kahtLkE>jnmqpbzYTs+YJ1uLAvwwa++aR=qaS0c#O|m z$oJ4@@IsXD{-t~vRth480Tc6js08_;-SEF#FqAR}X@EBeay1#epnr@j%541l(e+B- z4Yl#izbvbLN9G}4>Aaz~*Yg(|jJ{sMfiVfn#YN(F9mqWKKc~T0Es9~rgm+s%_HFj+ z1K&9adH47dgwVuXpnL*yaK>Ox+^>Txm6eN1|Iy;m3m^nWfedaf79yucPgwrq6km}M ztUC7)(&;#$p70l7&_j|yMR0Sz)!9>{pu@v+!oL9J#QO=l5AMhMg!+$6W7vr#i)Y^db$!Sjv~hQB?!za@(8Y2mCrW?WyV83Dr+~SJ z;lj_>V0tOtL&-1W9v)XF8(tT0S#@tt(c>}hBsz8mxc+g|67vefK2#F zB=+UN6Fz`|Pl-9*V-sHE-~^V2fCHTIGy)Ik+#r4)L&a|k~f^Tw(>eFE^U1;0LHC`|2N1SA8$)0oodv zjL}qC08k?TNjR8hV?ARF|v>ySqsTlofmKQoHF=v=u%sM7q)0#t$ zBvBZaYU=}7O|GyU-TZYDgzB#_lZkQST7L1fvtnBq`}($Uok%2y(J&R>bwQdar3L+x z(xuAB`*|gAsgeEwb09<7hDqYUi+eN7^Yc&k1TjXKP*X9u`z-Q3z@1Q%vn7vfQRfYd za5C}Niw*iwfK>8^j#etliviZyaAu6%U(LY4v%E}|8x_y^x43ccsA1F8A)&?-`S6Gu zEi){Cu2!Syw@Pv>Q?3O~%0}br@Gr9xmIutvfmXu;(8pwJpyo7jhT0NlPRk{0hy{#~ zPqtcOq!BfWCCAHqX7e)X_c%-~YV_MoIm$d<(l4_+{YEF zIAVF2fZ1rpi8Y=pG6f{>b+nxfE=%*5UrRD*v+QXK?r2jU56AK1vMSn?K=R5#q4Hn$ z-AnSMQq0(>9++b{LGx9dl}gL9b@8PoP%P0Yll1#Xb9p*@GPqJmSEh<+TAd#->^^4p zKr(&kqb?R>4A8sTA5yW4qD51WuA%In@5hDHI$3 zerXZM+cP!cblv0Xx;f9pgH9+AAZ@l*1wXvP04N4k#x)L6|Ej+RBoO4XcfYvr$L(yr;#IyW>uV<&lFS=E zI_nFesC4y)nXZOT3+$psdn?&*5@-ruJ@-vE4pV@g4{AjfmDo9&;fk^-eomE3N5=+{ z1qRV8X*4+G>vQh==joqI8Krkw;0$~AC+{$*zX*KzBk*We78_}VcU9cZ0-n_L9x`MH zFqd?L_P%u8#@=emMc;XQBs-Q{zj%l%uydi+1}kx2oYV=-hGF{$*h~@3A$S)f)fZXX zeer$C!!WewvG?Jw7<0SNxW7S;h2Y(TXz6-rJYg%SkM$xOtv3t7?bcW)E#+!zfA-G5 zXiQK7JQClQuXzDkvh9+<bVl%K7}1nX_SR+!kK`Z0wmnhVd* z@XXQmoY@vorce}6E}cq%CPJ;)E*4*Yy0`DCBv>OKDE0Zsi=W~(TOIpg!?|7i2ML?| zIF+5Ylok1nB86CJDWHw>;<#crQ+tI|zM`ra^-4gwE1nxMi}*~Yz&~sQY^n(cwb9w^(1sW4Y&+HsKQwrh|@RNo%}#5Jxm8({-Z3I0^9M-qbnqW4?g+F`n|4#7iJb zKZjdQcnT1-s$29zNxlSj+F?`-gFoFS`O=XDxFt$MX2T7Y~^;X}GkCFC$PtifpWx!~`AY8^2A>=1;v-WQ3 zugzzZdK@#}M=gli(zJe9DUo;EVTt^}&3$_&A(Af<6eM3(w?>qF7NNaRU3ZYq>F+ zfaueFS8|QqE_>2XHceLfpi_HkVB`Z{9DkOr##qwPIXYPgyU}~qUmyEgz0@(f{M;2d z33!|;m%>#>Y@rdPi>=+_n zro{CTBJ83lAKthFD8{}xx3TxSF2X6jU}0kozo*n=f|HsHK6~RC2edAn)Gk(nS|q9` z@C7L0<6sV|pOlz3#J-=tKcYySj(rb$f;ZHXKc=Xy3acB z4h25C9r~i6Hg54)Z$s^u=S>eu^xX=tRRVI5P)~03$1?TC8^@X(YKD9asfzDiKa15n zH&8CU)Ek{F_kmX4+u-2axQ4A=uxp@9F?3!{t&pSWctpyJ?Z=SZ`!K?`zuY6*A1qm^ z^+Us?*%Kvq@pWh+MsOgas7f@~tF5h7`P_TX>9GLY{(IqALFpa^8X))G z0nXDpT(?xBEAH6Y@lmpI@qAcb5cz%aX6(zkaJ>>mX7-kmI2)404^e{BhJ;KghC1Pt z3$gwmPF>rsk?1Vp7=NHOvm1t$UY~@#j-XZ9Z2E>XvvYObvC1}Asw5!5`>s6M;3te6 zOnvFrFZp9d|DhZ`oDFVoGKMFc9Or~vtT$5;mdmJ_vkJ|=bL=+Gm??+6Zu(DpiiOiY{4+~2A zj!A_u`=_5~oepZGcj?6DKl@3#^dU;Dz{q|Rl849gtWRGD?5bFKzy; zCWC+;mqKwGA{{Y|FmhfWm{t#bRs)+1ZJ=E4%ES13bBpR!2OQV#T}8JY#Gyuq9%EOY zrl3G-gQG*YnorHl(5sF;WohCOnhnKngD$4`yYP{KyvJn~79KoTKkS~XwD`>5LJ z!1hHmYKW(eWZ+@`K>Y>cbIu*3={6dVkn%d=?L}wBO3(HWzA`NZvpimtutw2JhOIkT z+6v>$E6p)J)eBviHkg0B7mNGhk?^v%rfCaVruO;nP+vfz57>cznXAuBP?oj`Rz2ho zZ6&r)`Q?>$5)ho&{UlYapj4`&h*8Ws7nmC^Kp@Go7UROs4wFK9D+!+^EI+$|I;C&{ z_0ui_mpF6*jj2OFo#HAcBVAl^2ADu9xgrmt$}^Fj$B|-q$729tJX}V88h4uCMjpkU zz=8#-PD%KXZt3qlhQC7?nzV4LuDvv&Khht~PifU!$!GYXiCa~vinJs50HQVa?NPTM zKl_Xlo|Xe`f?q>BHn|pwx}2vHUY<|RNW_X{)WB`UouRWPSeKY?vPknEW&*eU zC#a8fH#nf%irD^Pzdd#A)9jpcl2eu~N?ZzvIh+cu*(`^?DqE+X)O7Vo#PjXuU`p#D zl`~Z26{2eja%2finwT)OZ9CNbv~7jc!5%uo=J6*ImqXMXQ}a|_WB-hAhuY$WFZuT3 zqMn)mHJQwH-kFW{maZAz(OaX}P{Ozw0_K|=^77nbkZp3L)krcHLNj)g%bm5!a(ByzvJvXx(KzgTTnQZe&l{@&)QJ=PZs;&f(aeA;F>wUBWc1c;dv$yVlylvAP9l^d|EVvp8 z5KeJOXl|`6a!(+Khkuy4_n7s92%b{dH^6zZ`nt`NY>WBDW;TnfJ$&Y{aOQb_f^2~m z8}XMYLZ)76EbaNcW{b>wz%&KpZ9cXP+Orz zd9aZ1P65Ad=#COaLZTFv4|&g8+ML`>af<)Qj4kFI)p9#B^+IKv*Bs$yV}daLUVg{y zLH>z<-lrb{`}GN_ChrJ$lzf=d_3M?WqtR_lH0g~S)wv3oSly3URgVM5|JVp&3aKa%amV|AR} zHNw$?&loy!f6@jwJ@bEErYu+e)wT;+#>A0?&AV*bluc_*i%0h`+hoHR*TWSzxZV6$ zXCo5PU#pV$IML*^CfdnXO68eV2jb2V9k-J)RV6MxlpE%>LsvXl|9eQW;3#r z(FT2cKuxAxJI~uW-)SSvX40Vb;_BfI0)1w~IwaZGNp9UdWBKI2`wE{Mc6<0+6m)LZ zUgHpzDDo=CwBbiDU_%L7-tRA9C}i>-hCh=CG@t9;nt=_TPCnII*-1jnpK; z=GiqVwqlD5rZ%4#KT9r~?6f5|c3C5a;}j;>I@A1O-ThvF*p^9MkH80nk9EwhUH5E_ zlojHv12_X4-LsS3g`)yWot-Xg0TMuoZy(<==%6?{HN69Dzp8ItSYJPu8W&!SJkKQYNi7+ z_c|IZ;|sRGyNJE%Z1`LSFL+yp5O0z?q3w$Go;z{bU1^BJ?bx=iI>96y`w zeENs14swjn&V`=0G-E=-+k3XfDT}B+E>~Q}1bl9}qan2YL&}D6#!H(3&Pt09U1r9s zK&h`64OuFDLdGhI>7bS_v1ji$ZNGM=m^G97%!;@ee7l1W4qx0uWfxs=UIbSxX6pWG zx&8sPhmmAIKRn65omghkGvrnB-BW#4Ut@VqXH8~GcA?VDg&)q)8pkt8toEmkMi0IIb`~;aY zYye3|-e4(l*r{Gm8+zM=Nn>*Lq9;?8uU!w7J!by^7_{{OXeN^YYSoxrEdBJusXrjb z>#TM28~L1^$|jj~>v?m+5e>%$!_4_-eMvuX&Ml?(i~{2a10Vv_FjP4dt&=y0rf>=OhZ{@W64>D%U?q^l;DG5%`w zoGkI=cK*lk>)`!4R1~8S$3|`2qc5Vh;D5Q)V^8cnlk{fLIO@~aMo&8GZOWaTf+$V7 zk~<9GNztbvQF4WT{mif#h9ot7`sKktEcO!XsxD322YrF!vp06alC2Ut7F(MW4cny? zb`Cz(0j6Y`#Ha7KE^Uif`cy?d0ye5lTtAs?lUt~Km#yVues?HPzK~zDE$gITLYNN zB*y`N<{fq?@OaCn{2cfZf|IcHBo`9VJAAY1Qgf|+U!_faDRcave+658;^}=ivlS2N z5~-1*jypBKfK7b>a)G5PLhl=uDmKj$h$8T;$_v4RL5dZgvh8p)w?@#X6q%Jf?9rKz|ck z5FtL&>O2ZEom=ah2iwiRBShDmJ`)Q%Ju`)~%(9g$TaeUiY7=xe%86ufsE?CK=lkp$ z!F?@tfRx{2g|K&Mw1UT#Pn)w@mJqF-mc=yONYy<7V?=je$eo@{v&_ z@kEu7{U%eYW3w9SOq47g(P^|{c#2`@F0zeg8*|N-ZvY~6CHkBBSIAHIt23ic^yqZu zXN+PN3!tVSYsrn@nKDyZH%57vHgXN^W(Zne@E!-UgLdd?Of5H$%#&<0f!NVi1@Rmnw3-%h1`QIbs^ZF z*zOOhjB`|`H<={?_11&dc&ZWl(9Q^XG4l!ny`HQ0JH}re(Iwy=Im?Mob@~^2*vA>! zV(sTQor}cgcSG*x$iByA-l-b>?nt_IF-2x!Zu3}kbmWAVksbRHV{5n)xX{2r!@smnHTk3fXD!V{E5O~yoQ;D*Q?gtVT zL+2|ZB{Gr?vV*}(b)iC#y3e`MsElBOBma98%%%vaG@{;ALLn4rQVvO~mUWJJt$81y zM4H&Z;AySclnc!eFLnaF)?|pdoj3xB)|+&BKk&C%5->2;?4C*KD2}$fg7G2`AP#1ofd%-fTa&o zJSqiKOq?is_awos8nH_<0LCRFZd}VFiBk2S7HP6-w;uG>rN% zLJByZSlskTzYD!Z#CgMpea6KBILi z@t%WB-G5sqDD?%(g!Z0?_B%$169S(|5^J|)mUg>xF%^S(SFUTX?2QbKo=Rz&IC(P5 zq#r89>f*o2dLXuNh|xuEqx`((tJYmR(;U+HMi@YL3XC`SKmJ0Ux2k6mp4PhUaF0m&>q-g1;49OROe@biLonSFE#lB4NTDBmz~L9;t-nbc zzt1R|Jx;K!#^ z-eCyAKdF`@bv#FJ>MCd5SyQ_TThS(TZbLHRWsFzn$GX(ba3DSYW>`tAT>)7GGYdBc zCdR<#FHZP8%su+tnA(M!&WHjf3k~DT!FCF|k5zPB{P9}--qoYoZ}`KPgU>fgXf@00 zS8T*Rls|}uef)9=Jto@)rSGqnT}~0a9USYn_YyodILCs&6`{!1ZuI z9eyH{xH6;jY$zxET8(G`9p1plzU6)xeIokHO|FWe(!hqfA$ChaJ#0DT$vCH zJnLdB`TYb?X^Y2>Q$>G}LYYp`#wOSPjigC)Rd{fq+_qF9XS%A1|C3wP1>UZh_fY5R z4{qGIJlvOzn@EmkFac-ZrA0;{$SNw~9fwYJj5czcgY!Lr0@BIfh#9T0qBvzTk&xc- z#`t~^jk{Hj>&jPObvw%WR@g#Q4dCCA&cN!iILRM9f#d9vFvrWvDQQj!sFy^8j3j+#d@kWW=>Ts=9-AkFLv~~?Ol+2gaA4!t%)QZ_)RhOG@QIg<|Koc**YJfk zzP4K!6nriG$9uBMFq>vV#062jRB|oYZrJ{F`L7XR86DLlu5GDOjuwF(XfkB!N3g+4L_I*zPmamR&irix6XIK1qeC5&zhk9cYCM^1sIMGHb%jex`Brj>4LcStSyw|R?J(C*bLF)>}l>J=5(~+yrCJ*QbnBewBupmo)*JZZ;T#h)r%XBFo;>t zmC1ZVS9-S%wD$u;-jK%A-Q?VA*<+^8flV?VB<^)(`dvOtH5ZKfZd&qO4?9h^NgO?_ z()hvGU%8BLMN#QdGI6*&bQ-(wZofmdO#giz zQ^aLsXuY^^slDZ1_w**+y0RsQ?T_M*nc1?0H#CzRd2MiT*bl=Zhpp^|lm}T|uN>W9 z@m)M;#X{NauMZzOOFDO2am|WvBm>I>odvh0joMgFCx&~Qt z<#EV3;IWCRX!B22R7V02+islVYV}?*31!C4_-9AF{`Va+%0eB)Sbq}p+$|fr-b{({*d~D-(7@#VuxP;j?T~ zF3m_E%J)lbzj)v;Pcom(QrQb_0kP2W2FbO@wY-?FrcP}Xbygc2HOkSxPaD3?l`P35 z&=~p*wL{|G2Ap39PJK%;^j}bOcLaswtDlA05u*eh?`%xMQy@#rctG42^n&^f^~0%W zzdhT8p584}c|mQC(m-kxCwlsQNSav(U?UTZ_HXXLxbp3r3B0K`ACQ!m@zYI1qLoF< zWg3kMYDi0dHpPbRU-U4l$7HHE29!5;X)*xQ`){?oYKklHB<{rUDG$D`fAu+)o;J{~ zN=WSL#Mf6CVQ=oQY*Fyj{oO3g$0v3qn!Pcqq^?9nwiC^f|QT&+g`w{sy+=O?oq$R=xbnD>_72 zk0}1tNET5D9#jNxT^lRH-N;4Jq1A(*)EfnUdvhKpqybbK$W==o1#%}F7vzzZh6_y8gNVkiNh|oX(ZzG^E4Mu#VwZKHpo6LI zD%!mL&A9Z(>rw?l5GT!Q{T_=i>&u7F!oTHuBC7p|zQ?SFTxDv~{i z@k!sQb&L@ayxG~%Zq2X0FTo6xxPR-w9_Spgcfnb^;3Q?BsZjWqz4m^Jp<*wn__qN@ zpVA&Z8Xzb!L4?sC`D}2|i=??&I3(R*vf&J&4d3f z5h<>UD$D-mTiWwFCPn>IAl|?Zj;0Rf6}nwf59@StssK-`_!^f;ta!uIo;hIDbK}pajpQa_PL@Ts5V0 zP)@E~3{-u9hk@aS_I8LMY<>Avfiz7)?|KM)`#5Vdre^FG#trBhMs>AFNr}C}vghTH zd)-}_$5Lz7PYeX zG_LO(8EQpvrEMBC)CH?GFD)%6p^u?CI=*hrQg-6b)eVJbGy!}!2b2eh<9(fM z%AEXxENtb_?qN7*F)zxG@%O_k7uDc zb>BI?G2V8F9Ll31iJc`~+R0RLk*XARhFM#NUdg1t%H6dP(Q=kmBNz14A78Z)o8C)} z;li9~wtSuVY=V=wDaE_(t`D8De5A0I_0hTfCBRS-;Oy$M_FXq`O|pwpNife2NsIcY zpxxhVa6#}j(77|anDuRUgJcMvTJNP0> zVXnL*oKx+AvhK31T=-%3$66ulEtz++Y}-3K1Ci`Y|%q55AsS9XYVRX=eOB_Zy;MAc&hG$#z|d^ zrsx<oO&j zl5*_3GJE)Ka0Hc1sL%LoYEQB;kxX7=tWIZhRF->Gd(P(WbQF}jW6miuB8Cq>K7`;H zP^NF(Ir<;Y5FcGfU$s0u9{|fd6>n;DQX-H3T4sH9G|!a`XQ@)vvnGS3ZO?^uEF}Tk zfDVf~B^~61eto%54aN9@8oxR1YzdIZXKC|Rj1g99E4+vByUp2}p_3&7UU#y@3RVY4 z(}t|rW};p-B1({7F|L?FggM%u0pSqOZmCV<%Hfno0|k!pEkT zs_rB?TD}w41S86&QO1D`%XiW__UEl|@9=M;B>DV7kOpp^fhiTw*n=ygv=>a6NYtwq z-)&di^ADmk5Jqao>U$J~o#Xfb;Awc8BkxW=da+-#!$e7B1dObT{P`IBI<$Pt)plUr z3zat*1!*sdjd=1i=NgqGj}Vxd!{HxQ!Yy7~$!MqdZern!-8 z+#m|c7pJV9Qt$tGcJFS^59zc$POgvFQQ505)7}^gq##TxRj=@rp{_N*2KWlKkN`aA z{*CSH+$aT3tu9avV1qQ?9@sd2PI==I$Nr;FtVW-W@e=LXaBvOPpU(c6C`6ve*tA^b zMW9tpAWCFayMeEEHnI04*^0vithciMc-d}2YYmWRx>`&< zKpI~wTZs>KWwS|6V}0zLN#HX}@4iQfrzpc`GXRMLc!vbu^mGh{)Wts~iOvO1A+1GL zgm%EfwskL1_Zd{yPBvfTC*KK#i}HT-s;NuzO&3t%i&p2^{f*+WLmDDIFY$ZqP{Kc9 zd4I1$7gG$%#`^^&nMDYDZ*ErfQ0sF!Br?7y7oqh-1>RQwChM~tScxR|Re%&2Z_37F z#qHeh+?9uYevNu!|IANNS)W_5C~ll__bN_c+`aQy5z~&4&P9>U9*{*3^9iRC?_H~d zSn02|NBeC{VI{r|V0hd2H(9z5P@o3X2SEs9h8z*>m7lOPOBNRYOhMB+`dqznE2&b! z8T^mm_}!A+$*VUWi7)R{HRxDg4-pp`Im3KHZucw%;3zSvz^Xf`#MADaZs5krJIO&V zcb0CtVqUlUCg;VySuA2YW3Hz^l09~R`j{|8)SDb`#0b^-?a{$c6g^M6NMi)0ht_{# zOwyvKnzgF%zE85lu4b$l^hndDxfg7g{?!{3gWRqwXp!WY{HI3gpPmQ$Sl`0fUB*h5 zEybORn-c&N0lN@1AJ@+W85lS?uO_x?2w&}KK38UhID~c? zrm2xtJu6KZRpj=#v+75DHP;t@9l!zJjm|z2MCW4HvwkK4r!#r$3^}8)ifw6}Wc^8q zEWD=~4{`E6VW<=lnPzU^GdFD3!}a_xg-zI*Hg0LCWUHCa9ZnX1-&puslVOXcB!@*( zjZAjW{9U-*4eCudY)Sj0TOHO)1{P>Onl3UkPO`^}Te*`k@sFUI#(sbsY5aSFttw1F zEVZSG7c`q{))kWtp0y;Ar@8SKpF=)4++!UI_xo843ym@oDh0^lU6cfQ4cQrg$7t6R z24@>WQ15T(MxW)3rKhSo0L>K6IHjjqk2>B~6k2ODfU+T7&&G8Lx6*N$i-cYL)#?~g z&U=Cg6=gqT_rL|R6A zf%wBVitqSlCH!~vGp;^2(fiVwreOG+QM(&6p~piH`HmPN#!vu0z4Lkq|C+*@EHO~Q z!`h>dPdZhcK~{@Nii1&?Pq}nbFF zK)NPr992e*e1JiuTl4sgev@?k5raHFJ9wY^Jzx>qX-+OJBx3cH0?3aiF=8BSf{(3S z7htyLEcgdlzv?(EVgDo17c<O&ZUshb1x2j1!+n&(A^!2$D znK=R8#NX_c6wM-~2)it<rLD`+IY@dhvl#?tYDtsYB#EK1D|KHZcxPJSMd(_*I^v-wUiPb{*m=Y zVi7|h`QXD+967{mFXV_M){`fRYF@tioyb%!gehiUsKZFxJvO4#6H_XY8r^F~W7TFg(Q|sEML56p4dS+?3nRm>ht$G^%33ddXi z@or)~^2V!`t7bjf!PN?Ia6ng}?%f>588OtmaY26&-YoZ`sEg9=fo5QQ-a*)4tqS7~ z(@rs@=t^^M=zbvWt+g`fhZ#mq4>sW6TI7j_F)^yYAY*!^#c*A(Hy2@M%eR#`39CU~ zLuIF4+aQW%PoL^Ukri;Fg%6XB{qDbjIFT)IVEbwqt-z<`nW?<3W~+)j{ZF7rif&pw z`$NDG`ugbl`H|3utiAK0tx2u%Jqb899!r_VME$S3WT|>{f9buB8OQ5pmGAGw;l{6U z{D0QSVe;L-*uMXcSNRH2i$Cb);dC&W(LF=ud&}xq&q{c7)$UT=BXBM>c>}_#5-zt~ zxq^MAO22Gh-KI1!Z7rEC_`Yv3C#zv|aaJ_x?mO>+>6YNlNwNGv89h(69ldpKbrg3o z95+2k&8MvG-g$MR)kXp5W`o2qa>x~f4I*qQFvi21^iX!I(6;F#0wv;mhJcVw@RY9O$^dCc7EdVsn^Xv;zXX9+yo+L&iJl7Fqh zH26}=5fQN+*u1Fs?BW!|Q@r`I$&Rm;C1dw7beUzd=vI$!p=t&9!)K3}*Gc8I*EX%X zW+65<*v){9B*xZ!gPhjq)1)14BSGf^N^JRHqNJ`((fY3C;GQDa?a)@;4?)QqrZ<|n z)^qL~Z~CVAb!{$6XFaA-J^R8sDoQu^Jzasx%N?Ad`K;~X?5z-MRo|sA_i|>fwA9S6 zzcLGl=8+COaK-W36yfAiqIQV12=5yJ3&PW3>d>`|AoYzqUQKVJ3gGHB$_$~sdO*ix8JgpiAdJ#d=!^xN9}p<^E2mA z`X$zd*j=;UWeZBMA><^-MV3hm&9u05UANqDPRNJXZ3z+19im0%F%hl8w*}sgiwg)8 zTMr0ex3K`*dTc<}8x{apzFdY=eUS%;$b;Sv9W8_DAE2ZBefi0#d``)iB7}FOrJV?C z$JWMr`UX0&#T)HWCB{=U>_QZtCHit1gi?dRG&s`)bfgaGMX5~YrTH}`P$d{|o^!KH zrPHT7%;OJU84lB*QWk+PBJO^dD1m(b_1D$w#a$tR=N0+mmo(T)zx$KO1zOgE!8H2{ zr&1pge99pH!9(|Q?lpbF+jqQ=*Yv$UR1|UJJe>W#m<>NGV+BDDvi?c0@3`RF%1T_a zXBpcAr)3=Hr}Oyhm>PJksid5v-!z48M%TE6eO1Uc650XpCj1-pMaXfGlK8(b@PO>j zd{Wj(4U}>j|1Q^7Jee^HT5j*@FXZ_4{I9Sh>WzQP(N}D@onT^y+gycHg0sN<;>oy6 zfjRX7&mC1kVOp|YXOu=;rlZqFJJ7^6oxs+3#2Cn0T!W77TTcH*9%c3#_5D)f1rlvf z<3cp-o?{ns__a6n(p|y~Nf4We1*^F!2=3Dy03?by^5#AiYvm^^=|!K^(M()<;-C6g zhHjZ5`?)t%`1y{U?u&hAM`dwv7-S70G3dHV>l2jBtNp@HgbX4E_iBY>tBKf2L~gQK z@M;Zq`kfe`8ga&2v*>lT(M1by|+F|ZMo)+oQ zghc46{}Pr$glYBF2vxcCV+yL4?}DXNBChw$EsQ!IeSJD@c!B8Z_3@-&vH3B#ze~)i zM0|)qO83H1=E935I^KEPDr#I7l_POA+>xcx@ObUo1>)sdiN+|z;o25w?OL&lr?V83 zcGMpw^kc2&dfDT;Zh>mGF38q?xLwRcwdVesQi3yGbIJ~;U$MuZewS}%^iP2%6r+Tl zOv=?Q>)wzZm3?yfPOr~Jv8T51-?lHrWnFyP*WPi={P)MZ3oXQ`Z^W8w>?Z{rFWTNR zFQ-%ld>j8g1;cqS>Vo>9l{8&3_pF+zaft&bn4J5$_VnB+y9X_NIYtuxPSNGf)OVA1 z4y3!0OI#>Vuc2iy0fJlP?6R<)5i!xj<24PlxwE|9w&n zipv2zw%X23Brv(|;x+wbn^-JwEM9YN-n)m_z#0XxCyRrt^d$xo4~(X-86grVYRiAV zaK<689F2m{Uf-?}O2e`sG+*>TS^Y?&CpTc*BDd#mt83l45`Cg`qFFd$DU<>=1Z|as z@bG;)k5$EsX?e_t-!V)@URnCOdY_tkaZ_Tk>J9LCjbHr!iTc;MtR=D(?;nR=TxEal zZ8dK4k^eq=`B2(7e#hZP&Q=tT<&99xVKbrfw+m&K>Ql?WN7xSy4dIYCaz8mvmVq;O z;Gm9LQWTaSA!u?e=UGE+AYB@+Vy_nTcJK;286bTQPAM>IiVXtum9Ko2c>HH6;zk!z zJ}0gsbEmm}08$PKr`wlV*LpbqAWYK=?bGZ+(=6o^_H&u~J{F9h>W@xYU%7Q^U9eVI zHb+;oRvI_lc2=L>$^3Bz2Pt)*%5xccAC`7PNw|$p!}7wXbW>K7IrUy!p@#GBeo*st1C@PP$Rl~PAM}jxS`3Rg_NDJY0&s^i#ont)?BA8!3XYK5s5%t z0)po&qWOv;pIzrQ35ukgV>s_rWWZ%Xsi(=U$hM^RvEUmW$R;H617jOLyf3jv#~NgP zZW{0>ZePTEA6-vSHhL84Mg4Os=kzF+MEey-+PcraoBbB89YoMk>l;6%Jn0G5ks+N< zBNCi1uTz(!;RlLBQm5HV0(iWA1VIiUX-% zDRjY{!aoiw#LkS(jiq$gbWD(x7o17UPntQ?ku2D%bWa4bk$F94>+n;j2X z#VjC-I3r_gf(Bx_Qo}ug~4QC9jA?}MCbWJM=G z?p1D=98%g;agdNy+X&cDie%SKH8#02Ly7b7dU+-_foae8H-W*TpTjzLJO*@_WOi&t zc9u|1TwKqe3(Ga9wATFMlFw9@-T!#sB7df$obWZv(8~rU-43Xln`SvZ_mSVZQRqQp`nu}<;9QICJ`tvds` z>r2d-0#zhRVRrG4cx;<4J?E$Mifgh?G)fvETqQ&j+*|97T$*Wn{E}^WjWUP)Mg`Yb z-iHXvcPTjVPJqJ60$ZB5k8_Y-sF{-omJe*_Fq0UTLJqcXZ@5tP3!(^7D}MY%7mon= z6o8spxS_*wijpx6m2GPGyoYGwk37NoJJ-i)GJeQpMSLjPlSUT7KG+ET!g+Q$EU zC+CFO)<$l2M$GH|;wdTJLoy`|6Ayk^4J=1?tQ}3g)zWRU2r=00k-}-jY)a|onki)W znkv3pkz?t*j-m!zYPPM22AGX*hgYH6`q5SD`y1OrSixa0duZ9F=OvZ;NZSCo_Wkx} z9IJ8KRiZ6I`l`7J9+v|P-36D$3tsipk~~Xmr2)Jaz4mxn?DD{?&7>xVi5UTZT~z6% zvsF>AzK-di?|*U@J2&uGJ~u;%64U?b`)h(G96>ZU!M2UPATtR5N>ljOJwE*P2V!KS zgokNlu-D7&&Vl%^0zx#qR4P!1bA9?AO3Z37yZ8P=rx8y&M{Ed0@I!uHBcPO0Xkh8K z2cL)b&x>nLPfyV8p8H3ZxYrFO@A#Q^co>$SB<#bRN_V?5<6iaaVx$Vw;))=j5h0A$ zENU6foEqP~*w^{J&UZO+`s#0}Nw2XHj#}&Em;%O_8@>hjdtI_9R3Mn+hSW=(TI?($ z=Wsd3Q+&Ke`=+R7ub+zpOdn2e6#77?0+T~{^enN14 z?EL@;oPAbl`Abo0DM%(&94Z0g2c=L^29vr#EgeX8A@dE1*V7P2+PPW3piL)Sq<5%=j88Lq0_a3neAi_m_9!lYOf~LVr5X%C3oeX za-i3opmJ-BeeZdbXdStj0LRf#Vm&W>BKB81 zF~;Yk+q}iGFRQR4Wu%GT>&fJY5wj+B(@w6`L8IWi9}!<3(gn5~@a?e;pZ)mBM_SDez0u|K%a^7x)5f54 zj~$t<%f0iRtMoPf4|wmqo|hl~6$wM4zV+AT#fa*!r(A>t(ZY=boAjMvp?zJ{%$U%r z#UgZ>R?GCbzwj&ZVmWK|LvNO-V)4M#jO3}5JGg7D;x)Rz-*2!j3R5g6nzI7%qJPvi zqW{vcS7Kn%O`C6-rEd*gkQf>hI-}g-DoZ|u(9Uexz8rC33^Y~8wh|RRbvr0w=1%6Z z6ImOJEcIN3Z)l-tT>sGZL&wx#+~uP z=6PuEVwxWAR?Thny`}##B*=kozyhPq%md3W3*u1dACFy#I6w2gPkr*{NxS&%ztNMN zTgU2sgwP3hr*nQZy|)o>F+1^&7o&-C)Uf==0Xmzu=-kA8q5*(HB8l&fEN_Cpgwvp+ zU4=wWV?<^CMWg{-G)X$W*IsH%P_BX3;bniE~5N?up)j)i$#@T z(|~&6FB1`YYM_irPW<9ERs-oD??BkC8ah{#K+p#tyI1*8R2VqVl+vUKqd4u}C zU`|RSSAK^0DQ@7MUTpt{20qi`Et>wvJf7K)d0ltb(YhdcgV1ZO`|dVhm|E5tgoGH2 znrW!eG&htQ&iyTo(%h`A!57|>yJAjr3Y~be(<6u(gIbwTFO1#2(c^!7;ft_Q@JoN$ zGpMacISWSCGiH4QNf=Yw}du z@9+07`&~T(%2nxNX|d|_5fM~&3U(o~lT6`!S!P4IVo_zEs^xhzpF}L=CBW{9qx7Vv z9_UVWCiq+Aabx+-BqLtzALp8N2L3{DqFNY_-zA66{MquNmAo;djOtjIyLMiIA031_mf#?S&sN{QK!pc&hk)eAcUBzvv+UXHUKiY zuu4-F_v(8JnplwbwQwqQC7$z}hqXv3KZ+rtVg4P{zFgqm>9<|oy!Y0^GzmVDDEDRP zZzLfBw|Lq0a%6OxY zNlepOt-i!dLjVuh)SxOf@StH&-Dhbnw=0BEoqtAf4Ljzw!Y&umUaCvec`{-JP2vd4PYYnrmKe49vY`}72)kb|^3qZr4)Tr)rhFuNK7)^X?J9PI( z1U6l+?6a_xOIcBdm+muH0bj*+-d%x@S|vKJ!&?h2D`&ER>;qNZVU|EOYd;^X zsiy6SkVV)cF)ecA0?pv;lqJGEfK!avm2iT;f(#{Ix?j4I*z@V)@P&Yj$SVTf%Vl>d z?T51FU3e=iq@;NVUvEEQOfdWz?A%aceMm1<`dI`)pu3!6`!2Y2_qydIDszy@13GT$Y-N^0jL&d z4kGmfNCp_Of7i>tiGOdpcY5wFaAV-VZkXjQP$5J)`oV^V61Ncl{UQ-Jc&92oi91su z$A}G3i~sW`_{(Hqxpk^AkywJIG*Sxy7U^F{fJ-!I7-rJEgsYO zJq%#i2dJ0-*9qVe&Yv{|$pPb6nE$Nd|A<)pKphK!N9u6FOx|om$^ZJ+WF*kw`#4aa zu!yX6U61-c+y7jt0mc9D?rnaCW#(|;Yv26;ZQ4ZvQ{Yhm+zl9=|0kW}pYnAurQM|! zN-!%*0?^?9zRuYF27m_!3suNB$-@hX?(6?02zcsS?HVL1!K|R?zgq57#sk!Z1@*eHD!(Xd-?w*9O(M*{&~W{fbr!-o&TC~Yhjee4|V{ZgOK5; zT=Gg9srUX-x&OXQvVsfMyc?Cvy)fN5{f6cSs4G-V3=W?yXX2riW(Asj*cSl6;lD>q ziR=VFlo-kw#f#e5b-5_7D__^;Z+2f-2Rya5PRSxHtq_lRe*^nz)|(Od6olo5dYrA_ zzo9WQKZKS+m!SNhC8rq5LFi$@(=MnSsJ}+o9CT(NEDFr?v=FTdK&BPNpRnz#e7VQ? z?tl6f@aqZeZ7mp2jR(=_i(AFu2zmfrYH#fXED%xiO6YDP(8joQUqJdhm%w*utXpt% zs5bqvl16;?B185fD<#wmX3-Q#tI~IoyL9RX`4>(9Luu^Ixl*HiC5&X#agIJ_PViA_ zFmqkEf*Q= z4cWzdJL~j|WUh-*QRwrfA)?xZ4^D$!{F~G3O_acT1C>69u z5S?|5wGb@Cm|#}0S)0AippBF!iUP+H%PH$RH@SqMiXB$)SKhb&Gda7j)fM)Scw1*$ z=2JfmIl26o_pUgR{VaR%UEyeQ^^3a~=UCf9fEs6lSc$B-IM+g09ful0a_SPPo4l7j ztG{`8vqfT9vzB-HH>(SLc=O$TWJh3y;5lio?8Mjuj4xhz&Dl;~&iBk+GDU}k>JdZt zYM!+463T!R)??5R*EVIug%SpjzbTO)q_SIWc!q65)V zTKq{ca0Hn6Pmjy|8&|UD^sUd4dL)O>m8cDHbav#a|q>e>^>yq~Szoj@=nP1Y$_&yG7 zoXhdN1d$K>*-`_(<&r0ScIpyv_-MUgnBLwXW9PdjEb^R?N|E{$e-MGfM^TC8dH$Cr zTyS4i<${ZQcf`q^ie}Psd>9snb8KDPySeEk>-Bzn50_;u?4q4)h*~izI#_17Ykb@r zXNa~(_eXENSZ)%`K^t*oK9%{hZ|NsAX#7SO%?OrF0?6eeR|9B@de?ZX0+O>o_iY5W zz-}~T$E~?t(oFjhJPny5jG2}LaHFz<;g~6yjgN(6o1h#)E0q0!NU48Kqkau%Zp`@J1$ZMO;K`Uj?Yl-Z4R`R5S4gSL-ujHIw2mO zY@(jENO-uP(92;>U?gAt;wSYL#{!}%UpIl6{B-{Dig38t>?G}%o|t@lEc|ncAiA|r zL7R!_K2)%`c2yY(Hh_%OoK^pF_bwM}?_Z}K$dyJ!`|(=`j=k} zgy3y0KPjuOGXTDy9P(XJhbZ#Ja&%EYS0|nqfDSkPgo2zK-dTnF2+0{poVQ^2COjaB z5I&Rd7SX#MUzyHfK)(Y}N8GEY?nfAEoXmz>H4JYe(RQx8zMxunx2{-)J#&V&1YnN! z28F1eQBwvPhg*F#1O#*9ez#pCb&pEL$)T>VsI)%&+Q*+wG(!2J9{CB)no!?Q?J{lU zdYj9a9s4W)4gC+3fBc9D+=6`%oF*{$8pmY#`)>qnMg9NA0@PDuOrdinpLpf-^s*Or z`P;F%pNWHaM;;B(Kw8@AQy8cy@B_c|cnt^bkwh24Wo4~Ut~S9B`xE63?FsI1i@YEl zwZt6*33$x|Jc~@M#cFc~9PS_v-Bat5nmgaNpu#h-VYz<4MS;VvhRZGL7c+bepzu(= z3kXr}XV4T7zo7)Y=_4agj;>nDI40sI+b5e|kOScFD$4hP4pZkQ0YR)IUepUZgw77% zT6Q)`Xupbp9T+Mw_a4uT4fkU{q9U5N0zs#u>+yQoeTS%V#;P{=*R|V#jlJ+ehXBFD zPkk;^UG9J8qRPU}*f*zQ6`p?FlgcWYphEU4f1dx0iOZ@Qmfxx)pUnmEg+_D}3HFS^ zJXBu4Ils404bXw4ZdH2EZ9$hbJK}+Eu=p|g(Iey1!!x<-g!at>;4c}rAgb>s(?dQh z`pIGM`w7)Tjv$u6#t%*bFownXJeR5*T|yxI`c-8YFyC@?+n;~)&N&gb?;|2@DZJ2F z{#M$OKUr{tw|t>~Q@9)(sk%;0sk6vwCe#yn0Rm38hx!<5t*ya!aQ4i}Uy6&RZ%&xh z?GDSM_DYV9+g?>KpYM7OJU92Y1~kcC$Jt=S$(DLJIm=Tf9g&~Wd3f{B)lvE%r4dwG zbF3xK3gpOcb@@+TW)Ru@kg5u|^aFGjCZ>RcEuDI`?b(3g7iKk3rcie5Z=xL`_8)&| z&F`lZ$aH+qPxfW%y=EgJ=3{xl_mui%>n_t5y5WSvgz-;o=XPFit4sZv%80oJ;DRsj7HMCkUDa}immyuFKvHx2T+yjJCb<2ZJu9p zEmm*qK}_oxywqFb;(k#c9@Bhc$`@Ek+Z>F9maBaV3t;E9rp|kp>~NCD4S3WJ159uN z^w|K(`@1cWumJ$0()ZX||84BFC%Oux^knNrIDj=>&4QfT$9$8`5T8bXoRvs|Fy?e1 z9+-e%H@rSJW)#%USc6E48u4ZZ2#FV%90BJZLa0KCiY_wTp;simmX5?!raFEmAe?2U zNX*au;cRB8`m#|bq3KyAa2(zy+_|-3^96vYU!^UvBGC;%VQ&gNeml|ZJne8RQsNk{ z!3GS~7ns@)N#p=6Tk`p+wI)p$CANgd&fdbaEjh?)7pa1NjAH ztx789Yn|?kK(SMv<*sTYr6X0m-S19dpH(zQoJ)SBf$unby#T;W`UU3l+IK{smydC` zI|>Dz-@ho?<%dOMXXEaK#h33u@qux38~ex1d23<}N@xo1_DqnARDJPqNM-+cO3EAm zuPzM87t`-fNCHxSVXZMF@^$YE-ag}~&ab+(lv{xePMwsY=&k(=>)u|mnMUUKASsXr zeF-?btA?Kn-m`ql(@2sPmfw4YtcYR=z@3L%lszQ0lVX>D*r}*2X%w3(`VOK#8adE-v4*J! zRm~l0Ne+e5!1CX#H=*snYXhh3{w;85x+$g|!vK)7MLJh?=lVX?8J)^tQ8bhdc{#rV z0Q}*+7xV}2i!36;>3^Ui@)NDmQ*3tL*@j^d!<#)avqCGXQ`8P-!KW+Vg8IpKaJM6( zz&eb5ZuIN@yDqoNiGM{pszR6hSjnL%j&f6D-lin;sO?;7Dn?or!r`*-(CPH1vpN~| z>!nzX2uV6&Cwi-nQ;iXp_g4M; z1xAOQPKg-qz+QH497KF|DT{f9>|CqSa1)V#|9w5mv230sKUtgm%cUsFD>6_1Obk2B zF`#=^ne2{lc^$# zNE6&mo<@x?jOEuvjd(F+I~z}=i2)22Z>_L%zDqn(3|THKkj?Tq>IFe8+~>L__8O9Y zTC6#EaDb=$#U~LzjsrQZAb^P7A?36m(9i7IL66DXRFsO5Y4mMu4$AFf@dQmd_?VqPHO7X6bS%XnJqx-JoN_h z0gr&KHoD^S7PiSUyPVVQ@kZPNmB;63y(CVlBRjM%6v~m^fE0!_fE=MZOnqXW9GEp1 zZ`)j!zW2^1Ua#vrLIn9Of-SEscVdTCEb_oW#okGvzsi2s{Fp6-QX3QzpH%q9y+8A= zy8>E53Fxh_IjRL+b|mZsAC;>0ENQA$Unb&9@?# zMg?qOAn}s4McNAq34pKkUDI4CFr{ujHvwbS44wD6wL@D8-aXehCr}>o(g?Zv$;7aT z?I(KLIj_I3{hmTx55QRF+Mkma_@}=y!T6br-Io@c3OPQsB1xzgIDkGj=+_OpD0c<+ zA9PX5iTb3!uYw9W9x76+QK>Nl+U|Qeq3Vf0>k&2_blTKb`zZMijSKsPm32ZS>{2ZTL|H>rfE z7dTf``&#K=+`8u+VZuVB_v`&rYrT|?YYhpf%c7CP8GvauiB=5BV^+3Y5$b9w)wpyW zxQ(5>1~^q?GJa)YT-eiBZ14gsKP*VT^FUq}&c32Y<}I-vPdnSgiG4FHi?QW=#}c56 z;*V9c;m#is!N(0IR6tJkP|P9g=L4ahR(?V)kV7iU`Q7lk0fi~&mDue{7W{>sveuC7 zfBD&OO(+p&m1OJQHP@5VINlGtU7ciz^Ut-+o_`!!Ijx=K)QZEKIn^o<4oU1 z!Uq-=&eL%~Woxz6;1Y(~;Mk`GhusPR$(T1j5 z>7C>n1vp^aR@3SF^I`-AoG0}0GM56yR}aSM!? zym;Du9~R(n+^#UZnT$(VkS2d13q)Siq3OC_@67=iEG(B|C^G1*TQfF%RvnPb1m*o* z4`^7qEcKtjWGT4CWhKmbjpsx2pLVR<&T#W#2OlUs}A42Gp{v+eyciz>!SW07rJs?K49 zwbH>{!Mg2RIj?5wJs$G=A#N@g)Th@~>;mzzvtq+r$+*jQ#h$v)0AXVKM>nOzq%clg;Y_BV&&Xc|R&=yf-)HvqnWpJv~Q(~)q+;4W4`-zE2L$ILhcT902(8s$Z(d2$z zxXZw%gj&53el(i?&7JTL=rbeG=ims?2>pUhG8Bm4Zeim0t8zG)ec*hVO-1mlsme$4<4xv| zf$pjcJ{j|kOQ9n;;)c;uP$7(DStj(QSdw z#n^WQN0GSkXAIPTd@O!QNUd2Uq-4A7icHGVFODXV z%otdN))GTL3$Lrr+VbF?J1M%Uuyk+$7J|%sGkMm(r^%^^bPO((EY-?RQI;4L@bYucCsCq z0qDC&IJzP@b=f}lEITzKdoJE)Tw(Z3z-7L3itxIlDetqh_9mb+9I41Rz0zM9$#j86 zoL3ULIxFM*X{X|ETrWbpNnL?zp}m(jcTGOKSs;p}9K6*w<>Tasx;v-Fyo$X3>7@s4 zTX$S8Vx4Argq>F!KfU;CfC%ac4QgvkZTo*{g2~Jl$FS($;r-83Sg`wEWBL-jrE$8(`_x=@+P-8n|XgI(c z_rc+c4dd%?zBKZwEUI@Gko?9tL-{WUOFW3R)vui)C2yDg@eykfw91v?6nAP$5X&jU zmA34yzrhaxaoo)RK@cAH7UHudezkp{Y$&xbT9JKbXVo$SSM&fP#66x zUzO}PzguIi6ASdP!+h;n%#%Hc((EAnH!~ZCA9Lsnkl9t5aOLxraYnfj5UoYl5*BQQ zUiRv;>D#>_ZcJ_Hfo2-|h`pmS&WG-1(P@7fDjL;zOvkgrQ^jivmVKDadtf`9G~7n7 z>EQ4%3HHFGd|&I%&0{3_O8JKi>?;d2&4JQbP2^J=JGux7Wd2<`l0XBmb}@!$){}Af zFqjeW;!&+DlOO%E)w}+&90;CU`8q<|q0M4P4X~)y!8J2+ca<|ehCF@C@}w;AGuH8! z+hJv|znpDdCa)&-&5zRG6F*%Mc{?P$ZjUK~{b)`ONKzI$d$-7@LiAZ?qEFBj+=mYb z*@}+YURIGl3MS9{oI3KapYu~gc1#F2h-Xh-(fT%JNO(@z!{Hw>K#!cge|4c*sT zy(LBSrxRINw4bX|sTeDUed79&xO-HiE&9d=_e@|(`;)+{kQ}|{*K;$BTj^7inCX7} z^DewcG}D*OuUju@wvto?q<#gT+!emDuKjyu?4Bjrza;yMM)B-)qGOQ+x8>Q^fsj{* zV+9vD8Jg>T9-n-eEJ-LN_@d?J8kEdLYr7jEe1MwSgUm+8@2_+N*3qW4PSZgK80i=H zb9A+6)cLN-(MuL|3H$GxY(umfJAd83R-Vw1m_p=K)wGY6=-qTI)eDJ9eUMlP3PS|p<0Rpkyrv4r7Xb#6$mUy+?`VJz# z@>Wy1P~2&idZ=3kl70moHf#ePC{ z&#@BlHFuindMqg;4v@z5tIYji3Fi^XB}fT4#U*0!eP|)(2r~_ibyiKFwA0WC0S8DV z<9Lh+oX-1sf8aA)TR$sl+8ch|RdWw*1nZ0Qxdf`2YsfDey)C`P!)CTfVS8cA=3fHM z!-g}zYs7DU5OAzg$TO&U(fk^j{&TM7mD)%$v6}af%us`1OWwo6h;P9Ez``QX)$2z# z$mIB{}N22f4WnRuwgfwfk4(&D4iluJ~R5HvE_%YlhRx zGw*ekCBw=2Xwsh3TC`g=J^oWzYyQ3^M()MRQoRw^ZtL#7pRWD+8f^H3J1=_G8{NPkg(>8#P`8Rf5O!qt0&4M&15 zOr1L0Q42iP%fpaL&@gkt*+_n!BvU*6Qt2I!KzH_Cor#5Ha4KhV+18RcYKOts5^Xq^ zlKjlMb72VETG5WhdnLNG9QkYhA!TilCf=lA_B4(UjANssG7c+o`bFSgAXc~YP)<9> zJ7f2g^t#gDs4c*+nPz9~h^ASBuG~Ve>aBPye!T6gOUW|6gBE(Kjjt8gh;lDI(lzh$ zNpO9itKzhiX?#HYW`2m@`YOp&nRMv`C@01Zorm&%TA*{5R2&|?fox0Yzg2cY!fCB_ zkE)Y<@!KIG{c+@qKH&qS|ASxFalgDQv7%`mw`PXgRFLuv#SeG!`s`xl3)?v7Z ze07VEIpLXX;AOh;8F}b_qjm~z~CHIUmnxaKI)wIJ}_UTyrY?fZxoY5pg2}oOb zF%~=d9QLkSp2r$q=)d#FZ5f);5zI5by5^+TTvqwLh`J$uN7+k$5={!&*PQog!7<`3 z-M{7Ur(yRB!=h&sw~ME!?_0*(oJ_>g5z1C#8A5%Jk&@tTYT3?bdaoDT zs^ii-is@7vptr!b&W$m!ex!+jJHJ=03FS4Xjmc2toblhaBUM2u`f!DYfP2*vepHKL zUcTmsf+jZjX`bYYxV;~f^r9OxKq=cloF&rfv?@dzgB2=i&aA#ORp=7MmdnIRRWpMj zL6=QRXPTSSu}C%1DLQ^>LxLQ=Y{AC%E%u)D74uHphgwfz&zj1gc%Nu~Qz(uz5}Lez z2-!w?EzP;a&n@RHH16Q17xs5b>3<};NTuZ&1)L`QT2Tf0^snEQx))qI_2I;z+)&U( zW`##8<&s@(E8d@_E6~vD^e#kdkfl8oOLuBx+6o$9wGPTl9>QMJQL(b+(@C}K31tBIm2g9M^4Xs*HYp+U}$CJm(xrh`6 zvl>M_Bfg8`{^Ic2bON2fV|D8U7h%fZVt(%@%~o0065W>Xg(IPvX_IVUqDw=Pf5wXGD&e_3!Z?ivS71>J8{(|FrOf2YYp4`^{i(>OY+V@dCZQjsawWVO65fl@E6#Ao~88S zRF>I8E@D+}o$J~K@+%-cEZ-vBY!YcA%0L`Gcq|9rbA1Dt`MH<ecdbT8CWF+q670D}JlOrVTC+moR)EC(rG;N7c|>rEYj|I$qK;$Gl*Eaj>0-_u z@E2YADog2aQx6HG1yOn*X_cE6?RVav!?$u}56uNx^E0!hT6pXN2KX&PNuL6(bA_61 z_8c%=o!+GDeZMSfW_Yf?9a(=!h~~{%Vj}DE=9EcYV9%+gCaDb6OBuY3U<p}#bCD#3NWPNT&KqEc>!DEe%Le%2rNARecHJmzA1 zU{JVW)B8E1V5``p<)Nm$O#9X(U|nmB(`V|ohNxZ-m`eWS(*jn>?2ATP{%KbuT9-x% zi-uK!WA!C&L8ifg+v{4<_3svEy$4d54cHR=~v`_K2Q=+N%!cNR%! zL(BO9lJa$O@#*BljQ`At5Ku?yyVHJTCP>Qp?vrQVOe}=i;J@E*O{kPC^2kU9EOXXR_D;>(i;4C6 zK+s`elGxVb30^zVZQY ze?SglS_z5P^H60Q=PiU=*m^=$LH>YmWbhj^;Vm#eL#{1HrU=mtK+MlX!YKIR9;lfU zF!DtQwTspsjMI|D3;o}Fv}~}iPa`O~=O!{1CQvEHNd19d&~v`+J}T}Nj!*mpCG^i8 zmgZ3U-wi3G|N5hhVi;spd9CgZy&JveS!v`8{MyA+Is7K#?KAox_L|X|Ga?jdA{AiP z#w))0+&Pd?KIWJ7bwMj<^3_5kOmf^$8mQCR8j{|Qw<&p50H9Bhv7GHm28)B9L~E3L zt`$X&a7geUh*#5Kw2MDRqL?qBO$K~ezs1g@C;uLgoqliHk^jCc)c5(D1#P@P8ZF&wWl?g6CTN{G#HAG;Yi^xLhmBRJeAl|?*cKcVJRFx zGY+JR#%E>VFAZy7oI=8>`Q(I3LcLF^!upLDq2xFJM1gVu>!h8qfS~M3vwj5Su=s0s zZ8o-wOlDDK(*Qq263#aHJc8TBl-k|j`F)>UMA(OKL8d=!d6qaeTtxh55VeoM3U)oR zS1fb&i$acB_G|UB4enfB%Q5Ei{}OXKB3{#^zB@DmRZ7#`44f^6cfX_o|Bc<}I!Ad7 ztUCT>9?wP>={G;sylPhD$yVi$UlBM6;Evskx=^5IK=b9==+ZN#Bf)=I1kjAY;tx|{ zQ@eD2mA!-U8Yf%)qeQ(VgTpIwi91aOJnGNG&KnBhh3TQO^ek06ldfRyp5U4J&fgANpph>-Vn2QobAi)49zGW z{EF_%QT$%uDO9#h6xWIBRJ^~B6q2U!cqX}&-&0}Q=U%^1?){U?I%POM)fh&I>FC2Q zGTgm-5=}<@#VN@*_Eia{^tUnm&pc`nowYTzcfhg+cHa6$>RL46>1thYg2&@X?~kYb zK{J{im)zb(h5hw40krN?MbVnl>TS_%eru9sdVsHbiJHNZpGRA7?aL1GykL z`|Ia3m#bc4cj+BgRJCTAikh!&qEK^5{)^IXN6+c|Rp`E}b-h&IAy&bxag@Nl7gU{et&>qbndDUH^S|1eipa2pGi5fClQAHF=nR|fjK`T=C0<%0I!$O)hN7fz6++o|AV)09&2gEjTI z9-4V|Y_HfG+jdJviXJ$xgXx3i2I?D>V0MdpF$hW(=aT!cb>-4=--#6SERO!X`MMnJ)TBTqFo9X^fa&R{8PS7K+(2 zK0ExGG}Z1ip>t(IItzx@#8hl}r-;r6os)NjI#%uS!v{bAf_@3)21>$qoQV8=AP%3; zcx2?DFB6d;1fdb@o-0OBR(dOn>?15q%09YboJ!drP;R4l@0Naq1W5No0;+3z4s{~S zpg|qB21dKNt+w0-mD_irVJ9P3g1%iBE{`y8axh$}Mij-Dg6tdK5Ng+bV+UC5>|cH| z2SD%T6v%d<^6{kAf4s>?0jpCQjx0#5p< zav9>nG_W3kVrG-8mC!l6OAx<(+|-MS;h1L~I;)?3)rBbeo)jB zGI*=oVR5j@TfpUc36FMu^{$9trAy=50O_N_;%Mo9rN>&gj&;9pYA90)F6)nZ!MhUp z;BPB*YeJFJ|Ac zsC{VlpUBkzC#)C}FlW{l^xeC2lt2%DsE@G7qMt}p>v~3_O;|0DhwWR$`Lx_hRZM9n zVRLhfR?n7!8jiEZl;ENA%NfhdF}}j15&#=JpihVroO^KI=|4$tL4Z$}dc7C$cUm(S z_$Nb5W+Y@?0Ylw$-#?y0OY+0ar zVp>t=QeSB;?S)qj5MFzc?9)r&nwj<2e;>coOwlBM&jRRZfq&vc0betu5bD1WJvp&| z4aICB9jgr3aTFgw&j8tQqjlVM@bjy?mzxER5TYqQVi6)$B|1M!4|R2^lCB4=+Sa@b ze7)XB&;rPBZ_Fv3i!MuJ*81@zFGJ%jkFO<49mrPqQuydOnf7|*ejoU0jye*M&j-V#x#c zKs7{E6GCBYFtQNtrnB`UB31YC5mU<5^hx7Mo#H|+vJoI|Ig5nVkPFi6>TclJ7`MsC zJk>dksZq}4&1?rP(-3I@57w#R5P76UFMQ1(P;58B8k&WeImj$CSeEgh^)n_D-`^r` z&(b{}8?f?x6Nzq){#^FsiznNyuG4;4F5rCeeb-!5y3;A-Za&pts1?Rp_e~*}hW)UP zX1|3jK;z<#Lo)U0Z9bsm^L_jndRn2Ok9cHEz2IeU+VgarjS%^tRdT&4p@s!(%6+DN z`{?-{toKfcA~^W`%)y>>pXYNo%#lR^EU$H{>P2I9)G4kbxFwg>c(so z1l;+eHj9MUQwn{vZPDG=O&9%re9b2Px0u5jZs$C2$7h6T&@hRaEuNzU@3H&Kym7Ar zGT^%G_@=>ey~|W&;Z}8N>IwakhJqqMzuL+v&nkt7krLy61F2J^N9attF+i!GLm;}H z#gR8|l==%VXd8`ClzmrJFZqbB?EcW!JPCUPM2QAh1=}*LJiYD~fl{Spj%X6464Edm z$s1+}D4li>6*H9(bDs;3JbMG1%qQXDCxvXP>|uI$5u!k%o!K1R8E+=kkuAKfk4TUP z);{b_glELZUH(Eekv*s3)>>L1*@d|GzL|ihbNN3lU5P)`-S?j{%n*jDEM5CgmAr7_w&lUC;OT`2+6j-19m2 z+_SvT`+jb(H8qHx{p$}O=vQ&LpbVpp&Q$!w*J0jXC*D)*9M;y=N~v0>S%fWPMFMxS zPb>39r*#nT)zJ7+zqC1KFHVr>U#7t9?h$!U97f56C1JHjp2>0yk|=%Ac3+}bV;q1f1 zV;<e_MGkjTr|S}Fc;GopVY)}zrfK}hvy%KAmT zT}TQYqwr?}SiWz_^V$CqVmCFU(G`o1G5QYsJKYek;lXwO?f@ zo65@9Jgrnx;1tB@wV^!Yc?4>9yv?P>tK9S##QMk-_o&pUsJan1Wj~6hW1(}ag20wU z^22^ynV;E`taeGrn~`bbZM-gc(}SwwU>VFsolyDr)Su95Sw1;9Xj_z!!dtRCL-z6m z86mvN^2&%MCE*QcIKxPz4TCB41i#A#UMuGJ&iiW%rHJyiZ{Ix{+T5}}Jn6229{kIC zkM}g!m>^mm%nm(`lfk8gD`>K1U-RF+6DQ@6{xzDS5B2K3Z)+E@w|k_~_P9m{X)jKY zS9--)WUTMp#Nh0|dewhb&)tJF8q~tP0fIN?z`!pJgHPXJvTm`v?l@~5i<&TV3A8TlTcxzcFI`L= zpcPU2^HV5~l;u>pyu2OZj19gq08YfH-hfVYf!7GLeP-BkZTr8;@`_FSwbeUc^u20F zO750sZSI`9zLRrW`~zGnKqMZl?jlc*g?f2Frg);P?$bKfi&KEV*y2*ySy9_kT%4Je zmTs6QDxO#F|H#t^Ai}D$+FR*7K_Yw4EzeROP+#)`07$Qy2klPbLPcf~dgb*(pUx1WUe_Uh$QYYjy=-ah9hw01nWTaIIxA`h;|i{^&B7K9^j`3@M$>1b{Q{SJ>!u~k8IQzmxVPmc+_^mkep^{p z=uotlJer}kTl6Gi{ewkV(8CHxiM-Zdptw*Qk+iuVy0bGRozA>3Cw0fA$JkVZH1j0b zgDT2jOTt~Sxg3K*Un@v2^Y(}_$<`fL=kj$yIX2%-q0uv5R{J!-n zXj1Oro^tisbB@{B&d}AR)pkf84nA`Kc>s`=@9vVWEeN8LCcd(LdTNpn-)nDO4;73V zi}-Pt{(M$CKtW)GfCHGF);zS2MLfcUE9P57O=4na&|}`{<+oDg9Pg{7G=Sq9A2E!D z)WKpA49PE4A?*1A@BOK)Azp!8d(q3=-vUud`G48{RaK*p9AY&HoweXeyC=PxI!k*8)Q=o)bnSy``ot6Djv5MohthG=9)^`fUIS|hWj-fs z^ELzxakppI8jUV0VDovJKxFOnOPOjDJZf$zKt(jIFqQmPrz&pWJTNd!ItFKukICv< zZ1ZnCz-@O^j7Uqp4rtGp-dB00pL#3)-Po~&1@gUA0$e|n&XiP8qBDtUSiXj72YINM z9Bd(ee|`cNV@hm+r60_~z}%xva*b1D%=7jF=y`O}wGMNJ(j4wX2&IyEA#7P0wsFrB z`M_IdEdTv*G{uvVZr6uC)?BXycZa|Mc->d!CDZTyY;|*#A>S=x{4iWJrub(})X21x zEC0KkOxB2PaGCRe@ zb7M=`rS-iUJWIzs%~!OGmJy-E?|LQ-cgC!wlc&n`U3!|1h+sIc2 zHH=bCVo2Vx@=+skn+TimwJ~lN?(-4TNVCa3*HB*MRo_7X6LlzR6N`Mn|h9Ehn z){t!0Jee#pUDb>H8_p4Pr8^N(h6r$dv~g3lMFGBq8d(@T^wX9FIr74A;>oUhEzNO` zRyY__@VlBC{l|(V7F^(CC67Bi_UMO8pXKYLB>V1l&mr}O#0@u-J_8U{jKU1SsmyXA z!}OEiRG+_lecurxK?5rO3W;M%5|s1NqnAXvNoh@b_oY{LL#F_^=j2TGI&x57Z1GTe zqBqAtm*O?XYWsnNRf>GonndeAj`kjp)D-^`&4(MAUC;AJQ%*p8~d#qa5N;Cy74XL;)zd-HdR}%D{RhQf*Qcen!;Rjh0H}O zD}&}|L`w7~?SixCNM%MW_Vv${vs04Bv*(!)FJA@mg9n@6`>shrfW&z}rE_1ZD`lQl z^Ww;x<7iW?OcoDH&B^c3B{7>1vwU9{#L!+TBrQTD|1WKg#bZN_-&95)yII7w!AAgt z!WW8tduy}iGYg^_am$e9I|Wta3Wj>hk7Y|0eb~b>+ty#|fMeGoJIh203RQVSAP&&1;D~2IBV1nEc6OYRw{>OC8wjAG_BmrqfNS zmRcrr|9ZDdk{G!LOV^!NkN3^_-!DIT%Wiynk5x2puhP*(RzvKK`?Yn+=;Tx-lr4qY zxDcAypSH86`xmf*aep(q(|ZcUsWP8>Z={H4e7s#8nlMRUZrOY-eEkh?bKMuL*rLUD zDDthHkY3F0kxTOM+eYI5i~yZ&ReeabTgQk6fZd*^XXJ zJjYQ%m7sJrhvD`!9wt)^vtdb8e8_<{10he0ziAog3EZ*S_$4H06_8~p#zPIOEspa_ zuf?HqOUS`aSKJGH6y$YBvJEjfvwFn&WVv#X&rJVmQd(6ldbF~aY|dUr9HqSM30?0D zQ1Xb~5t%~HBA5{Z4Q2Q6QOmc^*Blj&so^>p3g}Ex3uvRj*D$Wpz437a?Dd6`s+7v? zP5b$RH9eu?rWN-1UjNU>DQZV5WzeREAf9tk9+N*)qoayOmpXQu94_*#dc!sM*Ai1r zR3i5KBTCa}#UyeSDJZP`u6lMhGfbd=*;Jd)_N;l3NvVb3ml_;ixVQ2G?RWnJ8x%E+ zL`C7Us$SLp;VMPK@2&y|ITkJ;nzMpLixAjzSiO$Sk)n+Nm`iku?jT+Qbi!-!i3fWE zHY6PD3!@A{6WV0bjrvOyg>s@{m_8YG`L(oH#xs8vZ^RGlSK42mEt=9PbU4OF<2XS; zBwafiZF3{m3F3M4r$$lmGLdC36w@Te`894qKi)~6ei}M<8|0gX`am-*AEsVhRFrEu zH2u5S-$M-qcB?gX0P$4KK~xOtFJ^};dfTy@|WLT6k!(s@qt6imniRINi)zffOi9~ zWZ9PI+^8DBSLZiz4^$zn2utp5gSsk{rizd)Sm-j)H)czdq})Vq z8E?H|cV9%_^g@n_>wi#zQZ*^>s@ckcVPIvo8EpadtGFyYK$fw8hNdbnux!%f9;mNv zeeAv16FBq=r6z-yar&w%xA?F;VNMO`c=!#(T!mRi3U^ZB6K06zPs`G+S^OM+cXEA4 zL-a6(hd>IN&_FrG;zy&S{~X<};Ypu=i+5!~hQ-$9w><02>E~Ue#ttyfJg9}wNp6`9 z2|Jv~8i#E6+0u3$C2WoF5Py&!HizAM3XdQ}p2}_Jx)8*_KeYYpFi^lu9VKXgVJ-lQ zUOG3Un)I76G1)k3EZYeUTWl7-VZKZXCQW8~8?MK}Q&sLZJHKi-?9!-^L&L* ze5|4}@UG$+1M)b^@C1-G?F9W+kOxT+WihLO8qLa_wCR68R)nIBglrA&t~YVWhKDYp|n3pQ+^FjAg(K9K9Dpf_`iR~ zu%zq%rwdE&tMN??XL|R?z<@whPKwqxy_9$rY1} z4Y{Cx@yjwiD}1d!u{qDJibe<|AY_Pnlz7tE7(^G9WMT?c&5>&__6Jx0Uc39?;;ET4 zhH-eRcYa$oLE(_vi$Il`?LCEO`pj@tN{s?G*JxjrF z9hvNXlEd0+C+9Lo6;vyN?(p)gBPG$v>D2jiJ!2sSvTMtC5$Y&Hf0Gk?NSMHt+vTj5 zC8`TSM)tSRH};^AmX)prT;f}Gw#<284rbK;<(4U(N6shdYS8gyTHlN)fLhPs3F7x^Rkz|4MT$lQIN&?Z(6yQ7+fVu|rL*tRFWj49 z0IP`E<=mc9!*npJQU-Dsk;!^m9|D{s2M(wUIl7^eK7jRUeaGWSM}T{a8jY|&y69Kf zY&v}P>vW(c_s%y3E@Ae}s5^e6c5>%8_@GtKcAyWWRyQ4UDc;?FNWE z3Y}&ByyV_SN->lEF~WNMJMf#7CdD+n{}~X8Tra0?%F~n=?tqsMVPv+Buvd#`{7NWs zCV?&zwlBw^TI~yohVS|5<|}`j1r4CS&~b}>kH)>iT`t_GzoT#&Ly5VP0H3l)ktG(z zORYH6#cXqyP-~}RSFu&A66L}gf?JxY9d>kMoJIMoi(eGu3TGeDH6nvS=xkF)&fcsH zM3gEc>w(shJiJXU`&17$p#7(CM#sbYGUl~zovW~r|4vhs*ZSvn&cJ7b>h!DhVXVSh zNbaEGO<@n)Cu<(CMo1d&iDS}QIpmGkgcZ=rs;YUo$;{(#meh`ePBe3DZ}N?c#2c8x z*xFB?6=|dk=dC@=rk;`w#J@p;4C|d*Ytn~4x9$ny0t9smtpo!QoUX?`O~Zp745)e& zN+fb?g6S`=U-NPwgwFaJE0*h*i*>-L#Ii6_Tb11vtuAYu(HMd8lQ_3W#r7v#dtbj$ zW_`|yCL5fwUsU1X9VihA#S3_~jC-S*N%;+6GWhP4&ctjO{CmyAd$>8`Tq{OZNb3JN zAs&?>Hx8Z?truzBpuMCQ9Pt;Q`xeNv*v&5w<`;f;c7*RVdmA*BfJu}yk{QeU#2$tU zJdiaw+_nS)#xQH5d>Nz0%%5LkpLbnlnxu>-tqGx$R_38ApFNuspv{xp1gKvLxQ2X^ z|Hr8@Bw)ps(+1yKZHoZ)#(*p9o$jd05RO?=%_hV}WDRu*)4+v|uhD#^c^8&yE*@JI g0>H80OV-=t7{{74@Wk?aLcnKq*#z@O7x(!802MAwaR2}S literal 0 HcmV?d00001 diff --git a/Yggdrasil Network iOS/Extensions/UIDevice.swift b/Yggdrasil Network iOS/Extensions/UIDevice.swift new file mode 100644 index 0000000..fb834e1 --- /dev/null +++ b/Yggdrasil Network iOS/Extensions/UIDevice.swift @@ -0,0 +1,28 @@ +import Foundation +import UIKit + +extension UIDevice { + /// A Boolean value indicating whether the device has cellular data capabilities (true) or not (false). + var hasCellularCapabilites: Bool { + var addrs: UnsafeMutablePointer? + var cursor: UnsafeMutablePointer? + + defer { freeifaddrs(addrs) } + + guard getifaddrs(&addrs) == 0 else { return false } + cursor = addrs + + while cursor != nil { + guard + let utf8String = cursor?.pointee.ifa_name, + let name = NSString(utf8String: utf8String), + name == "pdp_ip0" + else { + cursor = cursor?.pointee.ifa_next + continue + } + return true + } + return false + } +} diff --git a/Yggdrasil Network iOS/Info.plist b/Yggdrasil Network iOS/Info.plist new file mode 100644 index 0000000..fef3abf --- /dev/null +++ b/Yggdrasil Network iOS/Info.plist @@ -0,0 +1,76 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Yggdrasil + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.1 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSApplicationCategoryType + public.app-category.utilities + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UTExportedTypeDeclarations + + + UTTypeConformsTo + + public.text + + UTTypeDescription + Yggdrasil Configuration File + UTTypeIconFiles + + UTTypeIdentifier + eu.neilalexander.yggdrasil.configuration + UTTypeTagSpecification + + public.filename-extension + + yggconf + + + + + + diff --git a/Yggdrasil Network iOS/Storyboards/Base.lproj/LaunchScreen.storyboard b/Yggdrasil Network iOS/Storyboards/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..16aafe8 --- /dev/null +++ b/Yggdrasil Network iOS/Storyboards/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yggdrasil Network iOS/Storyboards/Base.lproj/Main.storyboard b/Yggdrasil Network iOS/Storyboards/Base.lproj/Main.storyboard new file mode 100644 index 0000000..2544e02 --- /dev/null +++ b/Yggdrasil Network iOS/Storyboards/Base.lproj/Main.storyboard @@ -0,0 +1,704 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The above options allow you to control whether your device is reachable from other devices on the network. "All outgoing traffic" allows connections only to devices that you initiate first. This does not affect peerings. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Configuration will be exported to the Files app. Your configuration contains your private keys which are extremely sensitive. You must not share it with others. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yggdrasil Network iOS/UI Components/CopyableLabel.swift b/Yggdrasil Network iOS/UI Components/CopyableLabel.swift new file mode 100644 index 0000000..bd6fce0 --- /dev/null +++ b/Yggdrasil Network iOS/UI Components/CopyableLabel.swift @@ -0,0 +1,52 @@ +// +// CopyableLabel.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 26/02/2019. +// + +import UIKit + +class CopyableLabel: UILabel { + override public var canBecomeFirstResponder: Bool { + get { + return true + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.isUserInteractionEnabled = true + self.addGestureRecognizer(UILongPressGestureRecognizer( + target: self, + action: #selector(showMenu(sender:)) + )) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.isUserInteractionEnabled = true + self.addGestureRecognizer(UILongPressGestureRecognizer( + target: self, + action: #selector(showMenu(sender:)) + )) + } + + override func copy(_ sender: Any?) { + UIPasteboard.general.string = text + UIMenuController.shared.setMenuVisible(false, animated: true) + } + + @objc func showMenu(sender: Any?) { + self.becomeFirstResponder() + let menu = UIMenuController.shared + if !menu.isMenuVisible { + menu.setTargetRect(bounds, in: self) + menu.setMenuVisible(true, animated: true) + } + } + + override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { + return (action == #selector(copy(_:))) + } +} diff --git a/Yggdrasil Network iOS/UI Components/ToggleTableViewCell.swift b/Yggdrasil Network iOS/UI Components/ToggleTableViewCell.swift new file mode 100644 index 0000000..703782d --- /dev/null +++ b/Yggdrasil Network iOS/UI Components/ToggleTableViewCell.swift @@ -0,0 +1,26 @@ +// +// ToggleTableViewCell.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 07/01/2019. +// + +import UIKit + +class ToggleTableViewCell: UITableViewCell { + + @IBOutlet weak var label: UILabel! + @IBOutlet weak var toggle: UISwitch! + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + +} diff --git a/Yggdrasil Network iOS/View Controllers/PeersViewController.swift b/Yggdrasil Network iOS/View Controllers/PeersViewController.swift new file mode 100644 index 0000000..0105ad4 --- /dev/null +++ b/Yggdrasil Network iOS/View Controllers/PeersViewController.swift @@ -0,0 +1,312 @@ +// +// PeersViewController.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 07/01/2019. +// + +import UIKit +import NetworkExtension +import CoreTelephony + +class PeersViewController: UITableViewController { + var app = UIApplication.shared.delegate as! AppDelegate + var config: [String: Any]? = nil + + @IBOutlet var peerTable: UITableView! + @IBOutlet weak var addButtonItem: UIBarButtonItem! + + override func viewDidLoad() { + super.viewDidLoad() + + if let proto = self.app.vpnManager.protocolConfiguration as? NETunnelProviderProtocol { + config = proto.providerConfiguration ?? nil + } + + self.navigationItem.rightBarButtonItems = [ + self.editButtonItem, + self.addButtonItem + ] + } + + override func viewWillAppear(_ animated: Bool) { + NotificationCenter.default.addObserver(self, selector: #selector(self.onYggdrasilPeersUpdated), name: NSNotification.Name.YggdrasilPeersUpdated, object: nil) + } + + override func viewWillDisappear(_ animated: Bool) { + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.YggdrasilPeersUpdated, object: nil) + } + + @objc func onYggdrasilPeersUpdated(notification: NSNotification) { + peerTable.reloadData() + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 3 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case 0: return app.yggdrasilSwitchPeers.count + case 1: + if let config = self.app.yggdrasilConfig { + if let peers = config.get("Peers") as? [String] { + return peers.count + } + } + return 0 + case 2: + if UIDevice.current.hasCellularCapabilites { + return 3 + } + return 2 + default: return 0 + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + switch indexPath.section { + case 0: + let cell = tableView.dequeueReusableCell(withIdentifier: "discoveredPeerPrototype", for: indexPath) + let peers = app.yggdrasilSwitchPeers.sorted { (a, b) -> Bool in + return (a["Port"] as! Int) < (b["Port"] as! Int) + } + + if indexPath.row < peers.count { + let value = peers[indexPath.row] + let proto = value["Protocol"] as? String ?? "tcp" + let sent = value["BytesSent"] as? Double ?? 0 + let recvd = value["BytesRecvd"] as? Double ?? 0 + let rx = self.format(bytes: sent) + let tx = self.format(bytes: recvd) + + cell.textLabel?.text = "\(value["Endpoint"] ?? "unknown")" + cell.detailTextLabel?.text = "\(proto.uppercased()) peer on port \(value["Port"] ?? "unknown"), sent \(tx), received \(rx)" + } + return cell + case 1: + let cell = tableView.dequeueReusableCell(withIdentifier: "configuredPeerPrototype", for: indexPath) + if let config = self.app.yggdrasilConfig { + if let peers = config.get("Peers") as? [String] { + cell.textLabel?.text = peers[indexPath.last!] + } else { + cell.textLabel?.text = "(unknown)" + } + } + return cell + case 2: + switch indexPath.last { + case 0: + let cell = tableView.dequeueReusableCell(withIdentifier: "togglePrototype", for: indexPath) as! ToggleTableViewCell + cell.isUserInteractionEnabled = true + cell.label?.text = "Search for multicast peers" + cell.label?.isEnabled = true + cell.toggle?.addTarget(self, action: #selector(toggledMulticast), for: .valueChanged) + cell.toggle?.isEnabled = true + if let config = self.app.yggdrasilConfig { + let interfaces = config.get("MulticastInterfaces") as? [String] ?? [] + cell.toggle?.isOn = interfaces.contains("en*") + } + return cell + case 1: + let cell = tableView.dequeueReusableCell(withIdentifier: "togglePrototype", for: indexPath) as! ToggleTableViewCell + cell.isUserInteractionEnabled = false + cell.label?.text = "Search for nearby iOS peers" + cell.label?.isEnabled = false + cell.toggle?.addTarget(self, action: #selector(toggledAWDL), for: .valueChanged) + cell.toggle?.setOn(false, animated: false) + cell.toggle?.isEnabled = false + /*if let config = self.app.yggdrasilConfig { + let interfaces = config.get("MulticastInterfaces") as? [String] ?? [] + cell.toggle?.isOn = interfaces.contains("awdl0") + }*/ + return cell + case 2: + let cell = tableView.dequeueReusableCell(withIdentifier: "menuPrototype", for: indexPath) + cell.isUserInteractionEnabled = true + cell.textLabel?.text = "Device settings" + cell.textLabel?.isEnabled = true + return cell + default: + let cell = tableView.dequeueReusableCell(withIdentifier: "menuPrototype", for: indexPath) + cell.isUserInteractionEnabled = false + cell.textLabel?.text = "Unknown" + cell.textLabel?.isEnabled = true + return cell + } + default: + let cell = tableView.dequeueReusableCell(withIdentifier: "configuredPeerPrototype", for: indexPath) + cell.textLabel?.text = "(unknown)" + return cell + } + } + + func format(bytes: Double) -> String { + guard bytes > 0 else { + return "0 bytes" + } + + // Adapted from http://stackoverflow.com/a/18650828 + let suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] + let k: Double = 1000 + let i = floor(log(bytes) / log(k)) + + // Format number with thousands separator and everything below 1 GB with no decimal places. + let numberFormatter = NumberFormatter() + numberFormatter.maximumFractionDigits = i < 3 ? 0 : 1 + numberFormatter.numberStyle = .decimal + + let numberString = numberFormatter.string(from: NSNumber(value: bytes / pow(k, i))) ?? "Unknown" + let suffix = suffixes[Int(i)] + return "\(numberString) \(suffix)" + } + + @objc func toggledMulticast(_ sender: UISwitch) { + if let config = self.app.yggdrasilConfig { + var interfaces = config.get("MulticastInterfaces") as! [String] + if sender.isOn { + interfaces.append("en*") + } else { + interfaces.removeAll(where: { $0 == "en*" }) + } + config.set("MulticastInterfaces", to: interfaces as [String]) + try? config.save(to: &app.vpnManager) + } + } + + @objc func toggledAWDL(_ sender: UISwitch) { + if let config = self.app.yggdrasilConfig { + var interfaces = config.get("MulticastInterfaces") as! [String] + if sender.isOn { + interfaces.append("awdl0") + } else { + interfaces.removeAll(where: { $0 == "awdl0" }) + } + config.set("MulticastInterfaces", to: interfaces as [String]) + try? config.save(to: &app.vpnManager) + } + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + switch section { + case 0: + if self.app.yggdrasilPeers.count > 0 { + return "Connected Peers" + } + return "No peers currently connected" + case 1: + if let config = self.app.yggdrasilConfig { + if let peers = config.get("Peers") as? [String] { + if peers.count > 0 { + return "Configured Peers" + } + } + } + return "No peers currently configured" + case 2: + return "Peer Connectivity" + default: return "(Unknown)" + } + } + + override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { + switch section { + case 1: + return "Yggdrasil will automatically attempt to connect to configured peers when started." + case 2: + var str = "Multicast peers will be discovered on the same Wi-Fi network or via USB." + if UIDevice.current.hasCellularCapabilites { + str += " Data charges may apply when using mobile data. You can prevent mobile data usage in the device settings." + } + return str + default: return nil + } + } + + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return indexPath.first == 1 + } + + override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { + switch indexPath.first { + case 0: + return [UITableViewRowAction(style: UITableViewRowAction.Style.default, title: "Disconnect", handler: { (action, index) in + + })] + case 1: + return [UITableViewRowAction(style: UITableViewRowAction.Style.normal, title: "Remove", handler: { (action, index) in + print(action, index) + if let config = self.app.yggdrasilConfig { + config.remove(index: index.last!, from: "Peers") + do { + try config.save(to: &self.app.vpnManager) + tableView.reloadSections(IndexSet(integer: 1), with: UITableView.RowAnimation.automatic) + } catch { + let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) + self.parent?.present(alert, animated: true, completion: nil) + print("Error removing: \(error)") + } + } + })] + default: + return [] + } + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + switch indexPath.first { + case 2: + if let last = indexPath.last, last == 2 { + UIApplication.shared.open(NSURL(string:UIApplication.openSettingsURLString)! as URL, options: [:]) { (result) in + NSLog("Result " + result.description) + } + } + default: + break + } + tableView.deselectRow(at: indexPath, animated: true) + } + + @IBAction func addNewPeerButtonPressed(_ sender: UIBarButtonItem) { + let alert = UIAlertController(title: "Add Configured Peer", message: """ + Enter the full URI of the peer to add. Yggdrasil will automatically connect to this peer when started. + """, preferredStyle: UIAlertController.Style.alert) + let action = UIAlertAction(title: "Add", style: .default) { (alertAction) in + let textField = alert.textFields![0] as UITextField + if let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) { + if let config = self.app.yggdrasilConfig { + if let peers = config.get("Peers") as? [String], !peers.contains(text) { + config.add(text, in: "Peers") + do { + try config.save(to: &self.app.vpnManager) + if let index = config.get("Peers") as? [String] { + self.peerTable.insertRows(at: [IndexPath(indexes: [1, index.count-1])], with: .automatic) + self.peerTable.reloadSections(IndexSet(integer: 1), with: UITableView.RowAnimation.automatic) + } + } catch { + let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) + self.parent?.present(alert, animated: true, completion: nil) + print("Add error: \(error)") + } + } else { + let alert = UIAlertController(title: "Error", message: "Peer already exists", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) + self.parent?.present(alert, animated: true, completion: nil) + } + } + } + } + let cancel = UIAlertAction(title: "Cancel", style: .cancel) + alert.addTextField { (textField) in + textField.placeholder = "tcp://hostname:port" + } + alert.addAction(action) + alert.addAction(cancel) + self.present(alert, animated: true, completion: nil) + } + +} diff --git a/Yggdrasil Network iOS/View Controllers/SettingsViewController.swift b/Yggdrasil Network iOS/View Controllers/SettingsViewController.swift new file mode 100644 index 0000000..9637a46 --- /dev/null +++ b/Yggdrasil Network iOS/View Controllers/SettingsViewController.swift @@ -0,0 +1,159 @@ +// +// SettingsTableViewController.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 03/01/2019. +// + +import UIKit +import NetworkExtension + +class SettingsViewController: UITableViewController, UIDocumentBrowserViewControllerDelegate { + var app = UIApplication.shared.delegate as! AppDelegate + + @IBOutlet weak var deviceNameField: UITextField! + + @IBOutlet weak var encryptionPublicKeyLabel: UILabel! + @IBOutlet weak var signingPublicKeyLabel: UILabel! + + @IBOutlet weak var autoStartWiFiCell: UITableViewCell! + @IBOutlet weak var autoStartMobileCell: UITableViewCell! + + @IBOutlet weak var sessionFirewallPeeredCell: UITableViewCell! + @IBOutlet weak var sessionFirewallOtherCell: UITableViewCell! + @IBOutlet weak var sessionFirewallOutboundCell: UITableViewCell! + + override func viewDidLoad() { + super.viewDidLoad() + + if let config = self.app.yggdrasilConfig { + deviceNameField.text = config.get("name", inSection: "NodeInfo") as? String ?? "" + encryptionPublicKeyLabel.text = config.get("EncryptionPublicKey") as? String ?? "Unknown" + signingPublicKeyLabel.text = config.get("SigningPublicKey") as? String ?? "Unknown" + + sessionFirewallPeeredCell.accessoryType = config.get("AllowFromDirect", inSection: "SessionFirewall") as? Bool ?? true ? .checkmark : .none + sessionFirewallOtherCell.accessoryType = config.get("AllowFromRemote", inSection: "SessionFirewall") as? Bool ?? true ? .checkmark : .none + sessionFirewallOutboundCell.accessoryType = config.get("AlwaysAllowOutbound", inSection: "SessionFirewall") as? Bool ?? true ? .checkmark : .none + + autoStartWiFiCell.accessoryType = config.get("WiFi", inSection: "AutoStart") as? Bool ?? false ? .checkmark : .none + autoStartMobileCell.accessoryType = config.get("Mobile", inSection: "AutoStart") as? Bool ?? false ? .checkmark : .none + } + } + + @IBAction func deviceNameEdited(_ sender: UITextField) { + if let config = self.app.yggdrasilConfig { + config.set("name", inSection: "NodeInfo", to: sender.text) + try? config.save(to: &app.vpnManager) + } + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + switch indexPath.first { + case 1: + let settings = [ + "WiFi", + "Mobile" + ] + if let cell = tableView.cellForRow(at: indexPath) { + cell.accessoryType = cell.accessoryType == .checkmark ? .none : .checkmark + if let config = self.app.yggdrasilConfig { + config.set(settings[indexPath.last!], inSection: "AutoStart", to: cell.accessoryType == .checkmark) + try? config.save(to: &app.vpnManager) + } + } + case 2: + let settings = [ + "AllowFromDirect", + "AllowFromRemote", + "AlwaysAllowOutbound" + ] + if let cell = tableView.cellForRow(at: indexPath) { + cell.accessoryType = cell.accessoryType == .checkmark ? .none : .checkmark + if let config = self.app.yggdrasilConfig { + config.set(settings[indexPath.last!], inSection: "SessionFirewall", to: cell.accessoryType == .checkmark) + try? config.save(to: &app.vpnManager) + } + } + case 4: + switch indexPath.last { + case 0: // import + if #available(iOS 11.0, *) { + let open = UIDocumentBrowserViewController(forOpeningFilesWithContentTypes: ["eu.neilalexander.yggdrasil.configuration"]) + open.delegate = self + open.allowsDocumentCreation = false + open.allowsPickingMultipleItems = false + open.additionalTrailingNavigationBarButtonItems = [ UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelDocumentBrowser)) ] + self.present(open, animated: true, completion: nil) + } else { + let alert = UIAlertController(title: "Import Configuration", message: "Not supported on this version of iOS!", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) + self.present(alert, animated: true, completion: nil) + } + case 1: // export + if let config = self.app.yggdrasilConfig, let data = config.data() { + var fileURL: URL? + var fileDir: URL? + do { + let dateFormatter = DateFormatter() + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + dateFormatter.dateFormat = "yyyy-MM-dd" + let date = dateFormatter.string(from: Date()) + fileDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) + fileURL = fileDir?.appendingPathComponent("Yggdrasil Backup \(date).yggconf") + try? data.write(to: fileURL!) + } catch { + break + } + if let dir = fileDir { + let sharedurl = dir.absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://") + let furl: URL = URL(string: sharedurl)! + UIApplication.shared.open(furl, options: [:], completionHandler: nil) + } + } + default: + break + } + case 5: + let alert = UIAlertController(title: "Warning", message: "This operation will reset your configuration and generate new keys. This is not reversible unless your configuration has been exported. Changes will not take effect until the next time Yggdrasil is restarted.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Reset", style: .destructive, handler: { action in + self.app.yggdrasilConfig = ConfigurationProxy() + if let config = self.app.yggdrasilConfig { + try? config.save(to: &self.app.vpnManager) + self.viewDidLoad() + }})) + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + self.present(alert, animated: true, completion: nil) + default: + break + } + tableView.deselectRow(at: indexPath, animated: true) + } + + @objc func cancelDocumentBrowser() { + self.dismiss(animated: true, completion: nil) + } + + @available(iOS 11.0, *) + func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) { + do { + if let url = documentURLs.first { + let data = try Data(contentsOf: url) + let conf = try ConfigurationProxy(json: data) + try conf.save(to: &self.app.vpnManager) + self.app.yggdrasilConfig = conf + + controller.dismiss(animated: true, completion: nil) + let alert = UIAlertController(title: "Import Configuration", message: "Configuration file has been imported.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) + self.present(alert, animated: true, completion: nil) + } + } catch { + controller.dismiss(animated: true, completion: nil) + let alert = UIAlertController(title: "Import Failed", message: "Unable to import this configuration file.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil)) + self.present(alert, animated: true, completion: nil) + } + self.viewDidLoad() + } + +} diff --git a/Yggdrasil Network iOS/View Controllers/SplitViewController.swift b/Yggdrasil Network iOS/View Controllers/SplitViewController.swift new file mode 100644 index 0000000..c4e580d --- /dev/null +++ b/Yggdrasil Network iOS/View Controllers/SplitViewController.swift @@ -0,0 +1,18 @@ +// +// SplitViewController.swift +// YggdrasilNetwork +// +// Created by Neil Alexander on 02/01/2019. +// + +import UIKit + +class SplitViewController: UISplitViewController, UISplitViewControllerDelegate { + + override func viewDidLoad() { + super.viewDidLoad() + self.delegate = self + self.preferredDisplayMode = .allVisible + } + +} diff --git a/Yggdrasil Network iOS/View Controllers/TableViewController.swift b/Yggdrasil Network iOS/View Controllers/TableViewController.swift new file mode 100644 index 0000000..7261e85 --- /dev/null +++ b/Yggdrasil Network iOS/View Controllers/TableViewController.swift @@ -0,0 +1,133 @@ +import UIKit +import NetworkExtension +import Yggdrasil + +class TableViewController: UITableViewController { + var app = UIApplication.shared.delegate as! AppDelegate + + @IBOutlet var connectedStatusLabel: UILabel! + + @IBOutlet var toggleTableView: UITableView! + @IBOutlet var toggleLabel: UILabel! + @IBOutlet var toggleConnect: UISwitch! + + @IBOutlet weak var statsSelfIPCell: UITableViewCell! + @IBOutlet weak var statsSelfSubnetCell: UITableViewCell! + @IBOutlet weak var statsSelfCoordsCell: UITableViewCell! + + @IBOutlet var statsSelfIP: UILabel! + @IBOutlet var statsSelfSubnet: UILabel! + @IBOutlet var statsSelfCoords: UILabel! + @IBOutlet var statsSelfPeers: UILabel! + + override func viewDidLoad() { + NotificationCenter.default.addObserver(self, selector: #selector(self.onYggdrasilSelfUpdated), name: NSNotification.Name.YggdrasilSelfUpdated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.onYggdrasilPeersUpdated), name: NSNotification.Name.YggdrasilPeersUpdated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.onYggdrasilSwitchPeersUpdated), name: NSNotification.Name.YggdrasilSwitchPeersUpdated, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(self.onYggdrasilSettingsUpdated), name: NSNotification.Name.YggdrasilSettingsUpdated, object: nil) + } + + @IBAction func onRefreshButton(_ sender: UIButton) { + sender.isEnabled = false + app.makeIPCRequests() + sender.isEnabled = true + } + + override func viewWillAppear(_ animated: Bool) { + //NotificationCenter.default.addObserver(self, selector: #selector(TableViewController.VPNStatusDidChange(_:)), name: NSNotification.Name.NEVPNStatusDidChange, object: nil) + + if let row = self.tableView.indexPathForSelectedRow { + self.tableView.deselectRow(at: row, animated: true) + } + } + + override func viewWillDisappear(_ animated: Bool) { + //NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NEVPNStatusDidChange, object: nil) + } + + override func viewDidAppear(_ animated: Bool) { + self.onYggdrasilSelfUpdated(notification: NSNotification.init(name: NSNotification.Name.YggdrasilSettingsUpdated, object: nil)) + } + + override func viewWillLayoutSubviews() { + self.onYggdrasilSelfUpdated(notification: NSNotification.init(name: NSNotification.Name.YggdrasilSettingsUpdated, object: nil)) + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if let row = self.tableView.indexPathForSelectedRow { + self.tableView.deselectRow(at: row, animated: true) + } + } + + @objc func onYggdrasilSettingsUpdated(notification: NSNotification) { + toggleLabel.isEnabled = !app.vpnManager.isOnDemandEnabled + toggleConnect.isEnabled = !app.vpnManager.isOnDemandEnabled + + if let footer = toggleTableView.footerView(forSection: 0) { + if let label = footer.textLabel { + label.text = app.vpnManager.isOnDemandEnabled ? "Yggdrasil will automatically stop and start based on settings." : "You must restart Yggdrasil to make configuration changes effective." + } + } + } + + func updateConnectedStatus() { + if self.app.vpnManager.connection.status == .connected { + if app.yggdrasilSwitchPeers.count > 0 { + connectedStatusLabel.text = "Connected" + connectedStatusLabel.textColor = UIColor(red: 0.37, green: 0.79, blue: 0.35, alpha: 1.0) + } else { + connectedStatusLabel.text = "No active connections" + connectedStatusLabel.textColor = UIColor.red + } + } else { + connectedStatusLabel.text = "Not enabled" + connectedStatusLabel.textColor = UIColor.systemGray + } + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + } + + @objc func onYggdrasilSelfUpdated(notification: NSNotification) { + statsSelfIP.text = app.yggdrasilSelfIP + statsSelfSubnet.text = app.yggdrasilSelfSubnet + statsSelfCoords.text = app.yggdrasilSelfCoords + + statsSelfIPCell.layoutSubviews() + statsSelfSubnetCell.layoutSubviews() + statsSelfCoordsCell.layoutSubviews() + + let status = self.app.vpnManager.connection.status + toggleConnect.isOn = status == .connecting || status == .connected + + self.updateConnectedStatus() + } + + @objc func onYggdrasilSwitchPeersUpdated(notification: NSNotification) { + self.updateConnectedStatus() + } + + @objc func onYggdrasilPeersUpdated(notification: NSNotification) { + let peercount = app.yggdrasilSwitchPeers.count + if peercount <= 0 { + statsSelfPeers.text = "No peers" + } else if peercount == 1 { + statsSelfPeers.text = "\(peercount) peer" + } else { + statsSelfPeers.text = "\(peercount) peers" + } + } + + @IBAction func toggleVPNStatus(_ sender: UISwitch, forEvent event: UIEvent) { + if sender.isOn { + do { + try self.app.vpnManager.connection.startVPNTunnel() + } catch { + print(error) + } + } else { + self.app.vpnManager.connection.stopVPNTunnel() + } + } +} diff --git a/Yggdrasil Network.xcodeproj/project.pbxproj b/Yggdrasil Network.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b3c8c76 --- /dev/null +++ b/Yggdrasil Network.xcodeproj/project.pbxproj @@ -0,0 +1,670 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 3913E9C021DD3A51001E0EC7 /* SplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3913E9BF21DD3A51001E0EC7 /* SplitViewController.swift */; }; + 3939196B21E35A7C009320F3 /* PeersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3939196A21E35A7C009320F3 /* PeersViewController.swift */; }; + 3939196D21E39313009320F3 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3939196C21E39313009320F3 /* UIDevice.swift */; }; + 3939197321E39815009320F3 /* ToggleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3939197221E39815009320F3 /* ToggleTableViewCell.swift */; }; + 3939197521E3AADB009320F3 /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3939197421E3AADB009320F3 /* ConfigurationProxy.swift */; }; + 3940354623F4C0C100E81A29 /* Yggdrasil.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3944B66321DAAEA500AF102D /* Yggdrasil.framework */; }; + 394A1EB321DEA46400D9F553 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 394A1EB221DEA46400D9F553 /* SettingsViewController.swift */; }; + 39682A392225AD15004FB670 /* CopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39682A382225AD15004FB670 /* CopyableLabel.swift */; }; + 397346D521E8E422009B17F6 /* CrossPlatformAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397346D421E8E422009B17F6 /* CrossPlatformAppDelegate.swift */; }; + 39AE88392319C93F0010FFF6 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */; }; + 39AE883A2319CA750010FFF6 /* ConfigurationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3939197421E3AADB009320F3 /* ConfigurationProxy.swift */; }; + 39CC924C221DEDCE004960DC /* NSNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* NSNotification.swift */; }; + 39CC924D221DEDD3004960DC /* NSNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CC924B221DEDCE004960DC /* NSNotification.swift */; }; + E593CE6F1DF8FC3C00D7265D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE6E1DF8FC3C00D7265D /* AppDelegate.swift */; }; + E593CE711DF8FC3C00D7265D /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE701DF8FC3C00D7265D /* TableViewController.swift */; }; + E593CE741DF8FC3C00D7265D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E593CE721DF8FC3C00D7265D /* Main.storyboard */; }; + E593CE761DF8FC3C00D7265D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E593CE751DF8FC3C00D7265D /* Assets.xcassets */; }; + E593CE791DF8FC3C00D7265D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E593CE771DF8FC3C00D7265D /* LaunchScreen.storyboard */; }; + E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */; }; + E593CEA01DF905AF00D7265D /* YggdrasilNetworkExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E593CE9E1DF905AF00D7265D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E593CE631DF8FC3C00D7265D /* Project object */; + proxyType = 1; + remoteGlobalIDString = E593CE961DF905AF00D7265D; + remoteInfo = NEPacketTunnelVPNDemoTunnel; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + E593CEA41DF905B000D7265D /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + E593CEA01DF905AF00D7265D /* YggdrasilNetworkExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3913E99C21DB9B1C001E0EC7 /* YggdrasilNetworkExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = YggdrasilNetworkExtension.entitlements; sourceTree = ""; }; + 3913E99E21DB9B41001E0EC7 /* YggdrasilNetwork.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = YggdrasilNetwork.entitlements; sourceTree = ""; }; + 3913E9BF21DD3A51001E0EC7 /* SplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewController.swift; sourceTree = ""; }; + 3939196A21E35A7C009320F3 /* PeersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeersViewController.swift; sourceTree = ""; }; + 3939196C21E39313009320F3 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = ""; }; + 3939197221E39815009320F3 /* ToggleTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleTableViewCell.swift; sourceTree = ""; }; + 3939197421E3AADB009320F3 /* ConfigurationProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationProxy.swift; sourceTree = ""; }; + 3944B66321DAAEA500AF102D /* Yggdrasil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Yggdrasil.framework; sourceTree = ""; }; + 394A1EB221DEA46400D9F553 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; + 39682A382225AD15004FB670 /* CopyableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLabel.swift; sourceTree = ""; }; + 397346D021E8BB5F009B17F6 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/NetworkExtension.framework; sourceTree = DEVELOPER_DIR; }; + 397346D421E8E422009B17F6 /* CrossPlatformAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossPlatformAppDelegate.swift; sourceTree = ""; }; + 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + 39CC924B221DEDCE004960DC /* NSNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSNotification.swift; sourceTree = ""; }; + E593CE6B1DF8FC3C00D7265D /* YggdrasilNetwork.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YggdrasilNetwork.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E593CE6E1DF8FC3C00D7265D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + E593CE701DF8FC3C00D7265D /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; + E593CE731DF8FC3C00D7265D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + E593CE751DF8FC3C00D7265D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E593CE781DF8FC3C00D7265D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + E593CE7A1DF8FC3C00D7265D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = YggdrasilNetworkExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; + E593CE9D1DF905AF00D7265D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E593CE681DF8FC3C00D7265D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 39AE88392319C93F0010FFF6 /* NetworkExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E593CE941DF905AF00D7265D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3940354623F4C0C100E81A29 /* Yggdrasil.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 39042D3121E4BDA20088A534 /* Storyboards */ = { + isa = PBXGroup; + children = ( + E593CE721DF8FC3C00D7265D /* Main.storyboard */, + E593CE771DF8FC3C00D7265D /* LaunchScreen.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + 3939196E21E3979C009320F3 /* View Controllers */ = { + isa = PBXGroup; + children = ( + E593CE701DF8FC3C00D7265D /* TableViewController.swift */, + 3939196A21E35A7C009320F3 /* PeersViewController.swift */, + 394A1EB221DEA46400D9F553 /* SettingsViewController.swift */, + 3913E9BF21DD3A51001E0EC7 /* SplitViewController.swift */, + ); + path = "View Controllers"; + sourceTree = ""; + }; + 3939196F21E397A9009320F3 /* Extensions */ = { + isa = PBXGroup; + children = ( + 3939196C21E39313009320F3 /* UIDevice.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 3939197021E397BC009320F3 /* Application */ = { + isa = PBXGroup; + children = ( + E593CE6E1DF8FC3C00D7265D /* AppDelegate.swift */, + 39CC924B221DEDCE004960DC /* NSNotification.swift */, + ); + path = Application; + sourceTree = ""; + }; + 3939197121E397D1009320F3 /* UI Components */ = { + isa = PBXGroup; + children = ( + 3939197221E39815009320F3 /* ToggleTableViewCell.swift */, + 39682A382225AD15004FB670 /* CopyableLabel.swift */, + ); + path = "UI Components"; + sourceTree = ""; + }; + 399D032221DA775D0016354F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 39AE88382319C93F0010FFF6 /* NetworkExtension.framework */, + 397346D021E8BB5F009B17F6 /* NetworkExtension.framework */, + 3944B66321DAAEA500AF102D /* Yggdrasil.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 39AE88372319C8840010FFF6 /* Yggdrasil Network Cross-Platform */ = { + isa = PBXGroup; + children = ( + 397346D421E8E422009B17F6 /* CrossPlatformAppDelegate.swift */, + 3939197421E3AADB009320F3 /* ConfigurationProxy.swift */, + ); + path = "Yggdrasil Network Cross-Platform"; + sourceTree = ""; + }; + E593CE621DF8FC3C00D7265D = { + isa = PBXGroup; + children = ( + 3913E99E21DB9B41001E0EC7 /* YggdrasilNetwork.entitlements */, + 3913E99C21DB9B1C001E0EC7 /* YggdrasilNetworkExtension.entitlements */, + E593CE981DF905AF00D7265D /* Yggdrasil Network Extension */, + 39AE88372319C8840010FFF6 /* Yggdrasil Network Cross-Platform */, + E593CE6D1DF8FC3C00D7265D /* Yggdrasil Network iOS */, + E593CE6C1DF8FC3C00D7265D /* Products */, + 399D032221DA775D0016354F /* Frameworks */, + ); + sourceTree = ""; + }; + E593CE6C1DF8FC3C00D7265D /* Products */ = { + isa = PBXGroup; + children = ( + E593CE6B1DF8FC3C00D7265D /* YggdrasilNetwork.app */, + E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */, + ); + name = Products; + sourceTree = ""; + }; + E593CE6D1DF8FC3C00D7265D /* Yggdrasil Network iOS */ = { + isa = PBXGroup; + children = ( + 39042D3121E4BDA20088A534 /* Storyboards */, + 3939197021E397BC009320F3 /* Application */, + 3939196F21E397A9009320F3 /* Extensions */, + 3939196E21E3979C009320F3 /* View Controllers */, + 3939197121E397D1009320F3 /* UI Components */, + E593CE751DF8FC3C00D7265D /* Assets.xcassets */, + E593CE7A1DF8FC3C00D7265D /* Info.plist */, + ); + path = "Yggdrasil Network iOS"; + sourceTree = ""; + }; + E593CE981DF905AF00D7265D /* Yggdrasil Network Extension */ = { + isa = PBXGroup; + children = ( + E593CE9B1DF905AF00D7265D /* PacketTunnelProvider.swift */, + E593CE9D1DF905AF00D7265D /* Info.plist */, + ); + path = "Yggdrasil Network Extension"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E593CE6A1DF8FC3C00D7265D /* YggdrasilNetwork */ = { + isa = PBXNativeTarget; + buildConfigurationList = E593CE7D1DF8FC3C00D7265D /* Build configuration list for PBXNativeTarget "YggdrasilNetwork" */; + buildPhases = ( + E593CE671DF8FC3C00D7265D /* Sources */, + E593CE681DF8FC3C00D7265D /* Frameworks */, + E593CE691DF8FC3C00D7265D /* Resources */, + E593CEA41DF905B000D7265D /* Embed App Extensions */, + ); + buildRules = ( + ); + dependencies = ( + E593CE9F1DF905AF00D7265D /* PBXTargetDependency */, + ); + name = YggdrasilNetwork; + productName = NEPacketTunnelVPNDemo; + productReference = E593CE6B1DF8FC3C00D7265D /* YggdrasilNetwork.app */; + productType = "com.apple.product-type.application"; + }; + E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = E593CEA31DF905AF00D7265D /* Build configuration list for PBXNativeTarget "YggdrasilNetworkExtension" */; + buildPhases = ( + E593CE931DF905AF00D7265D /* Sources */, + E593CE941DF905AF00D7265D /* Frameworks */, + E593CE951DF905AF00D7265D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = YggdrasilNetworkExtension; + productName = NEPacketTunnelVPNDemoTunnel; + productReference = E593CE971DF905AF00D7265D /* YggdrasilNetworkExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E593CE631DF8FC3C00D7265D /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1010; + LastUpgradeCheck = 1030; + ORGANIZATIONNAME = ""; + TargetAttributes = { + E593CE6A1DF8FC3C00D7265D = { + CreatedOnToolsVersion = 8.1; + DevelopmentTeam = R9AV23TXF2; + LastSwiftMigration = 1030; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + com.apple.NetworkExtensions.iOS = { + enabled = 1; + }; + com.apple.VPNLite = { + enabled = 1; + }; + com.apple.iCloud = { + enabled = 0; + }; + }; + }; + E593CE961DF905AF00D7265D = { + CreatedOnToolsVersion = 8.1; + DevelopmentTeam = R9AV23TXF2; + LastSwiftMigration = 1030; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + com.apple.NetworkExtensions.iOS = { + enabled = 1; + }; + com.apple.VPNLite = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = E593CE661DF8FC3C00D7265D /* Build configuration list for PBXProject "Yggdrasil Network" */; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E593CE621DF8FC3C00D7265D; + productRefGroup = E593CE6C1DF8FC3C00D7265D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E593CE6A1DF8FC3C00D7265D /* YggdrasilNetwork */, + E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E593CE691DF8FC3C00D7265D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E593CE791DF8FC3C00D7265D /* LaunchScreen.storyboard in Resources */, + E593CE761DF8FC3C00D7265D /* Assets.xcassets in Resources */, + E593CE741DF8FC3C00D7265D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E593CE951DF905AF00D7265D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E593CE671DF8FC3C00D7265D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3939196B21E35A7C009320F3 /* PeersViewController.swift in Sources */, + 3939197321E39815009320F3 /* ToggleTableViewCell.swift in Sources */, + 3939196D21E39313009320F3 /* UIDevice.swift in Sources */, + 394A1EB321DEA46400D9F553 /* SettingsViewController.swift in Sources */, + E593CE711DF8FC3C00D7265D /* TableViewController.swift in Sources */, + 39682A392225AD15004FB670 /* CopyableLabel.swift in Sources */, + 3939197521E3AADB009320F3 /* ConfigurationProxy.swift in Sources */, + E593CE6F1DF8FC3C00D7265D /* AppDelegate.swift in Sources */, + 397346D521E8E422009B17F6 /* CrossPlatformAppDelegate.swift in Sources */, + 3913E9C021DD3A51001E0EC7 /* SplitViewController.swift in Sources */, + 39CC924C221DEDCE004960DC /* NSNotification.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E593CE931DF905AF00D7265D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 39CC924D221DEDD3004960DC /* NSNotification.swift in Sources */, + E593CE9C1DF905AF00D7265D /* PacketTunnelProvider.swift in Sources */, + 39AE883A2319CA750010FFF6 /* ConfigurationProxy.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E593CE9F1DF905AF00D7265D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E593CE961DF905AF00D7265D /* YggdrasilNetworkExtension */; + targetProxy = E593CE9E1DF905AF00D7265D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + E593CE721DF8FC3C00D7265D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E593CE731DF8FC3C00D7265D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + E593CE771DF8FC3C00D7265D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E593CE781DF8FC3C00D7265D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E593CE7B1DF8FC3C00D7265D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALID_ARCHS = "arm64 arm64e armv7 armv7s i386 x86_64"; + }; + name = Debug; + }; + E593CE7C1DF8FC3C00D7265D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VALID_ARCHS = "arm64 arm64e armv7 armv7s i386 x86_64"; + }; + name = Release; + }; + E593CE7E1DF8FC3C00D7265D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = YggdrasilNetwork.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 20; + DEVELOPMENT_TEAM = R9AV23TXF2; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E593CE7F1DF8FC3C00D7265D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = YggdrasilNetwork.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 20; + DEVELOPMENT_TEAM = R9AV23TXF2; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + E593CEA11DF905AF00D7265D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = YggdrasilNetworkExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 20; + DEVELOPMENT_TEAM = R9AV23TXF2; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network Extension/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.extension; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + E593CEA21DF905AF00D7265D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = YggdrasilNetworkExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 20; + DEVELOPMENT_TEAM = R9AV23TXF2; + ENABLE_BITCODE = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = "$(SRCROOT)/Yggdrasil Network Extension/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = eu.neilalexander.yggdrasil.extension; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "8ce353d5-fd5f-4d5e-b664-8ad294091125"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E593CE661DF8FC3C00D7265D /* Build configuration list for PBXProject "Yggdrasil Network" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E593CE7B1DF8FC3C00D7265D /* Debug */, + E593CE7C1DF8FC3C00D7265D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E593CE7D1DF8FC3C00D7265D /* Build configuration list for PBXNativeTarget "YggdrasilNetwork" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E593CE7E1DF8FC3C00D7265D /* Debug */, + E593CE7F1DF8FC3C00D7265D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E593CEA31DF905AF00D7265D /* Build configuration list for PBXNativeTarget "YggdrasilNetworkExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E593CEA11DF905AF00D7265D /* Debug */, + E593CEA21DF905AF00D7265D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E593CE631DF8FC3C00D7265D /* Project object */; +} diff --git a/Yggdrasil Network.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Yggdrasil Network.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ac3d584 --- /dev/null +++ b/Yggdrasil Network.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/Yggdrasil Network.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme new file mode 100644 index 0000000..759e8a3 --- /dev/null +++ b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetwork.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetworkExtension.xcscheme b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetworkExtension.xcscheme new file mode 100644 index 0000000..244a32d --- /dev/null +++ b/Yggdrasil Network.xcodeproj/xcshareddata/xcschemes/YggdrasilNetworkExtension.xcscheme @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/YggdrasilNetwork.entitlements b/YggdrasilNetwork.entitlements new file mode 100644 index 0000000..e1e1350 --- /dev/null +++ b/YggdrasilNetwork.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.security.application-groups + + group.eu.neilalexander.yggdrasil + + + diff --git a/YggdrasilNetworkExtension.entitlements b/YggdrasilNetworkExtension.entitlements new file mode 100644 index 0000000..e1e1350 --- /dev/null +++ b/YggdrasilNetworkExtension.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.security.application-groups + + group.eu.neilalexander.yggdrasil + + +