Prepare for rewrite: Remove UI and model code

This commit is contained in:
Roopesh Chander 2018-10-23 23:22:14 +05:30
parent 0b839629d7
commit 8df3d72615
70 changed files with 0 additions and 4379 deletions

View File

@ -1,57 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
import os.log
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var appCoordinator: AppCoordinator!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
appCoordinator = AppCoordinator(window: self.window!)
appCoordinator.start()
appCoordinator.checkAndCleanConfigs()
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
defer {
do {
try FileManager.default.removeItem(at: url)
} catch {
os_log("Failed to remove item from Inbox: %{public}@", log: Log.general, type: .error, url.absoluteString)
}
}
if url.pathExtension == "conf" {
do {
try appCoordinator.importConfig(config: url)
} catch {
os_log("Unable to import config: %{public}@", log: Log.general, type: .error, url.absoluteString)
return false
}
return true
} else if url.pathExtension == "zip" {
do {
try appCoordinator.importConfigs(configZip: url)
} catch {
os_log("Unable to import config: %{public}@", log: Log.general, type: .error, url.absoluteString)
return false
}
return true
}
return false
}
func applicationWillEnterForeground(_ application: UIApplication) {
appCoordinator.checkAndCleanConfigs()
}
}

View File

@ -1,116 +0,0 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon_20pt@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon_20pt@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon_29pt@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon_29pt@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon_40pt@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon_40pt@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon_60pt@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon_60pt@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon_20pt.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon_20pt@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon_29pt.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon_29pt@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon_40pt.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon_40pt@2x-1.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon_76pt.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon_76pt@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "icon_83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -1,15 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "Arrow.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -1,15 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "Dots.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_action_delete.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_action_delete@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_action_delete@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

View File

@ -1,15 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "wireguard.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="fHK-5V-Wmb">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="KXE-tg-GCt">
<objects>
<navigationController id="fHK-5V-Wmb" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="kSJ-B6-Sbs">
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
<color key="barTintColor" red="0.53333333329999999" green="0.090196078430000007" blue="0.1019607843" alpha="1" colorSpace="calibratedRGB"/>
</navigationBar>
<connections>
<segue destination="ZbP-GT-mQn" kind="relationship" relationship="rootViewController" id="ejS-gV-hxU"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="bEx-a1-E4B" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="150" y="313"/>
</scene>
<!--View Controller-->
<scene sceneID="QPe-xp-OsR">
<objects>
<viewController id="ZbP-GT-mQn" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="LhN-TB-3GI">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="qsB-da-p6j"/>
</view>
<navigationItem key="navigationItem" id="Qbp-DQ-urz"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Sdb-mk-Xj1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="910" y="313"/>
</scene>
</scenes>
</document>

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
extension AppCoordinator: QRScanViewControllerDelegate {
func didSave(tunnel: Tunnel, qrScanViewController: QRScanViewController) {
qrScanViewController.navigationController?.popViewController(animated: true)
showTunnelInfoViewController(tunnel: tunnel, context: tunnel.managedObjectContext!)
saveTunnel(tunnel)
}
func didCancel(qrScanViewController: QRScanViewController) {
qrScanViewController.navigationController?.popViewController(animated: true)
}
}

View File

@ -1,18 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
import PromiseKit
import NetworkExtension
enum GoVersionCoordinatorError: Error {
case noEnabledSession
case noResponse
}
extension AppCoordinator: SettingsTableViewControllerDelegate {
func exportTunnels(settingsTableViewController: SettingsTableViewController, sourceView: UIView) {
self.exportConfigs(sourceView: sourceView)
}
}

View File

@ -1,11 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
extension AppCoordinator: TunnelConfigurationTableViewControllerDelegate {
func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController) {
saveTunnel(tunnel)
}
}

View File

@ -1,32 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import NetworkExtension
extension AppCoordinator: TunnelInfoTableViewControllerDelegate {
func connect(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) {
connect(tunnel: tunnel)
}
func disconnect(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) {
disconnect(tunnel: tunnel)
}
func status(for tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) -> NEVPNStatus {
let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession
return session?.status ?? .invalid
}
func configure(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) {
let editContext = persistentContainer.newBackgroundContext()
var backgroundTunnel: Tunnel?
editContext.performAndWait {
backgroundTunnel = editContext.object(with: tunnel.objectID) as? Tunnel
}
showTunnelConfigurationViewController(tunnel: backgroundTunnel, context: editContext)
}
}

View File

@ -1,114 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
import NetworkExtension
import os.log
import MobileCoreServices
import ZIPFoundation
import PromiseKit
extension AppCoordinator: TunnelsTableViewControllerDelegate {
func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus {
let session = self.providerManager(for: tunnel)?.connection as? NETunnelProviderSession
return session?.status ?? .invalid
}
func addProvider(tunnelsTableViewController: TunnelsTableViewController) {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Create from file or archive", comment: ""), style: .default) { [unowned self] _ in
self.addProviderFromFile()
})
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Create from QR code", comment: ""), style: .default) { [unowned self] _ in
self.addProviderWithQRScan()
})
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Create from scratch", comment: ""), style: .default) { [unowned self] _ in
self.addProviderManually()
})
actionSheet.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel))
tunnelsTableViewController.present(actionSheet, animated: true, completion: nil)
}
func addProviderFromFile() {
let documentPickerController = UIDocumentPickerViewController(documentTypes: [String(kUTTypeZipArchive), "com.wireguard.config.quick"], in: .import)
documentPickerController.delegate = documentPickerDelegateObject
tunnelsTableViewController.present(documentPickerController, animated: true, completion: nil)
}
func addProviderManually() {
let addContext = persistentContainer.newBackgroundContext()
showTunnelConfigurationViewController(tunnel: nil, context: addContext)
}
func addProviderWithQRScan() {
let addContext = persistentContainer.newBackgroundContext()
let qrScanViewController = storyboard.instantiateViewController(type: QRScanViewController.self)
qrScanViewController.configure(context: addContext, delegate: self)
self.navigationController.pushViewController(qrScanViewController, animated: true)
}
func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) {
connect(tunnel: tunnel)
}
func disconnect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) {
disconnect(tunnel: tunnel)
}
func info(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) {
showTunnelInfoViewController(tunnel: tunnel, context: self.persistentContainer.viewContext)
}
func delete(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) {
if let moc = tunnel.managedObjectContext {
moc.perform {
moc.delete(tunnel)
moc.saveContextToStore()
}
let manager = providerManager(for: tunnel)
manager?.removeFromPreferences { (error) in
if let error = error {
os_log("error removing preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription)
return
}
self.providerManagers?.removeAll { $0 == manager }
os_log("removed preferences", log: Log.general, type: .info)
}
}
}
func saveTunnel(_ tunnel: Tunnel) {
let manager = providerManager(for: tunnel) ?? NETunnelProviderManager()
manager.localizedDescription = tunnel.title
let protocolConfiguration = NETunnelProviderProtocol()
protocolConfiguration.providerBundleIdentifier = VPNBUNDLE
protocolConfiguration.serverAddress = (tunnel.peers?.array as? [Peer])?.compactMap { $0.endpoint}.joined(separator: ", ")
protocolConfiguration.providerConfiguration = tunnel.generateProviderConfiguration()
manager.protocolConfiguration = protocolConfiguration
let connectRule = NEOnDemandRuleConnect()
connectRule.interfaceTypeMatch = .any
manager.onDemandRules = [connectRule]
manager.saveToPreferences { (error) in
if let error = error {
os_log("error saving preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription)
return
}
os_log("saved preferences", log: Log.general, type: .info)
}
_ = refreshProviderManagers().then { () -> Promise<Void> in
self.navigationController.popViewController(animated: true)
return Promise.value(())
}
}
}

View File

@ -1,468 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import NetworkExtension
import os.log
import ZIPFoundation
import PromiseKit
import CoreData
import BNRCoreDataStack
import MobileCoreServices
enum AppCoordinatorError: Error {
case configImportError(msg: String)
}
extension UINavigationController: Identifyable {}
let APPGROUP = "group.com.wireguard.ios"
let VPNBUNDLE = "com.wireguard.ios.network-extension"
class AppCoordinator: RootViewCoordinator { // swiftlint:disable:this type_body_length
let persistentContainer = NSPersistentContainer(name: "WireGuard")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var providerManagers: [NETunnelProviderManager]?
let documentPickerDelegateObject: AppDocumentPickerDelegate
// MARK: - Properties
var childCoordinators: [Coordinator] = []
var rootViewController: UIViewController {
return self.tunnelsTableViewController
}
var tunnelsTableViewController: TunnelsTableViewController!
/// Window to manage
let window: UIWindow
let navigationController: UINavigationController = {
let navController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(type: UINavigationController.self)
return navController
}()
// MARK: - Init
public init(window: UIWindow) {
self.window = window
self.window.rootViewController = self.navigationController
self.window.makeKeyAndVisible()
documentPickerDelegateObject = AppDocumentPickerDelegate()
documentPickerDelegateObject.appCoordinator = self
NotificationCenter.default.addObserver(self,
selector: #selector(VPNStatusDidChange(notification:)),
name: .NEVPNStatusDidChange,
object: nil)
}
// MARK: - Functions
/// Starts the coordinator
public func start() {
_ = refreshProviderManagers().then { () -> Promise<Void> in
self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
self.persistentContainer.loadPersistentStores { [weak self] (_, error) in
if let error = error {
os_log("Unable to load Persistent Store: %{public}@", log: Log.general, type: .error, error.localizedDescription)
} else {
DispatchQueue.main.async {
//start
if let tunnelsTableViewController = self?.storyboard.instantiateViewController(type: TunnelsTableViewController.self) {
self?.tunnelsTableViewController = tunnelsTableViewController
self?.tunnelsTableViewController.viewContext = self?.persistentContainer.viewContext
self?.tunnelsTableViewController.delegate = self
self?.navigationController.viewControllers = [tunnelsTableViewController]
}
}
}
}
return Promise.value(())
}
}
func refreshProviderManagers() -> Promise<Void> {
return Promise { (resolver) in
NETunnelProviderManager.loadAllFromPreferences { [weak self] (managers, error) in
if let error = error {
os_log("Unable to load provider managers: %{public}@", log: Log.general, type: .error, error.localizedDescription)
}
self?.providerManagers = managers
resolver.fulfill(())
}
}
}
func importConfig(config: URL) throws {
do {
try importConfig(configString: try String(contentsOf: config), title: config.deletingPathExtension().lastPathComponent)
} catch {
throw AppCoordinatorError.configImportError(msg: "Failed")
}
}
func importConfig(configString: String, title: String) throws {
do {
let addContext = persistentContainer.newBackgroundContext()
let tunnel = try Tunnel.fromConfig(configString, context: addContext)
tunnel.title = title
addContext.saveContext()
self.saveTunnel(tunnel)
} catch {
throw AppCoordinatorError.configImportError(msg: "Failed")
}
}
func importConfigs(configZip: URL) throws {
if let archive = Archive(url: configZip, accessMode: .read) {
for entry in archive {
var entryData = Data(capacity: 0)
_ = try archive.extract(entry) { (data) in
entryData.append(data)
}
if let config = String(data: entryData, encoding: .utf8) {
try importConfig(configString: config, title: entry.path)
}
}
}
}
func checkAndCleanConfigs() {
_ = refreshProviderManagers().then { () -> Promise<Void> in
guard let providerManagers = self.providerManagers else {
return Promise.value(())
}
let tunnels = try Tunnel.allInContext(self.persistentContainer.viewContext)
let tunnelIdentifiers = tunnels.compactMap {$0.tunnelIdentifier}
let unknownManagers = providerManagers.filter {
guard let prot = $0.protocolConfiguration as? NETunnelProviderProtocol else {
return false
}
guard let candidateTunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else {
return false
}
return !tunnelIdentifiers.contains(candidateTunnelIdentifier)
}
let deletionPromises = unknownManagers.map({ (manager) -> Promise<NETunnelProviderManager> in
return Promise(resolver: { resolver in
return manager.removeFromPreferences(completionHandler: { (error) in
if let error = error {
resolver.reject(error)
} else {
resolver.fulfill(manager)
}
})
})
})
return when(resolved: deletionPromises).asVoid()
}
}
// swiftlint:disable next function_body_length
func exportConfigs(sourceView: UIView) {
guard let path = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let saveFileURL = path.appendingPathComponent("wireguard-export.zip")
do {
try FileManager.default.removeItem(at: saveFileURL)
} catch {
os_log("Failed to delete file: %{public}@ : %{public}@", log: Log.general, type: .error, saveFileURL.absoluteString, error.localizedDescription)
}
guard let archive = Archive(url: saveFileURL, accessMode: .create) else {
return
}
do {
var tunnelsByTitle = [String: [Tunnel]]()
let tunnels = try Tunnel.allInContext(persistentContainer.viewContext)
tunnels.forEach {
guard let title = $0.title ?? $0.tunnelIdentifier else {
// there is always a tunnelidentifier.
return
}
if let tunnels = tunnelsByTitle[title] {
tunnelsByTitle[title] = tunnels + [$0]
} else {
tunnelsByTitle[title] = [$0]
}
}
func addEntry(title: String, tunnel: Tunnel) throws {
let data = tunnel.export().data(using: .utf8)!
let byteCount: UInt32 = UInt32(data.count)
try archive.addEntry(with: "\(title).conf", type: .file, uncompressedSize: byteCount, provider: { (position, size) -> Data in
return data.subdata(in: position ..< size)
})
}
try tunnelsByTitle.keys.forEach {
if let tunnels = tunnelsByTitle[$0] {
if tunnels.count == 1 {
try addEntry(title: $0, tunnel: tunnels[0])
} else {
for (index, tunnel) in tunnels.enumerated() {
try addEntry(title: $0 + "-\(index + 1)", tunnel: tunnel)
}
}
}
}
} catch {
os_log("Failed to create archive file: %{public}@ : %{public}@", log: Log.general, type: .error, saveFileURL.absoluteString, error.localizedDescription)
return
}
let activityViewController = UIActivityViewController(
activityItems: [saveFileURL],
applicationActivities: nil)
if let popoverPresentationController = activityViewController.popoverPresentationController {
popoverPresentationController.sourceView = sourceView
}
navigationController.present(activityViewController, animated: true) {
}
}
func exportConfig(tunnel: Tunnel, barButtonItem: UIBarButtonItem) {
let exportString = tunnel.export()
guard let path = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let saveFileURL = path.appendingPathComponent("/\(tunnel.title ?? "wireguard").conf")
do {
try exportString.write(to: saveFileURL, atomically: true, encoding: .utf8)
} catch {
os_log("Failed to export tunnel to: %{public}@", log: Log.general, type: .error, saveFileURL.absoluteString)
return
}
let activityViewController = UIActivityViewController(
activityItems: [saveFileURL],
applicationActivities: nil)
if let popoverPresentationController = activityViewController.popoverPresentationController {
popoverPresentationController.barButtonItem = barButtonItem
}
self.navigationController.present(activityViewController, animated: true) {
}
}
// MARK: - NEVPNManager handling
@objc private func VPNStatusDidChange(notification: NSNotification) {
guard let session = notification.object as? NETunnelProviderSession else {
return
}
guard let prot = session.manager.protocolConfiguration as? NETunnelProviderProtocol else {
return
}
guard let changedTunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else {
return
}
providerManagers?.first(where: { (manager) -> Bool in
guard let prot = manager.protocolConfiguration as? NETunnelProviderProtocol else {
return false
}
guard let candidateTunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else {
return false
}
return changedTunnelIdentifier == candidateTunnelIdentifier
})?.loadFromPreferences(completionHandler: { [weak self] (_) in
self?.tunnelsTableViewController.updateStatus(for: changedTunnelIdentifier)
})
}
func showTunnelInfoViewController(tunnel: Tunnel, context: NSManagedObjectContext) {
let tunnelInfoViewController = storyboard.instantiateViewController(type: TunnelInfoTableViewController.self)
tunnelInfoViewController.configure(context: context, delegate: self, tunnel: tunnel)
self.navigationController.pushViewController(tunnelInfoViewController, animated: true)
}
func showTunnelConfigurationViewController(tunnel: Tunnel?, context: NSManagedObjectContext) {
let tunnelConfigurationViewController = storyboard.instantiateViewController(type: TunnelConfigurationTableViewController.self)
tunnelConfigurationViewController.configure(context: context, delegate: self, tunnel: tunnel)
self.navigationController.pushViewController(tunnelConfigurationViewController, animated: true)
}
func showSettings() {
let settingsTableViewController = storyboard.instantiateViewController(type: SettingsTableViewController.self)
settingsTableViewController.delegate = self
self.navigationController.pushViewController(settingsTableViewController, animated: true)
}
public func showError(_ error: Error) {
showAlert(title: NSLocalizedString("Error", comment: "Error alert title"), message: error.localizedDescription)
}
func connect(tunnel: Tunnel) {
_ = refreshProviderManagers().then { () -> Promise<Void> in
guard let manager = self.providerManager(for: tunnel) else {
return Promise.value(())
}
let block = {
switch manager.connection.status {
case .invalid, .disconnected:
os_log("connect tunnel: %{public}@", log: Log.general, type: .info, tunnel.description)
// Should the manager be enabled?
let manager = self.providerManager(for: tunnel)
manager?.isEnabled = true
manager?.saveToPreferences { (error) in
if let error = error {
os_log("error saving preferences: %{public}@", log: Log.general, type: .error, error.localizedDescription)
return
}
os_log("saved preferences", log: Log.general, type: .info)
let session = manager?.connection as! NETunnelProviderSession //swiftlint:disable:this force_cast
do {
try session.startTunnel()
} catch let error {
os_log("error starting tunnel: %{public}@", log: Log.general, type: .error, error.localizedDescription)
}
}
default:
break
}
}
if manager.connection.status == .invalid {
manager.loadFromPreferences { (_) in
block()
}
} else {
block()
}
return Promise.value(())
}
}
func disconnect(tunnel: Tunnel) {
_ = refreshProviderManagers().then { () -> Promise<Void> in
let manager = self.providerManager(for: tunnel)!
let block = {
switch manager.connection.status {
case .connected, .connecting:
let manager = self.providerManager(for: tunnel)
manager?.connection.stopVPNTunnel()
default:
break
}
}
if manager.connection.status == .invalid {
manager.loadFromPreferences { (_) in
block()
}
} else {
block()
}
return Promise.value(())
}
}
private func showAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "OK button"), style: .default))
self.navigationController.present(alert, animated: true)
}
func providerManager(for tunnel: Tunnel) -> NETunnelProviderManager? {
return self.providerManagers?.first {
guard let prot = $0.protocolConfiguration as? NETunnelProviderProtocol else {
return false
}
guard let tunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else {
return false
}
return tunnelIdentifier == tunnel.tunnelIdentifier
}
}
func extensionGoVersionInformation() -> Promise<String> {
return Promise(resolver: { (resolver) in
guard let session = self.providerManagers?.first(where: { $0.isEnabled })?.connection as? NETunnelProviderSession else {
resolver.reject(GoVersionCoordinatorError.noEnabledSession)
return
}
do {
try session.sendProviderMessage(ExtensionMessage.requestVersion.data, responseHandler: { (data) in
guard let data = data, let responseString = String(data: data, encoding: .utf8) else {
resolver.reject(GoVersionCoordinatorError.noResponse)
return
}
resolver.fulfill(responseString)
})
} catch {
resolver.reject(error)
}
})
}
}
class AppDocumentPickerDelegate: NSObject, UIDocumentPickerDelegate {
weak var appCoordinator: AppCoordinator?
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
if url.pathExtension == "conf" {
do {
try appCoordinator?.importConfig(config: url)
} catch {
os_log("Unable to import config: %{public}@", log: Log.general, type: .error, url.absoluteString)
}
} else if url.pathExtension == "zip" {
do {
try appCoordinator?.importConfigs(configZip: url)
} catch {
os_log("Unable to import config: %{public}@", log: Log.general, type: .error, url.absoluteString)
}
}
}
}
extension NEVPNStatus {
var statusDescription: String {
switch self {
case .connected:
return "Connected"
case .connecting:
return "Connecting"
case .disconnected:
return "Disconnected"
case .disconnecting:
return "Disconnecting"
case .invalid:
return "Invalid"
case .reasserting:
return "Reasserting"
}
}
}

View File

@ -1,30 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
/// The Coordinator protocol
public protocol Coordinator: class {
/// Starts the coordinator
func start()
/// The array containing any child Coordinators
var childCoordinators: [Coordinator] { get set }
}
public extension Coordinator {
/// Add a child coordinator to the parent
public func addChildCoordinator(_ childCoordinator: Coordinator) {
self.childCoordinators.append(childCoordinator)
}
/// Remove a child coordinator from the parent
public func removeChildCoordinator(_ childCoordinator: Coordinator) {
self.childCoordinators = self.childCoordinators.filter { $0 !== childCoordinator }
}
}

View File

@ -1,15 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import UIKit
public protocol RootViewControllerProvider: class {
// The coordinators 'rootViewController'. It helps to think of this as the view
// controller that can be used to dismiss the coordinator from the view hierarchy.
var rootViewController: UIViewController { get }
}
/// A Coordinator type that provides a root UIViewController
public typealias RootViewCoordinator = Coordinator & RootViewControllerProvider

View File

@ -1,177 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+
*
* Copyright (C) 2015-2018 WireGuard LLC. All Rights Reserved.
*
* Curve25519 ECDH functions, based on TweetNaCl but cleaned up.
*/
#include <stdint.h>
#include <string.h>
#include <CommonCrypto/CommonRandom.h>
#include "x25519.h"
typedef int64_t fe[16];
static inline void carry(fe o)
{
int i;
for (i = 0; i < 16; ++i) {
o[(i + 1) % 16] += (i == 15 ? 38 : 1) * (o[i] >> 16);
o[i] &= 0xffff;
}
}
static inline void cswap(fe p, fe q, int b)
{
int i;
int64_t t, c = ~(b - 1);
for (i = 0; i < 16; ++i) {
t = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
static inline void pack(uint8_t *o, const fe n)
{
int i, j, b;
fe m, t;
memcpy(t, n, sizeof(t));
carry(t);
carry(t);
carry(t);
for (j = 0; j < 2; ++j) {
m[0] = t[0] - 0xffed;
for (i = 1; i < 15; ++i) {
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
m[i - 1] &= 0xffff;
}
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
b = (m[15] >> 16) & 1;
m[14] &= 0xffff;
cswap(t, m, 1 - b);
}
for (i = 0; i < 16; ++i) {
o[2 * i] = t[i] & 0xff;
o[2 * i + 1] = t[i] >> 8;
}
}
static inline void unpack(fe o, const uint8_t *n)
{
int i;
for (i = 0; i < 16; ++i)
o[i] = n[2 * i] + ((int64_t)n[2 * i + 1] << 8);
o[15] &= 0x7fff;
}
static inline void add(fe o, const fe a, const fe b)
{
int i;
for (i = 0; i < 16; ++i)
o[i] = a[i] + b[i];
}
static inline void subtract(fe o, const fe a, const fe b)
{
int i;
for (i = 0; i < 16; ++i)
o[i] = a[i] - b[i];
}
static inline void multmod(fe o, const fe a, const fe b)
{
int i, j;
int64_t t[31] = { 0 };
for (i = 0; i < 16; ++i) {
for (j = 0; j < 16; ++j)
t[i + j] += a[i] * b[j];
}
for (i = 0; i < 15; ++i)
t[i] += 38 * t[i + 16];
memcpy(o, t, sizeof(fe));
carry(o);
carry(o);
}
static inline void invert(fe o, const fe i)
{
fe c;
int a;
memcpy(c, i, sizeof(c));
for (a = 253; a >= 0; --a) {
multmod(c, c, c);
if (a != 2 && a != 4)
multmod(c, c, i);
}
memcpy(o, c, sizeof(fe));
}
static void curve25519_shared_secret(uint8_t shared_secret[32], const uint8_t private_key[32], const uint8_t public_key[32])
{
static const fe a24 = { 0xdb41, 1 };
uint8_t z[32];
int64_t r;
int i;
fe a = { 1 }, b, c = { 0 }, d = { 1 }, e, f, x;
memcpy(z, private_key, sizeof(z));
z[31] = (z[31] & 127) | 64;
z[0] &= 248;
unpack(x, public_key);
memcpy(b, x, sizeof(b));
for (i = 254; i >= 0; --i) {
r = (z[i >> 3] >> (i & 7)) & 1;
cswap(a, b, (int)r);
cswap(c, d, (int)r);
add(e, a, c);
subtract(a, a, c);
add(c, b, d);
subtract(b, b, d);
multmod(d, e, e);
multmod(f, a, a);
multmod(a, c, a);
multmod(c, b, e);
add(e, a, c);
subtract(a, a, c);
multmod(b, a, a);
subtract(c, d, f);
multmod(a, c, a24);
add(a, a, d);
multmod(c, c, a);
multmod(a, d, f);
multmod(d, b, x);
multmod(b, e, e);
cswap(a, b, (int)r);
cswap(c, d, (int)r);
}
invert(c, c);
multmod(a, a, c);
pack(shared_secret, a);
}
void curve25519_derive_public_key(uint8_t public_key[32], const uint8_t private_key[32])
{
static const uint8_t basepoint[32] = { 9 };
curve25519_shared_secret(public_key, private_key, basepoint);
}
void curve25519_generate_private_key(uint8_t private_key[32])
{
CCRandomGenerateBytes(private_key, 32);
private_key[31] = (private_key[31] & 127) | 64;
private_key[0] &= 248;
}

View File

@ -1,7 +0,0 @@
#ifndef X25519_H
#define X25519_H
void curve25519_derive_public_key(unsigned char public_key[32], const unsigned char private_key[32]);
void curve25519_generate_private_key(unsigned char private_key[32]);
#endif

View File

@ -1,40 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
@IBDesignable
class CopyableLabel: UILabel {
override func awakeFromNib() {
super.awakeFromNib()
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
self.addGestureRecognizer(gestureRecognizer)
self.isUserInteractionEnabled = true
}
// MARK: - UIGestureRecognizer
@objc func handleTapGesture(_ recognizer: UIGestureRecognizer) {
guard recognizer.state == .recognized else { return }
if let recognizerView = recognizer.view,
let recognizerSuperView = recognizerView.superview, recognizerView.becomeFirstResponder() {
let menuController = UIMenuController.shared
menuController.setTargetRect(recognizerView.frame, in: recognizerSuperView)
menuController.setMenuVisible(true, animated: true)
}
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return (action == #selector(UIResponderStandardEditActions.copy(_:)))
}
override func copy(_ sender: Any?) {
UIPasteboard.general.string = text
}
}

View File

@ -1,20 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
extension String {
static func commaSeparatedStringFrom(elements: [String]) -> String {
return elements.joined(separator: ",")
}
func commaSeparatedToArray() -> [String] {
return components(separatedBy: .whitespaces)
.joined()
.split(separator: ",")
.map(String.init)
}
}

View File

@ -1,14 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
extension String {
func isBase64() -> Bool {
let base64Predicate = NSPredicate(format: "SELF MATCHES %@", "^[a-zA-Z0-9+/]{43}=$")
return base64Predicate.evaluate(with: self)
}
}

View File

@ -1,173 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>conf</string>
</array>
<key>CFBundleTypeIconFiles</key>
<array>
<string>icon_20pt</string>
<string>icon_20pt@3x</string>
<string>icon_60pt@3x</string>
</array>
<key>CFBundleTypeName</key>
<string>WireGuard configuration</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.wireguard.config.quick</string>
</array>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>zip</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>zip</string>
<key>CFBundleTypeIconFiles</key>
<array/>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/zip</string>
<string>application/x-zip</string>
<string>application/x-zip-compressed</string>
</array>
<key>CFBundleTypeName</key>
<string>WireGuard Config Zip</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>ZIP </string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>com.pkware.zip-archive</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>WireGuardGoVersion</key>
<string>unknown</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>auto-generated</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<false/>
<key>NSCameraUsageDescription</key>
<string>Camera is used to scan QR codes</string>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>WireGuard configuration</string>
<key>UTTypeIconFiles</key>
<array>
<string>icon_20pt@3x</string>
<string>icon_60pt@3x</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.wireguard.config.quick</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>conf</string>
</dict>
</dict>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>com.pkware.zip-archive</string>
</array>
<key>UTTypeDescription</key>
<string>WireGuard configuration</string>
<key>UTTypeIconFiles</key>
<array>
<string>icon_20pt@3x</string>
<string>icon_60pt@3x</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.wireguard.config.quick</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>conf</string>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>WireGuard configurations</string>
<key>UTTypeIconFiles</key>
<array>
<string>icon_20pt.png</string>
<string>icon_60pt@3x.png</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.wireguard.config.quick.confs</string>
<key>public.filename-extension</key>
<string>zip</string>
</dict>
</array>
</dict>
</plist>

View File

@ -1,9 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import os.log
struct Log {
static var general = OSLog(subsystem: "com.wireguard.ios", category: "general")
}

View File

@ -1,40 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
struct Attribute {
enum Key: String, CaseIterable {
case address = "Address"
case allowedIPs = "AllowedIPs"
case dns = "DNS"
case endpoint = "Endpoint"
case listenPort = "ListenPort"
case mtu = "MTU"
case persistentKeepalive = "PersistentKeepalive"
case presharedKey = "PresharedKey"
case privateKey = "PrivateKey"
case publicKey = "PublicKey"
}
private static let separatorPattern = (try? NSRegularExpression(pattern: "\\s|=", options: []))!
let line: String
let key: Key
let stringValue: String
var arrayValue: [String] {
return stringValue.commaSeparatedToArray()
}
static func match(line: String) -> Attribute? {
guard let equalsIndex = line.firstIndex(of: "=") else { return nil }
let keyString = line[..<equalsIndex].trimmingCharacters(in: .whitespaces)
let value = line[line.index(equalsIndex, offsetBy: 1)...].trimmingCharacters(in: .whitespaces)
guard let key = Key.allCases.first(where: { $0.rawValue.lowercased() == keyString.lowercased() }) else { return nil }
return Attribute(line: line, key: key, stringValue: value)
}
}

View File

@ -1,11 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import CoreData
@objc(Interface)
public class Interface: NSManagedObject {
}

View File

@ -1,25 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import CoreData
extension Interface {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Interface> {
return NSFetchRequest<Interface>(entityName: "Interface")
}
@NSManaged public var addresses: String?
@NSManaged public var listenPort: Int16
@NSManaged public var privateKey: String?
@NSManaged public var mtu: Int32
@NSManaged public var dns: String?
@NSManaged public var tunnel: Tunnel?
}
// MARK: Generated accessors for adresses
extension Interface {
}

View File

@ -1,100 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
extension Interface {
var publicKey: String? {
if let privateKeyString = privateKey, let privateKey = Data(base64Encoded: privateKeyString) {
var publicKey = Data(count: 32)
privateKey.withUnsafeBytes({ (privateKeyBytes) -> Void in
publicKey.withUnsafeMutableBytes({ (mutableBytes) -> Void in
curve25519_derive_public_key(mutableBytes, privateKeyBytes)
})
})
return publicKey.base64EncodedString()
} else {
return nil
}
}
func validate() throws {
guard let privateKey = privateKey, !privateKey.isEmpty else {
throw InterfaceValidationError.emptyPrivateKey
}
guard privateKey.isBase64() else {
throw InterfaceValidationError.invalidPrivateKey
}
try addresses?.commaSeparatedToArray().forEach { address in
do {
try _ = CIDRAddress(stringRepresentation: address)
} catch {
throw InterfaceValidationError.invalidAddress(cause: error)
}
}
try dns?.commaSeparatedToArray().forEach { address in
do {
try _ = Endpoint(endpointString: address, needsPort: false)
} catch {
throw InterfaceValidationError.invalidDNSServer(cause: error)
}
}
}
func parse(attribute: Attribute) throws {
switch attribute.key {
case .address:
addresses = attribute.stringValue
case .dns:
dns = attribute.stringValue
case .listenPort:
if let port = Int16(attribute.stringValue) {
listenPort = port
}
case .mtu:
if let mtu = Int32(attribute.stringValue) {
self.mtu = mtu
}
case .privateKey:
privateKey = attribute.stringValue
default:
throw TunnelParseError.invalidLine(attribute.line)
}
}
func export() -> String {
var exportString = "[Interface]\n"
if let privateKey = privateKey {
exportString.append("PrivateKey=\(privateKey)\n")
}
if let addresses = addresses {
exportString.append("Address=\(addresses)\n")
}
if let dns = dns {
exportString.append("DNS=\(dns)\n")
}
if mtu > 0 {
exportString.append("MTU=\(mtu)\n")
}
if listenPort > 0 {
exportString.append("ListenPort=\(listenPort)\n")
}
exportString.append("\n")
return exportString
}
}
enum InterfaceValidationError: Error {
case emptyPrivateKey
case invalidPrivateKey
case invalidAddress(cause: Error)
case invalidDNSServer(cause: Error)
}

View File

@ -1,11 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import CoreData
@objc(Peer)
public class Peer: NSManagedObject {
}

View File

@ -1,21 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import CoreData
extension Peer {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Peer> {
return NSFetchRequest<Peer>(entityName: "Peer")
}
@NSManaged public var publicKey: String?
@NSManaged public var presharedKey: String?
@NSManaged public var allowedIPs: String?
@NSManaged public var endpoint: String?
@NSManaged public var persistentKeepalive: Int32
@NSManaged public var tunnel: Tunnel?
}

View File

@ -1,94 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
extension Peer {
func validate() throws {
guard let publicKey = publicKey, !publicKey.isEmpty else {
throw PeerValidationError.emptyPublicKey
}
guard publicKey.isBase64() else {
throw PeerValidationError.invalidPublicKey
}
guard let allowedIPs = allowedIPs, !allowedIPs.isEmpty else {
throw PeerValidationError.nilAllowedIps
}
try allowedIPs.commaSeparatedToArray().forEach { address in
do {
try _ = CIDRAddress(stringRepresentation: address)
} catch {
throw PeerValidationError.invalidAllowedIPs(cause: error)
}
}
if let endpoint = endpoint {
do {
try _ = Endpoint(endpointString: endpoint)
} catch {
throw PeerValidationError.invalidEndpoint(cause: error)
}
}
guard persistentKeepalive >= 0, persistentKeepalive <= 65535 else {
throw PeerValidationError.invalidPersistedKeepAlive
}
}
func parse(attribute: Attribute) throws {
switch attribute.key {
case .allowedIPs:
allowedIPs = attribute.stringValue
case .endpoint:
endpoint = attribute.stringValue
case .persistentKeepalive:
if let keepAlive = Int32(attribute.stringValue) {
persistentKeepalive = keepAlive
}
case .presharedKey:
presharedKey = attribute.stringValue
case .publicKey:
publicKey = attribute.stringValue
default:
throw TunnelParseError.invalidLine(attribute.line)
}
}
func export() -> String {
var exportString = "[Peer]\n"
if let publicKey = publicKey {
exportString.append("PublicKey=\(publicKey)\n")
}
if let presharedKey = presharedKey {
exportString.append("PresharedKey=\(presharedKey)\n")
}
if let allowedIPs = allowedIPs {
exportString.append("AllowedIPs=\(allowedIPs)\n")
}
if let endpoint = endpoint {
exportString.append("Endpoint=\(endpoint)\n")
}
if persistentKeepalive > 0 {
exportString.append("PersistentKeepalive=\(persistentKeepalive)\n")
}
exportString.append("\n")
return exportString
}
}
enum PeerValidationError: Error {
case emptyPublicKey
case invalidPublicKey
case nilAllowedIps
case invalidAllowedIPs(cause: Error)
case invalidEndpoint(cause: Error)
case invalidPersistedKeepAlive
}

View File

@ -1,11 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import CoreData
@objc(Tunnel)
public class Tunnel: NSManagedObject {
}

View File

@ -1,54 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import CoreData
extension Tunnel {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Tunnel> {
return NSFetchRequest<Tunnel>(entityName: "Tunnel")
}
@NSManaged public var tunnelIdentifier: String?
@NSManaged public var title: String?
@NSManaged public var interface: Interface?
@NSManaged public var peers: NSOrderedSet?
}
// MARK: Generated accessors for peers
extension Tunnel {
@objc(insertObject:inPeersAtIndex:)
@NSManaged public func insertIntoPeers(_ value: Peer, at idx: Int)
@objc(removeObjectFromPeersAtIndex:)
@NSManaged public func removeFromPeers(at idx: Int)
@objc(insertPeers:atIndexes:)
@NSManaged public func insertIntoPeers(_ values: [Peer], at indexes: NSIndexSet)
@objc(removePeersAtIndexes:)
@NSManaged public func removeFromPeers(at indexes: NSIndexSet)
@objc(replaceObjectInPeersAtIndex:withObject:)
@NSManaged public func replacePeers(at idx: Int, with value: Peer)
@objc(replacePeersAtIndexes:withPeers:)
@NSManaged public func replacePeers(at indexes: NSIndexSet, with values: [Peer])
@objc(addPeersObject:)
@NSManaged public func addToPeers(_ value: Peer)
@objc(removePeersObject:)
@NSManaged public func removeFromPeers(_ value: Peer)
@objc(addPeers:)
@NSManaged public func addToPeers(_ values: NSOrderedSet)
@objc(removePeers:)
@NSManaged public func removeFromPeers(_ values: NSOrderedSet)
}

View File

@ -1,212 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import CoreData
extension Tunnel {
public func generateProviderConfiguration() -> [String: Any] {
var providerConfiguration = [String: Any]()
providerConfiguration[PCKeys.title.rawValue] = self.title
providerConfiguration[PCKeys.tunnelIdentifier.rawValue] = self.tunnelIdentifier
providerConfiguration[PCKeys.endpoints.rawValue] = peers?.array.compactMap {($0 as? Peer)?.endpoint}.joined(separator: ", ")
providerConfiguration[PCKeys.dns.rawValue] = interface?.dns
providerConfiguration[PCKeys.addresses.rawValue] = interface?.addresses
if let mtu = interface?.mtu, mtu > 0 {
providerConfiguration[PCKeys.mtu.rawValue] = NSNumber(value: mtu)
}
var settingsString = "replace_peers=true\n"
if let interface = interface {
settingsString += generateInterfaceProviderConfiguration(interface)
}
if let peers = peers?.array as? [Peer] {
peers.forEach {
settingsString += generatePeerProviderConfiguration($0)
}
}
providerConfiguration["settings"] = settingsString
return providerConfiguration
}
private func generateInterfaceProviderConfiguration(_ interface: Interface) -> String {
var settingsString = ""
if let hexPrivateKey = base64KeyToHex(interface.privateKey) {
settingsString += "private_key=\(hexPrivateKey)\n"
}
if interface.listenPort > 0 {
settingsString += "listen_port=\(interface.listenPort)\n"
}
if interface.mtu > 0 {
settingsString += "mtu=\(interface.mtu)\n"
}
return settingsString
}
private func generatePeerProviderConfiguration(_ peer: Peer) -> String {
var settingsString = ""
if let hexPublicKey = base64KeyToHex(peer.publicKey) {
settingsString += "public_key=\(hexPublicKey)\n"
}
if let presharedKey = peer.presharedKey {
settingsString += "preshared_key=\(presharedKey)\n"
}
if let endpoint = peer.endpoint {
settingsString += "endpoint=\(endpoint)\n"
}
if peer.persistentKeepalive > 0 {
settingsString += "persistent_keepalive_interval=\(peer.persistentKeepalive)\n"
}
if let allowedIPs = peer.allowedIPs?.commaSeparatedToArray() {
allowedIPs.forEach {
settingsString += "allowed_ip=\($0.trimmingCharacters(in: .whitespaces))\n"
}
}
return settingsString
}
func validate() throws {
let nameRegex = "[a-zA-Z0-9_=+.-]{1,15}"
let nameTest = NSPredicate(format: "SELF MATCHES %@", nameRegex)
guard let title = title, nameTest.evaluate(with: title) else {
throw TunnelValidationError.invalidTitle
}
let fetchRequest: NSFetchRequest<Tunnel> = Tunnel.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "title == %@", title)
guard (try? managedObjectContext?.count(for: fetchRequest)) == 1 else {
throw TunnelValidationError.titleExists
}
guard let interface = interface else {
throw TunnelValidationError.nilInterface
}
try interface.validate()
guard let peers = peers else {
throw TunnelValidationError.nilPeers
}
try peers.forEach {
guard let peer = $0 as? Peer else {
throw TunnelValidationError.invalidPeer
}
try peer.validate()
}
}
static func fromConfig(_ text: String, context: NSManagedObjectContext) throws -> Tunnel {
let lines = text.split(separator: "\n")
var currentPeer: Peer?
var isInInterfaceSection = false
var tunnel: Tunnel!
context.performAndWait {
tunnel = Tunnel(context: context)
tunnel.interface = Interface(context: context)
}
tunnel.tunnelIdentifier = UUID().uuidString
for line in lines {
var trimmedLine: String
if let commentRange = line.range(of: "#") {
trimmedLine = String(line[..<commentRange.lowerBound])
} else {
trimmedLine = String(line)
}
trimmedLine = trimmedLine.trimmingCharacters(in: .whitespaces)
guard trimmedLine.count > 0 else { continue }
if "[interface]" == line.lowercased() {
currentPeer = nil
isInInterfaceSection = true
} else if "[peer]" == line.lowercased() {
context.performAndWait { currentPeer = Peer(context: context) }
tunnel.insertIntoPeers(currentPeer!, at: tunnel.peers?.count ?? 0)
isInInterfaceSection = false
} else if isInInterfaceSection, let attribute = Attribute.match(line: String(line)) {
try tunnel.interface!.parse(attribute: attribute)
} else if let currentPeer = currentPeer, let attribute = Attribute.match(line: String(line)) {
try currentPeer.parse(attribute: attribute)
} else {
throw TunnelParseError.invalidLine(String(line))
}
}
if !isInInterfaceSection && currentPeer == nil {
throw TunnelParseError.noConfigInfo
}
return tunnel
}
func export() -> String {
var exportString = ""
if let interfaceExport = self.interface?.export() {
exportString.append(interfaceExport)
}
if let peers = peers?.array as? [Peer] {
peers.forEach {
exportString.append($0.export())
}
}
return exportString
}
}
private func base64KeyToHex(_ base64: String?) -> String? {
guard let base64 = base64 else {
return nil
}
guard base64.count == 44 else {
return nil
}
guard base64.last == "=" else {
return nil
}
guard let keyData = Data(base64Encoded: base64) else {
return nil
}
guard keyData.count == 32 else {
return nil
}
let hexKey = keyData.reduce("") {$0 + String(format: "%02x", $1)}
return hexKey
}
enum TunnelValidationError: Error {
case invalidTitle
case titleExists
case nilInterface
case nilPeers
case invalidPeer
}
enum TunnelParseError: Error {
case invalidLine(_ line: String)
case noConfigInfo
}

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14315.18" systemVersion="18A391" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Interface" representedClassName="Interface" syncable="YES">
<attribute name="addresses" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="dns" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="listenPort" optional="YES" attributeType="Integer 16" usesScalarValueType="YES" syncable="YES"/>
<attribute name="mtu" optional="YES" attributeType="Integer 32" usesScalarValueType="YES" syncable="YES"/>
<attribute name="privateKey" attributeType="String" syncable="YES"/>
<relationship name="tunnel" maxCount="1" deletionRule="Nullify" destinationEntity="Tunnel" inverseName="interface" inverseEntity="Tunnel" syncable="YES"/>
</entity>
<entity name="Peer" representedClassName="Peer" syncable="YES">
<attribute name="allowedIPs" attributeType="String" syncable="YES"/>
<attribute name="endpoint" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="persistentKeepalive" attributeType="Integer 32" minValueString="0" maxValueString="65535" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="presharedKey" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="publicKey" attributeType="String" syncable="YES"/>
<relationship name="tunnel" maxCount="1" deletionRule="Nullify" destinationEntity="Tunnel" inverseName="peers" inverseEntity="Tunnel" syncable="YES"/>
</entity>
<entity name="Tunnel" representedClassName="Tunnel" syncable="YES">
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="tunnelIdentifier" attributeType="String" syncable="YES"/>
<relationship name="interface" maxCount="1" deletionRule="Cascade" destinationEntity="Interface" inverseName="tunnel" inverseEntity="Interface" syncable="YES"/>
<relationship name="peers" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="Peer" inverseName="tunnel" inverseEntity="Peer" syncable="YES"/>
</entity>
<elements>
<element name="Interface" positionX="-54" positionY="-9" width="128" height="135"/>
<element name="Peer" positionX="-36" positionY="9" width="128" height="135"/>
<element name="Tunnel" positionX="160" positionY="192" width="128" height="105"/>
</elements>
</model>

View File

@ -1,23 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import UIKit
public protocol Identifyable: class {
static var identifier: String { get }
}
public extension Identifyable {
static var identifier: String {
return String(describing: Self.self)
}
}
extension UIStoryboard {
public func instantiateViewController<T: Identifyable>(type: T.Type) -> T where T: UIViewController {
return self.instantiateViewController(withIdentifier: type.identifier) as! T // swiftlint:disable:this force_cast
}
}

View File

@ -1,152 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import AVFoundation
import CoreData
import UIKit
protocol QRScanViewControllerDelegate: class {
func didSave(tunnel: Tunnel, qrScanViewController: QRScanViewController)
func didCancel(qrScanViewController: QRScanViewController)
}
class QRScanViewController: UIViewController {
private var viewContext: NSManagedObjectContext!
private weak var delegate: QRScanViewControllerDelegate?
var captureSession: AVCaptureSession? = AVCaptureSession()
let metadataOutput = AVCaptureMetadataOutput()
var previewLayer: AVCaptureVideoPreviewLayer!
func configure(context: NSManagedObjectContext, delegate: QRScanViewControllerDelegate? = nil) {
viewContext = context
self.delegate = delegate
}
override func viewDidLoad() {
super.viewDidLoad()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video),
let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice),
let captureSession = captureSession,
captureSession.canAddInput(videoInput),
captureSession.canAddOutput(metadataOutput) else {
scanDidEncounterError(title: "Scanning Not Supported", message: "This device does not have the ability to scan QR codes.")
return
}
captureSession.addInput(videoInput)
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: .main)
metadataOutput.metadataObjectTypes = [.qr]
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.insertSublayer(previewLayer, at: 0)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if captureSession?.isRunning == false {
captureSession?.startRunning()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if captureSession?.isRunning == true {
captureSession?.stopRunning()
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = previewLayer.connection {
let currentDevice: UIDevice = UIDevice.current
let orientation: UIDeviceOrientation = currentDevice.orientation
let previewLayerConnection: AVCaptureConnection = connection
if previewLayerConnection.isVideoOrientationSupported {
switch orientation {
case .portrait:
previewLayerConnection.videoOrientation = .portrait
case .landscapeRight:
previewLayerConnection.videoOrientation = .landscapeLeft
case .landscapeLeft:
previewLayerConnection.videoOrientation = .landscapeRight
case .portraitUpsideDown:
previewLayerConnection.videoOrientation = .portraitUpsideDown
default:
previewLayerConnection.videoOrientation = .portrait
}
}
}
previewLayer.frame = self.view.bounds
}
func scanDidComplete(withCode code: String) {
do {
let tunnel = try Tunnel.fromConfig(code, context: viewContext)
let alert = UIAlertController(title: NSLocalizedString("Enter a title for new tunnel", comment: ""), message: nil, preferredStyle: .alert)
alert.addTextField(configurationHandler: nil)
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: { [weak self] _ in
self?.delegate?.didCancel(qrScanViewController: self!)
}))
alert.addAction(UIAlertAction(title: NSLocalizedString("Save", comment: ""), style: .default, handler: { [weak self] _ in
do {
tunnel.title = alert.textFields?[0].text
try self?.viewContext.save()
self?.delegate?.didSave(tunnel: tunnel, qrScanViewController: self!)
} catch {
self?.scanDidEncounterError(title: "Invalid Code", message: "The scanned code is not a valid WireGuard config file.")
}
}))
self.present(alert, animated: true)
} catch {
scanDidEncounterError(title: "Invalid Code", message: "The scanned code is not a valid WireGuard config file.")
}
}
func scanDidEncounterError(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak self] _ in
self?.navigationController?.popViewController(animated: true)
}))
present(alertController, animated: true)
captureSession = nil
}
}
extension QRScanViewController: AVCaptureMetadataOutputObjectsDelegate {
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession?.stopRunning()
guard let metadataObject = metadataObjects.first,
let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject,
let stringValue = readableObject.stringValue else {
scanDidEncounterError(title: "Invalid Code", message: "The scanned code could not be read.")
return
}
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
scanDidComplete(withCode: stringValue)
}
}
extension QRScanViewController: Identifyable {}

View File

@ -1,77 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
import PromiseKit
enum GoVersionError: Error {
case noDelegate
}
protocol SettingsTableViewControllerDelegate: class {
func exportTunnels(settingsTableViewController: SettingsTableViewController, sourceView: UIView)
}
class SettingsTableViewController: UITableViewController {
weak var delegate: SettingsTableViewControllerDelegate?
@IBOutlet weak var versionInfoCell: UITableViewCell!
@IBOutlet weak var goVersionInfoCell: UITableViewCell!
@IBOutlet weak var exportCell: UITableViewCell!
@IBOutlet weak var versionInfoLabel: UILabel!
@IBOutlet weak var goVersionInfoLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
versionInfoLabel.text = versionInformation
goVersionInfoLabel.text = goVersionInformation
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
switch cell {
case versionInfoCell, goVersionInfoCell:
UIPasteboard.general.string = ["WireGuard for iOS:", versionInformation, "Go userspace backend:", goVersionInfoLabel.text ?? ""].joined(separator: "\n")
showCopyConfirmation()
case exportCell:
delegate?.exportTunnels(settingsTableViewController: self, sourceView: exportCell)
default:
()
}
tableView.deselectRow(at: indexPath, animated: true)
}
}
var versionInformation: String {
var versionElements: [String] = []
if let appBuildNumber = Bundle.main.infoDictionary!["CFBundleVersion"] as? String, let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String {
versionElements.append(appVersion)
versionElements.append("(\(appBuildNumber))")
}
return versionElements.joined(separator: " ")
}
var goVersionInformation: String {
return Bundle.main.infoDictionary!["WireGuardGoVersion"] as? String ?? "Unknown!!!"
}
private func showNotEnabledAlert() {
let notEnabledAlertController = UIAlertController(title: NSLocalizedString("Go version", comment: ""), message: NSLocalizedString("Enable a WireGuard config by connecting or by selecting one in the VPN section of the device Settings app.", comment: ""), preferredStyle: .alert)
notEnabledAlertController.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: "Generic OK button"), style: .default, handler: nil))
present(notEnabledAlertController, animated: true, completion: nil)
}
private func showCopyConfirmation() {
let confirmationAlertController = UIAlertController(title: NSLocalizedString("Copied version information", comment: ""), message: UIPasteboard.general.string, preferredStyle: .alert)
confirmationAlertController.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: "Generic OK button"), style: .default, handler: nil))
present(confirmationAlertController, animated: true, completion: nil)
}
}
extension SettingsTableViewController: Identifyable {}

View File

@ -1,300 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
import CoreData
import BNRCoreDataStack
import PromiseKit
protocol TunnelConfigurationTableViewControllerDelegate: class {
func didSave(tunnel: Tunnel, tunnelConfigurationTableViewController: TunnelConfigurationTableViewController)
func showSettings()
}
class TunnelConfigurationTableViewController: UITableViewController {
@IBOutlet weak var saveButton: UIBarButtonItem!
private var viewContext: NSManagedObjectContext!
private weak var delegate: TunnelConfigurationTableViewControllerDelegate?
private var tunnel: Tunnel!
override func viewDidLoad() {
super.viewDidLoad()
// Get rid of seperator lines in table.
tableView.tableFooterView = UIView(frame: CGRect.zero)
}
func configure(context: NSManagedObjectContext, delegate: TunnelConfigurationTableViewControllerDelegate? = nil, tunnel: Tunnel? = nil) {
viewContext = context
self.delegate = delegate
self.tunnel = tunnel ?? generateNewTunnelConfig()
}
private func generateNewTunnelConfig() -> Tunnel {
var tunnel: Tunnel! = nil
viewContext.performAndWait {
tunnel = Tunnel(context: viewContext)
tunnel.tunnelIdentifier = UUID().uuidString
let interface = Interface(context: viewContext)
tunnel.interface = interface
}
return tunnel
}
@IBAction func showSettings(_ sender: Any) {
delegate?.showSettings()
}
@IBAction func addPeer(_ sender: Any) {
if let moc = tunnel.managedObjectContext {
tableView.beginUpdates()
let insertedAt = IndexPath(row: tunnel.peers?.count ?? 0, section: 1)
tableView.insertRows(at: [insertedAt], with: .automatic)
let peer = Peer(context: moc)
tunnel.addToPeers(peer)
tableView.endUpdates()
tableView.scrollToRow(at: insertedAt, at: .middle, animated: true)
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 1:
return tunnel?.peers?.count ?? 1
default:
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(type: InterfaceTableViewCell.self, for: indexPath)
cell.model = tunnel.interface
cell.delegate = self
return cell
case 1:
let cell = tableView.dequeueReusableCell(type: PeerTableViewCell.self, for: indexPath)
if let peer = tunnel.peers?.object(at: indexPath.row) as? Peer {
cell.peer = peer
} else {
let peer = Peer(context: tunnel.managedObjectContext!)
tunnel.addToPeers(peer)
cell.peer = peer
}
cell.delegate = self
return cell
default:
let cell = tableView.dequeueReusableCell(type: AddPeerTableViewCell.self, for: indexPath)
cell.tunnel = tunnel
return cell
}
}
@IBAction func saveTunnelConfiguration(_ sender: Any) {
Promise<Void>(resolver: { (seal) in
do {
try tunnel.validate()
} catch {
seal.reject(error)
return
}
viewContext.perform({
self.viewContext.saveContext({ (result) in
switch result {
case .success:
seal.fulfill(())
case .failure(let error):
seal.reject(error)
}
})
})
}).then { () -> Promise<Void> in
self.delegate?.didSave(tunnel: self.tunnel, tunnelConfigurationTableViewController: self)
return Promise.value(())
}.catch { error in
let alert = UIAlertController(title: "Error", message: "\(error)", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
extension TunnelConfigurationTableViewController: PeerTableViewCellDelegate {
func delete(peer: Peer) {
if let moc = tunnel.managedObjectContext {
tableView.beginUpdates()
let deletedAt = IndexPath(row: tunnel.peers?.index(of: peer) ?? 0, section: 1)
tableView.deleteRows(at: [deletedAt], with: .automatic)
tunnel.removeFromPeers(peer)
moc.delete(peer)
tableView.endUpdates()
}
}
}
extension TunnelConfigurationTableViewController: InterfaceTableViewCellDelegate {
func generateKeys() {
if let moc = tunnel.managedObjectContext {
moc.perform {
var privateKey = Data(count: 32)
privateKey.withUnsafeMutableBytes { (mutableBytes) -> Void in
curve25519_generate_private_key(mutableBytes)
}
self.tunnel.interface?.privateKey = privateKey.base64EncodedString()
}
}
self.tableView.reloadData()
}
}
protocol InterfaceTableViewCellDelegate: class {
func generateKeys()
}
class InterfaceTableViewCell: UITableViewCell {
var model: Interface! {
didSet {
nameField.text = model.tunnel?.title
addressesField.text = model.addresses
privateKeyField.text = model.privateKey
publicKeyField.text = model.publicKey
listenPortField.text = model.listenPort > 0 ? String(model.listenPort) : nil
dnsField.text = model.dns
mtuField.text = model.mtu > 0 ? String(model.mtu) : nil
}
}
weak var delegate: InterfaceTableViewCellDelegate?
@IBOutlet weak var nameField: UITextField!
@IBOutlet weak var addressesField: UITextField!
@IBOutlet weak var privateKeyField: UITextField!
@IBOutlet weak var publicKeyField: CopyableLabel!
@IBOutlet weak var listenPortField: UITextField!
@IBOutlet weak var dnsField: UITextField!
@IBOutlet weak var mtuField: UITextField!
@IBAction func generateTapped(_ sender: Any) {
delegate?.generateKeys()
}
}
extension InterfaceTableViewCell: UITextFieldDelegate {
@IBAction
func textfieldDidChange(_ sender: UITextField) {
let string = sender.text
if sender == nameField {
model.tunnel?.title = string
} else if sender == privateKeyField {
model.privateKey = string
publicKeyField.text = model.publicKey
} else if sender == addressesField {
model.addresses = string
} else if sender == listenPortField {
if let string = string, let port = Int16(string) {
model.listenPort = port
} else {
model.listenPort = 0
}
} else if sender == dnsField {
model.dns = string
} else if sender == mtuField {
if let string = string, let mtu = Int32(string) {
model.mtu = mtu
} else {
model.mtu = 0
}
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == addressesField {
if let addresses = model.addresses?.commaSeparatedToArray() {
textField.text = addresses.compactMap { try? CIDRAddress(stringRepresentation: $0 ) }.compactMap { $0?.stringRepresentation }.joined(separator: ", ")
}
}
}
}
protocol PeerTableViewCellDelegate: class {
func delete(peer: Peer)
}
class PeerTableViewCell: UITableViewCell {
var peer: Peer! {
didSet {
publicKeyField.text = peer.publicKey
preSharedKeyField.text = peer.presharedKey
allowedIpsField.text = peer.allowedIPs
endpointField.text = peer.endpoint
persistentKeepaliveField.text = peer.persistentKeepalive > 0 ? String(peer.persistentKeepalive) : nil
}
}
weak var delegate: PeerTableViewCellDelegate?
@IBOutlet weak var publicKeyField: UITextField!
@IBOutlet weak var preSharedKeyField: UITextField!
@IBOutlet weak var allowedIpsField: UITextField!
@IBOutlet weak var endpointField: UITextField!
@IBOutlet weak var persistentKeepaliveField: UITextField!
@IBAction func deletePeer(_ sender: Any) {
delegate?.delete(peer: peer)
}
}
extension PeerTableViewCell: UITextFieldDelegate {
@IBAction
func textfieldDidChange(_ sender: UITextField) {
let string = sender.text
if sender == publicKeyField {
peer.publicKey = string
} else if sender == preSharedKeyField {
peer.presharedKey = string
} else if sender == allowedIpsField {
peer.allowedIPs = string
} else if sender == endpointField {
peer.endpoint = string
} else if sender == persistentKeepaliveField {
if let string = string, let persistentKeepalive = Int32(string) {
peer.persistentKeepalive = persistentKeepalive
} else {
peer.persistentKeepalive = 0
}
}
}
}
class AddPeerTableViewCell: UITableViewCell {
var tunnel: Tunnel!
@IBAction func addPeer(_ sender: Any) {
if let moc = tunnel.managedObjectContext {
tunnel.addToPeers(Peer(context: moc))
}
}
}
extension TunnelConfigurationTableViewController: Identifyable {}
extension InterfaceTableViewCell: Identifyable {}
extension PeerTableViewCell: Identifyable {}
extension AddPeerTableViewCell: Identifyable {}

View File

@ -1,194 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
import CoreData
import NetworkExtension
import BNRCoreDataStack
import PromiseKit
protocol TunnelInfoTableViewControllerDelegate: class {
func connect(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController)
func disconnect(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController)
func configure(tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController)
func showSettings()
func status(for tunnel: Tunnel, tunnelInfoTableViewController: TunnelInfoTableViewController) -> NEVPNStatus
}
class TunnelInfoTableViewController: UITableViewController {
@IBOutlet weak var editButton: UIBarButtonItem!
private var viewContext: NSManagedObjectContext!
private weak var delegate: TunnelInfoTableViewControllerDelegate?
private var tunnel: Tunnel!
func configure(context: NSManagedObjectContext, delegate: TunnelInfoTableViewControllerDelegate? = nil, tunnel: Tunnel) {
viewContext = context
self.delegate = delegate
self.tunnel = tunnel
}
override func viewDidLoad() {
super.viewDidLoad()
// Get rid of seperator lines in table.
tableView.tableFooterView = UIView(frame: CGRect.zero)
NotificationCenter.default.addObserver(self,
selector: #selector(VPNStatusDidChange(notification:)),
name: .NEVPNStatusDidChange,
object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super .viewWillAppear(animated)
self.tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 1:
return tunnel?.peers?.count ?? 0
default:
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
let cell = tableView.dequeueReusableCell(type: InterfaceInfoTableViewCell.self, for: indexPath)
cell.delegate = self
cell.configure(model: tunnel.interface, status: delegate?.status(for: tunnel, tunnelInfoTableViewController: self) ?? .invalid)
return cell
default:
let cell = tableView.dequeueReusableCell(type: PeerInfoTableViewCell.self, for: indexPath)
if let peer = tunnel.peers?.object(at: indexPath.row) as? Peer {
cell.peer = peer
} else {
let peer = Peer(context: tunnel.managedObjectContext!)
tunnel.addToPeers(peer)
cell.peer = peer
}
return cell
}
}
@IBAction func showSettings(_ sender: Any) {
delegate?.showSettings()
}
@IBAction func editTunnelConfiguration(_ sender: Any) {
delegate?.configure(tunnel: self.tunnel, tunnelInfoTableViewController: self)
}
@objc private func VPNStatusDidChange(notification: NSNotification) {
guard let session = notification.object as? NETunnelProviderSession else {
return
}
guard let prot = session.manager.protocolConfiguration as? NETunnelProviderProtocol else {
return
}
guard let changedTunnelIdentifier = prot.providerConfiguration?[PCKeys.tunnelIdentifier.rawValue] as? String else {
return
}
guard tunnel.tunnelIdentifier == changedTunnelIdentifier else {
return
}
self.tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none)
}
}
extension TunnelInfoTableViewController: InterfaceInfoTableViewCellDelegate {
func connect(tunnelIdentifier: String) {
delegate?.connect(tunnel: tunnel, tunnelInfoTableViewController: self)
}
func disconnect(tunnelIdentifier: String) {
delegate?.disconnect(tunnel: tunnel, tunnelInfoTableViewController: self)
}
}
protocol InterfaceInfoTableViewCellDelegate: class {
func connect(tunnelIdentifier: String)
func disconnect(tunnelIdentifier: String)
}
class InterfaceInfoTableViewCell: UITableViewCell {
weak var delegate: InterfaceInfoTableViewCellDelegate?
private var model: Interface! {
didSet {
nameField.text = model.tunnel?.title
addressesField.text = model.addresses
publicKeyField.text = model.publicKey
}
}
func configure(model: Interface!, status: NEVPNStatus) {
self.model = model
if status == .connecting || status == .disconnecting || status == .reasserting {
activityIndicator.startAnimating()
tunnelSwitch.isHidden = true
} else {
activityIndicator.stopAnimating()
tunnelSwitch.isHidden = false
}
tunnelSwitch.isOn = status == .connected
tunnelSwitch.onTintColor = status == .invalid || status == .reasserting ? .gray : .green
tunnelSwitch.isEnabled = true
}
@IBAction func tunnelSwitchChanged(_ sender: Any) {
tunnelSwitch.isEnabled = false
guard let tunnelIdentifier = model.tunnel?.tunnelIdentifier else {
return
}
if tunnelSwitch.isOn {
delegate?.connect(tunnelIdentifier: tunnelIdentifier)
} else {
delegate?.disconnect(tunnelIdentifier: tunnelIdentifier)
}
}
@IBOutlet weak var nameField: UILabel!
@IBOutlet weak var addressesField: UILabel!
@IBOutlet weak var publicKeyField: CopyableLabel!
@IBOutlet weak var tunnelSwitch: UISwitch!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
}
class PeerInfoTableViewCell: UITableViewCell {
var peer: Peer! {
didSet {
publicKeyField.text = peer.publicKey
allowedIpsField.text = peer.allowedIPs
endpointField.text = peer.endpoint
}
}
@IBOutlet weak var publicKeyField: CopyableLabel!
@IBOutlet weak var allowedIpsField: UILabel!
@IBOutlet weak var endpointField: UILabel!
@IBOutlet weak var copiedStatusLabel: UILabel!
}
extension TunnelInfoTableViewController: Identifyable {}
extension InterfaceInfoTableViewCell: Identifyable {}
extension PeerInfoTableViewCell: Identifyable {}

View File

@ -1,308 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import UIKit
import os.log
import CoreData
import BNRCoreDataStack
import NetworkExtension
protocol TunnelsTableViewControllerDelegate: class {
func addProvider(tunnelsTableViewController: TunnelsTableViewController)
func connect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
func disconnect(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
func info(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
func delete(tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController)
func status(for tunnel: Tunnel, tunnelsTableViewController: TunnelsTableViewController) -> NEVPNStatus
func showSettings()
}
class TunnelsTableViewController: UITableViewController {
weak var delegate: TunnelsTableViewControllerDelegate?
var viewContext: NSManagedObjectContext!
@IBOutlet var settingsButton: UIBarButtonItem!
@IBOutlet var editButton: UIBarButtonItem!
@IBOutlet var doneButton: UIBarButtonItem!
private lazy var fetchedResultsController: FetchedResultsController<Tunnel> = {
let fetchRequest = NSFetchRequest<Tunnel>()
fetchRequest.entity = Tunnel.entity()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]
let frc = FetchedResultsController<Tunnel>(fetchRequest: fetchRequest,
managedObjectContext: viewContext)
frc.setDelegate(self.frcDelegate)
return frc
}()
public func updateStatus(for tunnelIdentifier: String) {
viewContext.perform {
do {
let tunnel = try Tunnel.findFirstInContext(self.viewContext, predicate: NSPredicate(format: "tunnelIdentifier == %@", tunnelIdentifier))
if let tunnel = tunnel {
if let indexPath = self.fetchedResultsController.indexPathForObject(tunnel) {
self.tableView.reloadRows(at: [indexPath], with: .none)
}
}
} catch {
os_log("Unable to load tunnel for tunnel identifier: %{public}@", log: Log.general, type: .error, error.localizedDescription)
}
}
}
private lazy var frcDelegate: TunnelFetchedResultsControllerDelegate = { // swiftlint:disable:this weak_delegate
return TunnelFetchedResultsControllerDelegate(tableView: self.tableView)
}()
override func viewDidLoad() {
super.viewDidLoad()
do {
try fetchedResultsController.performFetch()
} catch {
print("Failed to fetch objects: \(error)")
}
// Get rid of seperator lines in table.
tableView.tableFooterView = UIView(frame: CGRect.zero)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateBarButtons()
}
@IBAction func editTunnels(_ sender: Any) {
tableView.setEditing(!tableView.isEditing, animated: true)
updateBarButtons()
}
private func updateBarButtons() {
navigationController?.setToolbarHidden(tableView.isEditing, animated: true)
if tableView.isEditing {
self.navigationItem.setRightBarButtonItems([doneButton], animated: true)
} else {
self.navigationItem.setRightBarButtonItems([settingsButton, editButton], animated: true)
}
}
@IBAction func showSettings(_ sender: Any) {
delegate?.showSettings()
}
@IBAction func addProvider(_ sender: UIBarButtonItem) {
delegate?.addProvider(tunnelsTableViewController: self)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController.sections?[0].objects.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(type: TunnelTableViewCell.self, for: indexPath)
cell.delegate = self
guard let sections = fetchedResultsController.sections else {
fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil")
}
let section = sections[indexPath.section]
let tunnel = section.objects[indexPath.row]
cell.configure(tunnel: tunnel, status: delegate?.status(for: tunnel, tunnelsTableViewController: self) ?? .invalid)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let sections = fetchedResultsController.sections else {
fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil")
}
let section = sections[indexPath.section]
let tunnel = section.objects[indexPath.row]
delegate?.info(tunnel: tunnel, tunnelsTableViewController: self)
tableView.deselectRow(at: indexPath, animated: true)
}
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
guard let sections = fetchedResultsController.sections else {
fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil")
}
let section = sections[indexPath.section]
let tunnel = section.objects[indexPath.row]
delegate?.info(tunnel: tunnel, tunnelsTableViewController: self)
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
guard let sections = fetchedResultsController.sections else {
fatalError("FetchedResultsController \(fetchedResultsController) should have sections, but found nil")
}
let section = sections[indexPath.section]
let tunnel = section.objects[indexPath.row]
delegate?.delete(tunnel: tunnel, tunnelsTableViewController: self)
}
}
}
extension TunnelsTableViewController: TunnelTableViewCellDelegate {
func connect(tunnelIdentifier: String) {
let tunnel = try? Tunnel.findFirstInContext(self.viewContext, predicate: NSPredicate(format: "tunnelIdentifier == %@", tunnelIdentifier))
if let tunnel = tunnel {
self.delegate?.connect(tunnel: tunnel!, tunnelsTableViewController: self)
}
}
func disconnect(tunnelIdentifier: String) {
let tunnel = try? Tunnel.findFirstInContext(self.viewContext, predicate: NSPredicate(format: "tunnelIdentifier == %@", tunnelIdentifier))
if let tunnel = tunnel {
self.delegate?.disconnect(tunnel: tunnel!, tunnelsTableViewController: self)
}
}
}
extension TunnelsTableViewController: Identifyable {}
class TunnelFetchedResultsControllerDelegate: NSObject, FetchedResultsControllerDelegate {
private weak var tableView: UITableView?
private var arrowImage: UIImageView?
// MARK: - Lifecycle
init(tableView: UITableView) {
self.tableView = tableView
}
func fetchedResultsControllerDidPerformFetch(_ controller: FetchedResultsController<Tunnel>) {
tableView?.reloadData()
updateEmptyIndicator(controller)
}
func fetchedResultsControllerWillChangeContent(_ controller: FetchedResultsController<Tunnel>) {
tableView?.beginUpdates()
}
func fetchedResultsControllerDidChangeContent(_ controller: FetchedResultsController<Tunnel>) {
tableView?.endUpdates()
updateEmptyIndicator(controller)
}
func fetchedResultsController(_ controller: FetchedResultsController<Tunnel>, didChangeObject change: FetchedResultsObjectChange<Tunnel>) {
guard let tableView = tableView else { return }
switch change {
case let .insert(_, indexPath):
tableView.insertRows(at: [indexPath], with: .automatic)
case let .delete(_, indexPath):
tableView.deleteRows(at: [indexPath], with: .automatic)
case let .move(_, fromIndexPath, toIndexPath):
tableView.moveRow(at: fromIndexPath, to: toIndexPath)
case let .update(_, indexPath):
tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
func fetchedResultsController(_ controller: FetchedResultsController<Tunnel>, didChangeSection change: FetchedResultsSectionChange<Tunnel>) {
guard let tableView = tableView else { return }
switch change {
case let .insert(_, index):
tableView.insertSections(IndexSet(integer: index), with: .automatic)
case let .delete(_, index):
tableView.deleteSections(IndexSet(integer: index), with: .automatic)
}
}
private func updateEmptyIndicator(_ controller: FetchedResultsController<Tunnel>) {
guard let tableView = tableView else { return }
if controller.count > 0 {
tableView.backgroundView = nil
arrowImage = nil
} else {
if arrowImage == nil {
let imageView = UIImageView(image: UIImage(named: "Arrow"))
imageView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
imageView.frame = tableView.bounds
imageView.contentMode = .bottomRight
tableView.backgroundView = imageView
arrowImage = imageView
}
}
}
}
protocol TunnelTableViewCellDelegate: class {
func connect(tunnelIdentifier: String)
func disconnect(tunnelIdentifier: String)
}
class TunnelTableViewCell: UITableViewCell {
@IBOutlet weak var tunnelTitleLabel: UILabel!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var tunnelSwitch: UISwitch!
weak var delegate: TunnelTableViewCellDelegate?
private var tunnelIdentifier: String?
@IBAction func tunnelSwitchChanged(_ sender: Any) {
tunnelSwitch.isEnabled = false
guard let tunnelIdentifier = tunnelIdentifier else {
return
}
if tunnelSwitch.isOn {
delegate?.connect(tunnelIdentifier: tunnelIdentifier)
} else {
delegate?.disconnect(tunnelIdentifier: tunnelIdentifier)
}
}
func configure(tunnel: Tunnel, status: NEVPNStatus) {
self.tunnelTitleLabel?.text = tunnel.title
tunnelIdentifier = tunnel.tunnelIdentifier
if status == .connecting || status == .disconnecting || status == .reasserting {
activityIndicator.startAnimating()
tunnelSwitch.isHidden = true
} else {
activityIndicator.stopAnimating()
tunnelSwitch.isHidden = false
}
tunnelSwitch.isOn = status == .connected
tunnelSwitch.onTintColor = status == .invalid || status == .reasserting ? .gray : .green
tunnelSwitch.isEnabled = true
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tunnelSwitch.isHidden = editing
}
}
extension TunnelTableViewCell: Identifyable {}

View File

@ -1,32 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import Foundation
import UIKit
extension UITableView {
func register<T: Identifyable>(type: T.Type, prefix: String = "") where T: UITableViewCell {
register(type, forCellReuseIdentifier: prefix + type.identifier)
}
func dequeueReusableCell<T: Identifyable>(type: T.Type, for indexPath: IndexPath, prefix: String = "") -> T where T: UITableViewCell {
return dequeueReusableCell(withIdentifier: prefix + type.identifier, for: indexPath) as! T // swiftlint:disable:this force_cast
}
func registerNib<T: Identifyable>(type: T.Type, prefix: String = "") where T: UITableViewCell {
let nib = UINib(nibName: prefix + type.identifier, bundle: nil)
register(nib, forCellReuseIdentifier: prefix + type.identifier)
}
func registerNib<T: Identifyable>(type: T.Type, prefix: String = "") where T: UITableViewHeaderFooterView {
let nib = UINib(nibName: prefix + type.identifier, bundle: nil)
register(nib, forHeaderFooterViewReuseIdentifier: prefix + type.identifier)
}
func dequeueReusableHeaderFooterView<T: Identifyable>(type: T.Type, prefix: String = "") -> T where T: UITableViewHeaderFooterView {
return dequeueReusableHeaderFooterView(withIdentifier: prefix + type.identifier) as! T // swiftlint:disable:this force_cast
}
}

View File

@ -1,6 +0,0 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "Crypto/x25519.h"
#import "../wireguard-go-bridge/wireguard.h"

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider</string>
</array>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.wireguard.ios</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.wireguard.ios</string>
</array>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,189 +0,0 @@
//
// Copyright © 2018 WireGuard LLC. All rights reserved.
//
import XCTest
@testable import WireGuard
class ValidatorsTests: XCTestCase {
func testEndpoint() throws {
_ = try Endpoint(endpointString: "[2607:f938:3001:4000::aac]:12345")
_ = try Endpoint(endpointString: "192.168.0.1:12345")
}
func testEndpoint_invalidIP() throws {
func executeTest(endpointString: String, ipString: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertThrowsError(try Endpoint(endpointString: endpointString)) { (error) in
guard case EndpointValidationError.invalidIP(let value) = error else {
return XCTFail("Unexpected error: \(error)", file: file, line: line)
}
XCTAssertEqual(value, ipString, file: file, line: line)
}
}
executeTest(endpointString: "12345:12345", ipString: "12345")
executeTest(endpointString: ":12345", ipString: "")
}
func testEndpoint_invalidPort() throws {
func executeTest(endpointString: String, portString: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertThrowsError(try Endpoint(endpointString: endpointString)) { (error) in
guard case EndpointValidationError.invalidPort(let value) = error else {
return XCTFail("Unexpected error: \(error)", file: file, line: line)
}
XCTAssertEqual(value, portString, file: file, line: line)
}
}
executeTest(endpointString: ":", portString: "")
executeTest(endpointString: "[2607:f938:3001:4000::aac]:-12345", portString: "-12345")
executeTest(endpointString: "[2607:f938:3001:4000::aac]", portString: "aac]")
executeTest(endpointString: "[2607:f938:3001:4000::aac]:", portString: "")
executeTest(endpointString: "192.168.0.1:-12345", portString: "-12345")
executeTest(endpointString: "192.168.0.1:", portString: "")
}
func testEndpoint_noIpAndPort() throws {
func executeTest(endpointString: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertThrowsError(try Endpoint(endpointString: endpointString)) { (error) in
guard case EndpointValidationError.noIpAndPort(let value) = error else {
return XCTFail("Unexpected error: \(error)", file: file, line: line)
}
XCTAssertEqual(value, endpointString, file: file, line: line)
}
}
executeTest(endpointString: "192.168.0.1")
executeTest(endpointString: "12345")
}
func testCIDRAddress() throws {
_ = try CIDRAddress(stringRepresentation: "2607:f938:3001:4000::aac/24")
_ = try CIDRAddress(stringRepresentation: "192.168.0.1/24")
}
func testIPv4CIDRAddress() throws {
_ = try CIDRAddress(stringRepresentation: "192.168.0.1/24")
}
func testCIDRAddress_invalidIP() throws {
func executeTest(stringRepresentation: String, ipString: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertThrowsError(try CIDRAddress(stringRepresentation: stringRepresentation)) { (error) in
guard case CIDRAddressValidationError.invalidIP(let value) = error else {
return XCTFail("Unexpected error: \(error)", file: file, line: line)
}
XCTAssertEqual(value, ipString, file: file, line: line)
}
}
executeTest(stringRepresentation: "12345/12345", ipString: "12345")
executeTest(stringRepresentation: "/12345", ipString: "")
}
func testCIDRAddress_invalidSubnet() throws {
func executeTest(stringRepresentation: String, subnetString: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertThrowsError(try CIDRAddress(stringRepresentation: stringRepresentation)) { (error) in
guard case CIDRAddressValidationError.invalidSubnet(let value) = error else {
return XCTFail("Unexpected error: \(error)", file: file, line: line)
}
XCTAssertEqual(value, subnetString, file: file, line: line)
}
}
executeTest(stringRepresentation: "/", subnetString: "")
executeTest(stringRepresentation: "2607:f938:3001:4000::aac/a", subnetString: "a")
executeTest(stringRepresentation: "2607:f938:3001:4000:/aac", subnetString: "aac")
executeTest(stringRepresentation: "2607:f938:3001:4000::aac/", subnetString: "")
executeTest(stringRepresentation: "192.168.0.1/a", subnetString: "a")
executeTest(stringRepresentation: "192.168.0.1/", subnetString: "")
}
func testCIDRAddress_noIpAndSubnet() throws {
func executeTest(stringRepresentation: String, file: StaticString = #file, line: UInt = #line) {
XCTAssertThrowsError(try CIDRAddress(stringRepresentation: stringRepresentation)) { (error) in
guard case CIDRAddressValidationError.noIpAndSubnet(let value) = error else {
return XCTFail("Unexpected error: \(error)", file: file, line: line)
}
XCTAssertEqual(value, stringRepresentation, file: file, line: line)
}
}
executeTest(stringRepresentation: "192.168.0.1")
executeTest(stringRepresentation: "12345")
}
// swiftlint:disable next function_body_length
func testIPv4CIDRAddressSubnetConversion() throws {
// swiftlint:disable force_try
let cidrAddress1 = try! CIDRAddress(stringRepresentation: "128.0.0.0/1")!
XCTAssertEqual(cidrAddress1.ipAddress, cidrAddress1.subnetString)
let cidrAddress2 = try! CIDRAddress(stringRepresentation: "192.0.0.0/2")!
XCTAssertEqual(cidrAddress2.ipAddress, cidrAddress2.subnetString)
let cidrAddress3 = try! CIDRAddress(stringRepresentation: "224.0.0.0/3")!
XCTAssertEqual(cidrAddress3.ipAddress, cidrAddress3.subnetString)
let cidrAddress4 = try! CIDRAddress(stringRepresentation: "240.0.0.0/4")!
XCTAssertEqual(cidrAddress4.ipAddress, cidrAddress4.subnetString)
let cidrAddress5 = try! CIDRAddress(stringRepresentation: "248.0.0.0/5")!
XCTAssertEqual(cidrAddress5.ipAddress, cidrAddress5.subnetString)
let cidrAddress6 = try! CIDRAddress(stringRepresentation: "252.0.0.0/6")!
XCTAssertEqual(cidrAddress6.ipAddress, cidrAddress6.subnetString)
let cidrAddress7 = try! CIDRAddress(stringRepresentation: "254.0.0.0/7")!
XCTAssertEqual(cidrAddress7.ipAddress, cidrAddress7.subnetString)
let cidrAddress8 = try! CIDRAddress(stringRepresentation: "255.0.0.0/8")!
XCTAssertEqual(cidrAddress8.ipAddress, cidrAddress8.subnetString)
let cidrAddress9 = try! CIDRAddress(stringRepresentation: "255.128.0.0/9")!
XCTAssertEqual(cidrAddress9.ipAddress, cidrAddress9.subnetString)
let cidrAddress10 = try! CIDRAddress(stringRepresentation: "255.192.0.0/10")!
XCTAssertEqual(cidrAddress10.ipAddress, cidrAddress10.subnetString)
let cidrAddress11 = try! CIDRAddress(stringRepresentation: "255.224.0.0/11")!
XCTAssertEqual(cidrAddress11.ipAddress, cidrAddress11.subnetString)
let cidrAddress12 = try! CIDRAddress(stringRepresentation: "255.240.0.0/12")!
XCTAssertEqual(cidrAddress12.ipAddress, cidrAddress12.subnetString)
let cidrAddress13 = try! CIDRAddress(stringRepresentation: "255.248.0.0/13")!
XCTAssertEqual(cidrAddress13.ipAddress, cidrAddress13.subnetString)
let cidrAddress14 = try! CIDRAddress(stringRepresentation: "255.252.0.0/14")!
XCTAssertEqual(cidrAddress14.ipAddress, cidrAddress14.subnetString)
let cidrAddress15 = try! CIDRAddress(stringRepresentation: "255.254.0.0/15")!
XCTAssertEqual(cidrAddress15.ipAddress, cidrAddress15.subnetString)
let cidrAddress16 = try! CIDRAddress(stringRepresentation: "255.255.0.0/16")!
XCTAssertEqual(cidrAddress16.ipAddress, cidrAddress16.subnetString)
let cidrAddress17 = try! CIDRAddress(stringRepresentation: "255.255.128.0/17")!
XCTAssertEqual(cidrAddress17.ipAddress, cidrAddress17.subnetString)
let cidrAddress18 = try! CIDRAddress(stringRepresentation: "255.255.192.0/18")!
XCTAssertEqual(cidrAddress18.ipAddress, cidrAddress18.subnetString)
let cidrAddress19 = try! CIDRAddress(stringRepresentation: "255.255.224.0/19")!
XCTAssertEqual(cidrAddress19.ipAddress, cidrAddress19.subnetString)
let cidrAddress20 = try! CIDRAddress(stringRepresentation: "255.255.240.0/20")!
XCTAssertEqual(cidrAddress20.ipAddress, cidrAddress20.subnetString)
let cidrAddress21 = try! CIDRAddress(stringRepresentation: "255.255.248.0/21")!
XCTAssertEqual(cidrAddress21.ipAddress, cidrAddress21.subnetString)
let cidrAddress22 = try! CIDRAddress(stringRepresentation: "255.255.252.0/22")!
XCTAssertEqual(cidrAddress22.ipAddress, cidrAddress22.subnetString)
let cidrAddress23 = try! CIDRAddress(stringRepresentation: "255.255.254.0/23")!
XCTAssertEqual(cidrAddress23.ipAddress, cidrAddress23.subnetString)
let cidrAddress24 = try! CIDRAddress(stringRepresentation: "255.255.255.0/24")!
XCTAssertEqual(cidrAddress24.ipAddress, cidrAddress24.subnetString)
let cidrAddress25 = try! CIDRAddress(stringRepresentation: "255.255.255.128/25")!
XCTAssertEqual(cidrAddress25.ipAddress, cidrAddress25.subnetString)
let cidrAddress26 = try! CIDRAddress(stringRepresentation: "255.255.255.192/26")!
XCTAssertEqual(cidrAddress26.ipAddress, cidrAddress26.subnetString)
let cidrAddress27 = try! CIDRAddress(stringRepresentation: "255.255.255.224/27")!
XCTAssertEqual(cidrAddress27.ipAddress, cidrAddress27.subnetString)
let cidrAddress28 = try! CIDRAddress(stringRepresentation: "255.255.255.240/28")!
XCTAssertEqual(cidrAddress28.ipAddress, cidrAddress28.subnetString)
let cidrAddress29 = try! CIDRAddress(stringRepresentation: "255.255.255.248/29")!
XCTAssertEqual(cidrAddress29.ipAddress, cidrAddress29.subnetString)
let cidrAddress30 = try! CIDRAddress(stringRepresentation: "255.255.255.252/30")!
XCTAssertEqual(cidrAddress30.ipAddress, cidrAddress30.subnetString)
let cidrAddress31 = try! CIDRAddress(stringRepresentation: "255.255.255.254/31")!
XCTAssertEqual(cidrAddress31.ipAddress, cidrAddress31.subnetString)
let cidrAddress32 = try! CIDRAddress(stringRepresentation: "255.255.255.255/32")!
XCTAssertEqual(cidrAddress32.ipAddress, cidrAddress32.subnetString)
// swiftlint:enable force_try
}
}