Merge branch 'use-ovpn-parser-from-tunnelkit'
This commit is contained in:
commit
38db06d2ea
|
@ -24,6 +24,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import TunnelKit
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
|
||||||
|
@ -93,32 +94,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||||
let topmost = root.presentedViewController ?? root
|
let topmost = root.presentedViewController ?? root
|
||||||
|
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
guard let parsedFile = ParsedFile.from(url, withErrorAlertIn: topmost) else {
|
guard let parsingResult = ConfigurationParser.ParsingResult.from(url, withErrorAlertIn: topmost) else {
|
||||||
try? fm.removeItem(at: url)
|
try? fm.removeItem(at: url)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if let warning = parsedFile.warning {
|
if let warning = parsingResult.warning {
|
||||||
ParsedFile.alertImportWarning(url: url, in: topmost, withWarning: warning) {
|
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: topmost, withWarning: warning) {
|
||||||
if $0 {
|
if $0 {
|
||||||
self.handleParsedFile(parsedFile, in: topmost)
|
self.handleParsingResult(parsingResult, in: topmost)
|
||||||
} else {
|
} else {
|
||||||
try? fm.removeItem(at: url)
|
try? fm.removeItem(at: url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
handleParsedFile(parsedFile, in: topmost)
|
handleParsingResult(parsingResult, in: topmost)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleParsedFile(_ parsedFile: ParsedFile, in target: UIViewController) {
|
private func handleParsingResult(_ parsingResult: ConfigurationParser.ParsingResult, in target: UIViewController) {
|
||||||
|
|
||||||
// already presented: update parsed configuration
|
// already presented: update parsed configuration
|
||||||
if let nav = target as? UINavigationController, let wizard = nav.topViewController as? WizardHostViewController {
|
if let nav = target as? UINavigationController, let wizard = nav.topViewController as? WizardHostViewController {
|
||||||
if let oldURL = wizard.parsedFile?.url {
|
if let oldURL = wizard.parsingResult?.url {
|
||||||
try? FileManager.default.removeItem(at: oldURL)
|
try? FileManager.default.removeItem(at: oldURL)
|
||||||
}
|
}
|
||||||
wizard.parsedFile = parsedFile
|
wizard.parsingResult = parsingResult
|
||||||
wizard.removesConfigurationOnCancel = true
|
wizard.removesConfigurationOnCancel = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -128,7 +129,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||||
guard let wizard = wizardNav.topViewController as? WizardHostViewController else {
|
guard let wizard = wizardNav.topViewController as? WizardHostViewController else {
|
||||||
fatalError("Expected WizardHostViewController from storyboard")
|
fatalError("Expected WizardHostViewController from storyboard")
|
||||||
}
|
}
|
||||||
wizard.parsedFile = parsedFile
|
wizard.parsingResult = parsingResult
|
||||||
wizard.removesConfigurationOnCancel = true
|
wizard.removesConfigurationOnCancel = true
|
||||||
|
|
||||||
wizardNav.modalPresentationStyle = .formSheet
|
wizardNav.modalPresentationStyle = .formSheet
|
||||||
|
|
|
@ -91,7 +91,7 @@ class IssueReporter: NSObject {
|
||||||
}
|
}
|
||||||
if let url = configurationURL {
|
if let url = configurationURL {
|
||||||
do {
|
do {
|
||||||
let parsedFile = try TunnelKitProvider.Configuration.parsed(fromURL: url, returnsStripped: true)
|
let parsedFile = try ConfigurationParser.parsed(fromURL: url, returnsStripped: true)
|
||||||
if let attachment = parsedFile.strippedLines?.joined(separator: "\n").data(using: .utf8) {
|
if let attachment = parsedFile.strippedLines?.joined(separator: "\n").data(using: .utf8) {
|
||||||
vc.addAttachmentData(attachment, mimeType: AppConstants.IssueReporter.MIME.configuration, fileName: AppConstants.IssueReporter.Filenames.configuration)
|
vc.addAttachmentData(attachment, mimeType: AppConstants.IssueReporter.MIME.configuration, fileName: AppConstants.IssueReporter.Filenames.configuration)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// ParsedFile+Alerts.swift
|
// ParsingResult+Alerts.swift
|
||||||
// Passepartout-iOS
|
// Passepartout-iOS
|
||||||
//
|
//
|
||||||
// Created by Davide De Rosa on 10/27/18.
|
// Created by Davide De Rosa on 10/27/18.
|
||||||
|
@ -30,18 +30,18 @@ import SwiftyBeaver
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
extension ParsedFile {
|
extension ConfigurationParser.ParsingResult {
|
||||||
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController) -> ParsedFile? {
|
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController) -> ConfigurationParser.ParsingResult? {
|
||||||
let file: ParsedFile
|
let result: ConfigurationParser.ParsingResult
|
||||||
log.debug("Parsing configuration URL: \(url)")
|
log.debug("Parsing configuration URL: \(url)")
|
||||||
do {
|
do {
|
||||||
file = try TunnelKitProvider.Configuration.parsed(fromURL: url)
|
result = try ConfigurationParser.parsed(fromURL: url)
|
||||||
} catch let e {
|
} catch let e {
|
||||||
let message = localizedMessage(forError: e)
|
let message = localizedMessage(forError: e)
|
||||||
alertImportError(url: url, in: viewController, withMessage: message)
|
alertImportError(url: url, in: viewController, withMessage: message)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return file
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func alertImportError(url: URL, in vc: UIViewController, withMessage message: String) {
|
private static func alertImportError(url: URL, in vc: UIViewController, withMessage message: String) {
|
||||||
|
@ -55,7 +55,7 @@ extension ParsedFile {
|
||||||
vc.present(alert, animated: true, completion: nil)
|
vc.present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func alertImportWarning(url: URL, in vc: UIViewController, withWarning warning: ApplicationError, completionHandler: @escaping (Bool) -> Void) {
|
static func alertImportWarning(url: URL, in vc: UIViewController, withWarning warning: ConfigurationParser.ParsingError, completionHandler: @escaping (Bool) -> Void) {
|
||||||
let message = details(forWarning: warning)
|
let message = details(forWarning: warning)
|
||||||
let alert = Macros.alert(url.normalizedFilename, L10n.ParsedFile.Alerts.PotentiallyUnsupported.message(message))
|
let alert = Macros.alert(url.normalizedFilename, L10n.ParsedFile.Alerts.PotentiallyUnsupported.message(message))
|
||||||
alert.addDefaultAction(L10n.Global.ok) {
|
alert.addDefaultAction(L10n.Global.ok) {
|
||||||
|
@ -68,7 +68,7 @@ extension ParsedFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func localizedMessage(forError error: Error) -> String {
|
private static func localizedMessage(forError error: Error) -> String {
|
||||||
if let appError = error as? ApplicationError {
|
if let appError = error as? ConfigurationParser.ParsingError {
|
||||||
switch appError {
|
switch appError {
|
||||||
case .missingConfiguration(let option):
|
case .missingConfiguration(let option):
|
||||||
log.error("Could not parse configuration URL: missing configuration, \(option)")
|
log.error("Could not parse configuration URL: missing configuration, \(option)")
|
||||||
|
@ -77,25 +77,19 @@ extension ParsedFile {
|
||||||
case .unsupportedConfiguration(let option):
|
case .unsupportedConfiguration(let option):
|
||||||
log.error("Could not parse configuration URL: unsupported configuration, \(option)")
|
log.error("Could not parse configuration URL: unsupported configuration, \(option)")
|
||||||
return L10n.ParsedFile.Alerts.Unsupported.message(option)
|
return L10n.ParsedFile.Alerts.Unsupported.message(option)
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.error("Could not parse configuration URL: \(error)")
|
log.error("Could not parse configuration URL: \(error)")
|
||||||
return L10n.ParsedFile.Alerts.Parsing.message(error.localizedDescription)
|
return L10n.ParsedFile.Alerts.Parsing.message(error.localizedDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func details(forWarning warning: ApplicationError) -> String {
|
private static func details(forWarning warning: ConfigurationParser.ParsingError) -> String {
|
||||||
switch warning {
|
switch warning {
|
||||||
case .missingConfiguration(let option):
|
case .missingConfiguration(let option):
|
||||||
return option
|
return option
|
||||||
|
|
||||||
case .unsupportedConfiguration(let option):
|
case .unsupportedConfiguration(let option):
|
||||||
return option
|
return option
|
||||||
|
|
||||||
default:
|
|
||||||
fatalError("Only use .missingConfiguration or .unsupportedConfiguration for warnings")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -121,16 +121,16 @@ class ConfigurationViewController: UIViewController, TableModelHost {
|
||||||
log.warning("Resetting with no original configuration set? Bad table model?")
|
log.warning("Resetting with no original configuration set? Bad table model?")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let parsedFile: ParsedFile
|
let parsingResult: ConfigurationParser.ParsingResult
|
||||||
do {
|
do {
|
||||||
parsedFile = try TunnelKitProvider.Configuration.parsed(fromURL: originalURL)
|
parsingResult = try ConfigurationParser.parsed(fromURL: originalURL)
|
||||||
} catch let e {
|
} catch let e {
|
||||||
log.error("Could not parse original configuration: \(e)")
|
log.error("Could not parse original configuration: \(e)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
configuration = parsedFile.configuration.sessionConfiguration.builder()
|
configuration = parsingResult.configuration.builder()
|
||||||
itemRefresh.isEnabled = !configuration.canCommunicate(with: initialConfiguration)
|
itemRefresh.isEnabled = !configuration.canCommunicate(with: initialConfiguration)
|
||||||
initialConfiguration = parsedFile.configuration.sessionConfiguration
|
initialConfiguration = parsingResult.configuration
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
|
|
||||||
delegate?.configuration(didUpdate: initialConfiguration)
|
delegate?.configuration(didUpdate: initialConfiguration)
|
||||||
|
|
|
@ -27,7 +27,7 @@ import UIKit
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
|
|
||||||
protocol EndpointViewControllerDelegate: class {
|
protocol EndpointViewControllerDelegate: class {
|
||||||
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: TunnelKitProvider.EndpointProtocol?)
|
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: EndpointProtocol?)
|
||||||
}
|
}
|
||||||
|
|
||||||
class EndpointViewController: UIViewController, TableModelHost {
|
class EndpointViewController: UIViewController, TableModelHost {
|
||||||
|
@ -37,15 +37,15 @@ class EndpointViewController: UIViewController, TableModelHost {
|
||||||
|
|
||||||
private var endpointAddresses: [String] = []
|
private var endpointAddresses: [String] = []
|
||||||
|
|
||||||
private var endpointProtocols: [TunnelKitProvider.EndpointProtocol] = []
|
private var endpointProtocols: [EndpointProtocol] = []
|
||||||
|
|
||||||
private var initialAddress: String?
|
private var initialAddress: String?
|
||||||
|
|
||||||
private var initialProtocol: TunnelKitProvider.EndpointProtocol?
|
private var initialProtocol: EndpointProtocol?
|
||||||
|
|
||||||
private var currentAddress: String?
|
private var currentAddress: String?
|
||||||
|
|
||||||
private var currentProtocol: TunnelKitProvider.EndpointProtocol?
|
private var currentProtocol: EndpointProtocol?
|
||||||
|
|
||||||
private var currentAddressIndexPath: IndexPath?
|
private var currentAddressIndexPath: IndexPath?
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ private let log = SwiftyBeaver.self
|
||||||
class ImportedHostsViewController: UITableViewController {
|
class ImportedHostsViewController: UITableViewController {
|
||||||
private lazy var pendingConfigurationURLs = TransientStore.shared.service.pendingConfigurationURLs().sortedCaseInsensitive()
|
private lazy var pendingConfigurationURLs = TransientStore.shared.service.pendingConfigurationURLs().sortedCaseInsensitive()
|
||||||
|
|
||||||
private var parsedFile: ParsedFile?
|
private var parsingResult: ConfigurationParser.ParsingResult?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
@ -43,7 +43,7 @@ class ImportedHostsViewController: UITableViewController {
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
parsedFile = nil
|
parsingResult = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
@ -70,25 +70,25 @@ class ImportedHostsViewController: UITableViewController {
|
||||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||||
|
|
||||||
// segue parses configuration file if not yet
|
// segue parses configuration file if not yet
|
||||||
if parsedFile == nil {
|
if parsingResult == nil {
|
||||||
guard let cell = sender as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) else {
|
guard let cell = sender as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let url = pendingConfigurationURLs[indexPath.row]
|
let url = pendingConfigurationURLs[indexPath.row]
|
||||||
guard let parsedFile = ParsedFile.from(url, withErrorAlertIn: self) else {
|
guard let parsingResult = ConfigurationParser.ParsingResult.from(url, withErrorAlertIn: self) else {
|
||||||
deselectSelectedRow()
|
deselectSelectedRow()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
self.parsedFile = parsedFile
|
self.parsingResult = parsingResult
|
||||||
|
|
||||||
// postpone segue until alert dismissal
|
// postpone segue until alert dismissal
|
||||||
if let warning = parsedFile.warning {
|
if let warning = parsingResult.warning {
|
||||||
ParsedFile.alertImportWarning(url: url, in: self, withWarning: warning) {
|
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: self, withWarning: warning) {
|
||||||
self.deselectSelectedRow()
|
self.deselectSelectedRow()
|
||||||
if $0 {
|
if $0 {
|
||||||
self.perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier)
|
self.perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier)
|
||||||
} else {
|
} else {
|
||||||
self.parsedFile = nil
|
self.parsingResult = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -101,7 +101,7 @@ class ImportedHostsViewController: UITableViewController {
|
||||||
guard let wizard = segue.destination as? WizardHostViewController else {
|
guard let wizard = segue.destination as? WizardHostViewController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wizard.parsedFile = parsedFile
|
wizard.parsingResult = parsingResult
|
||||||
|
|
||||||
// retain back button
|
// retain back button
|
||||||
wizard.navigationItem.leftBarButtonItem = nil
|
wizard.navigationItem.leftBarButtonItem = nil
|
||||||
|
|
|
@ -36,7 +36,7 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
||||||
return TransientStore.shared.service.ids(forContext: .host).sortedCaseInsensitive()
|
return TransientStore.shared.service.ids(forContext: .host).sortedCaseInsensitive()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var parsedFile: ParsedFile? {
|
var parsingResult: ConfigurationParser.ParsingResult? {
|
||||||
didSet {
|
didSet {
|
||||||
useSuggestedTitle()
|
useSuggestedTitle()
|
||||||
}
|
}
|
||||||
|
@ -88,19 +88,21 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
||||||
// MARK: Actions
|
// MARK: Actions
|
||||||
|
|
||||||
private func useSuggestedTitle() {
|
private func useSuggestedTitle() {
|
||||||
cellTitle?.field.text = parsedFile?.url?.normalizedFilename
|
cellTitle?.field.text = parsingResult?.url?.normalizedFilename
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func next() {
|
@IBAction private func next() {
|
||||||
guard let enteredTitle = cellTitle?.field.text?.trimmingCharacters(in: .whitespaces), !enteredTitle.isEmpty else {
|
guard let enteredTitle = cellTitle?.field.text?.trimmingCharacters(in: .whitespaces), !enteredTitle.isEmpty else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let file = parsedFile else {
|
guard let result = parsingResult else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let profile = HostConnectionProfile(title: enteredTitle, hostname: file.hostname)
|
let profile = HostConnectionProfile(title: enteredTitle, hostname: result.hostname)
|
||||||
profile.parameters = file.configuration
|
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: result.configuration)
|
||||||
|
builder.endpointProtocols = result.protocols
|
||||||
|
profile.parameters = builder.build()
|
||||||
|
|
||||||
let service = TransientStore.shared.service
|
let service = TransientStore.shared.service
|
||||||
guard !service.containsProfile(profile) else {
|
guard !service.containsProfile(profile) else {
|
||||||
|
@ -132,7 +134,7 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
||||||
fatalError("No profile created?")
|
fatalError("No profile created?")
|
||||||
}
|
}
|
||||||
let service = TransientStore.shared.service
|
let service = TransientStore.shared.service
|
||||||
if let url = parsedFile?.url {
|
if let url = parsingResult?.url {
|
||||||
do {
|
do {
|
||||||
let savedURL = try service.save(configurationURL: url, for: profile)
|
let savedURL = try service.save(configurationURL: url, for: profile)
|
||||||
log.debug("Associated .ovpn configuration file to profile '\(profile.id)': \(savedURL)")
|
log.debug("Associated .ovpn configuration file to profile '\(profile.id)': \(savedURL)")
|
||||||
|
@ -149,7 +151,7 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func close() {
|
@IBAction private func close() {
|
||||||
if removesConfigurationOnCancel, let url = parsedFile?.url {
|
if removesConfigurationOnCancel, let url = parsingResult?.url {
|
||||||
try? FileManager.default.removeItem(at: url)
|
try? FileManager.default.removeItem(at: url)
|
||||||
}
|
}
|
||||||
dismiss(animated: true, completion: nil)
|
dismiss(animated: true, completion: nil)
|
||||||
|
|
|
@ -1050,7 +1050,7 @@ extension ServiceViewController: AccountViewControllerDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ServiceViewController: EndpointViewControllerDelegate {
|
extension ServiceViewController: EndpointViewControllerDelegate {
|
||||||
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: TunnelKitProvider.EndpointProtocol?) {
|
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: EndpointProtocol?) {
|
||||||
if let providerProfile = profile as? ProviderConnectionProfile {
|
if let providerProfile = profile as? ProviderConnectionProfile {
|
||||||
providerProfile.manualAddress = newAddress
|
providerProfile.manualAddress = newAddress
|
||||||
providerProfile.manualProtocol = newProtocol
|
providerProfile.manualProtocol = newProtocol
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; };
|
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; };
|
||||||
0E8D97E221388B52006FB4A0 /* InfrastructurePreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */; };
|
0E8D97E221388B52006FB4A0 /* InfrastructurePreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */; };
|
||||||
0E8D97E521389277006FB4A0 /* pia.json in Resources */ = {isa = PBXBuildFile; fileRef = 0E8D97E421389276006FB4A0 /* pia.json */; };
|
0E8D97E521389277006FB4A0 /* pia.json in Resources */ = {isa = PBXBuildFile; fileRef = 0E8D97E421389276006FB4A0 /* pia.json */; };
|
||||||
0EA068F4218475F800C320AD /* ParsedFile+Alerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA068F3218475F800C320AD /* ParsedFile+Alerts.swift */; };
|
0EA068F4218475F800C320AD /* ParsingResult+Alerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA068F3218475F800C320AD /* ParsingResult+Alerts.swift */; };
|
||||||
0EAAD71920E6669A0088754A /* GroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DED20C93E4C004C739C /* GroupConstants.swift */; };
|
0EAAD71920E6669A0088754A /* GroupConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DED20C93E4C004C739C /* GroupConstants.swift */; };
|
||||||
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */; };
|
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */; };
|
||||||
0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; };
|
0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */; };
|
||||||
|
@ -74,9 +74,6 @@
|
||||||
0ED31C3D20CF396E0027975F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; };
|
0ED31C3D20CF396E0027975F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED31C3920CF39510027975F /* NetworkExtension.framework */; };
|
||||||
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AD8213F33150004D387 /* WizardHostViewController.swift */; };
|
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AD8213F33150004D387 /* WizardHostViewController.swift */; };
|
||||||
0ED38ADA213F44D00004D387 /* Organizer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ED38ADC213F44D00004D387 /* Organizer.storyboard */; };
|
0ED38ADA213F44D00004D387 /* Organizer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ED38ADC213F44D00004D387 /* Organizer.storyboard */; };
|
||||||
0ED38ADE213F4E980004D387 /* TunnelKitProvider+FileConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38ADD213F4E980004D387 /* TunnelKitProvider+FileConfiguration.swift */; };
|
|
||||||
0ED38AE1213F51370004D387 /* FileConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AE0213F51370004D387 /* FileConfigurationTests.swift */; };
|
|
||||||
0ED38AE3213F517D0004D387 /* pia-hungary.ovpn in Resources */ = {isa = PBXBuildFile; fileRef = 0ED38AE2213F517D0004D387 /* pia-hungary.ovpn */; };
|
|
||||||
0ED38AE721404F100004D387 /* EndpointDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AE621404F100004D387 /* EndpointDataSource.swift */; };
|
0ED38AE721404F100004D387 /* EndpointDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AE621404F100004D387 /* EndpointDataSource.swift */; };
|
||||||
0ED38AEA214054A50004D387 /* OptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AE9214054A50004D387 /* OptionViewController.swift */; };
|
0ED38AEA214054A50004D387 /* OptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AE9214054A50004D387 /* OptionViewController.swift */; };
|
||||||
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */; };
|
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */; };
|
||||||
|
@ -171,7 +168,7 @@
|
||||||
0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardProviderViewController.swift; sourceTree = "<group>"; };
|
0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardProviderViewController.swift; sourceTree = "<group>"; };
|
||||||
0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfrastructurePreset.swift; sourceTree = "<group>"; };
|
0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfrastructurePreset.swift; sourceTree = "<group>"; };
|
||||||
0E8D97E421389276006FB4A0 /* pia.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = pia.json; sourceTree = "<group>"; };
|
0E8D97E421389276006FB4A0 /* pia.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = pia.json; sourceTree = "<group>"; };
|
||||||
0EA068F3218475F800C320AD /* ParsedFile+Alerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsedFile+Alerts.swift"; sourceTree = "<group>"; };
|
0EA068F3218475F800C320AD /* ParsingResult+Alerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParsingResult+Alerts.swift"; sourceTree = "<group>"; };
|
||||||
0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Search.swift"; sourceTree = "<group>"; };
|
0EB60FD92111136E00AD27F3 /* UITextView+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Search.swift"; sourceTree = "<group>"; };
|
||||||
0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportedHostsViewController.swift; sourceTree = "<group>"; };
|
0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportedHostsViewController.swift; sourceTree = "<group>"; };
|
||||||
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionServiceTests.swift; sourceTree = "<group>"; };
|
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionServiceTests.swift; sourceTree = "<group>"; };
|
||||||
|
@ -197,9 +194,6 @@
|
||||||
0ED31C3B20CF39510027975F /* Tunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = "<group>"; };
|
0ED31C3B20CF39510027975F /* Tunnel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = "<group>"; };
|
||||||
0ED38AD8213F33150004D387 /* WizardHostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardHostViewController.swift; sourceTree = "<group>"; };
|
0ED38AD8213F33150004D387 /* WizardHostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WizardHostViewController.swift; sourceTree = "<group>"; };
|
||||||
0ED38ADB213F44D00004D387 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Organizer.storyboard; sourceTree = "<group>"; };
|
0ED38ADB213F44D00004D387 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Organizer.storyboard; sourceTree = "<group>"; };
|
||||||
0ED38ADD213F4E980004D387 /* TunnelKitProvider+FileConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelKitProvider+FileConfiguration.swift"; sourceTree = "<group>"; };
|
|
||||||
0ED38AE0213F51370004D387 /* FileConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileConfigurationTests.swift; sourceTree = "<group>"; };
|
|
||||||
0ED38AE2213F517D0004D387 /* pia-hungary.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "pia-hungary.ovpn"; sourceTree = "<group>"; };
|
|
||||||
0ED38AE621404F100004D387 /* EndpointDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointDataSource.swift; sourceTree = "<group>"; };
|
0ED38AE621404F100004D387 /* EndpointDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndpointDataSource.swift; sourceTree = "<group>"; };
|
||||||
0ED38AE9214054A50004D387 /* OptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionViewController.swift; sourceTree = "<group>"; };
|
0ED38AE9214054A50004D387 /* OptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionViewController.swift; sourceTree = "<group>"; };
|
||||||
0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationModificationDelegate.swift; sourceTree = "<group>"; };
|
0ED38AEB2141260D0004D387 /* ConfigurationModificationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationModificationDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
@ -283,7 +277,6 @@
|
||||||
0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */,
|
0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */,
|
||||||
0E4FD7E020D3E4C5002221FF /* MockVPNProvider.swift */,
|
0E4FD7E020D3E4C5002221FF /* MockVPNProvider.swift */,
|
||||||
0E4FD7DD20D3E49A002221FF /* StandardVPNProvider.swift */,
|
0E4FD7DD20D3E49A002221FF /* StandardVPNProvider.swift */,
|
||||||
0ED38ADD213F4E980004D387 /* TunnelKitProvider+FileConfiguration.swift */,
|
|
||||||
0ED31C3620CF38D10027975F /* VPN.swift */,
|
0ED31C3620CF38D10027975F /* VPN.swift */,
|
||||||
0E5E5DE1215119DD00E318A3 /* VPNConfiguration.swift */,
|
0E5E5DE1215119DD00E318A3 /* VPNConfiguration.swift */,
|
||||||
0ED38AF1214177920004D387 /* VPNProvider.swift */,
|
0ED38AF1214177920004D387 /* VPNProvider.swift */,
|
||||||
|
@ -337,10 +330,8 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0EBBE8F121822B4D00106008 /* ConnectionService.json */,
|
0EBBE8F121822B4D00106008 /* ConnectionService.json */,
|
||||||
0ED38AE2213F517D0004D387 /* pia-hungary.ovpn */,
|
|
||||||
0E57F65220C83FC7008323CF /* Info.plist */,
|
0E57F65220C83FC7008323CF /* Info.plist */,
|
||||||
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */,
|
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */,
|
||||||
0ED38AE0213F51370004D387 /* FileConfigurationTests.swift */,
|
|
||||||
0ED31C2620CF257C0027975F /* InfrastructureTests.swift */,
|
0ED31C2620CF257C0027975F /* InfrastructureTests.swift */,
|
||||||
);
|
);
|
||||||
path = "PassepartoutTests-iOS";
|
path = "PassepartoutTests-iOS";
|
||||||
|
@ -445,7 +436,7 @@
|
||||||
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
||||||
0E4FD7F020D58618002221FF /* Macros.swift */,
|
0E4FD7F020D58618002221FF /* Macros.swift */,
|
||||||
0ED38AE9214054A50004D387 /* OptionViewController.swift */,
|
0ED38AE9214054A50004D387 /* OptionViewController.swift */,
|
||||||
0EA068F3218475F800C320AD /* ParsedFile+Alerts.swift */,
|
0EA068F3218475F800C320AD /* ParsingResult+Alerts.swift */,
|
||||||
0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */,
|
0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */,
|
||||||
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */,
|
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */,
|
||||||
0E05C61C20D27C82006EE732 /* Theme.swift */,
|
0E05C61C20D27C82006EE732 /* Theme.swift */,
|
||||||
|
@ -671,7 +662,6 @@
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0ED38AE3213F517D0004D387 /* pia-hungary.ovpn in Resources */,
|
|
||||||
0EBBE8F321822B4D00106008 /* ConnectionService.json in Resources */,
|
0EBBE8F321822B4D00106008 /* ConnectionService.json in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -826,7 +816,6 @@
|
||||||
0EBE3AA5213DC1B000BFA2F5 /* HostConnectionProfile.swift in Sources */,
|
0EBE3AA5213DC1B000BFA2F5 /* HostConnectionProfile.swift in Sources */,
|
||||||
0E05C5D420D1645F006EE732 /* FieldTableViewCell.swift in Sources */,
|
0E05C5D420D1645F006EE732 /* FieldTableViewCell.swift in Sources */,
|
||||||
0E05C61D20D27C82006EE732 /* Theme.swift in Sources */,
|
0E05C61D20D27C82006EE732 /* Theme.swift in Sources */,
|
||||||
0ED38ADE213F4E980004D387 /* TunnelKitProvider+FileConfiguration.swift in Sources */,
|
|
||||||
0EDE8DEE20C93E4C004C739C /* GroupConstants.swift in Sources */,
|
0EDE8DEE20C93E4C004C739C /* GroupConstants.swift in Sources */,
|
||||||
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */,
|
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */,
|
||||||
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */,
|
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */,
|
||||||
|
@ -866,7 +855,7 @@
|
||||||
0E57F63E20C83FC5008323CF /* ServiceViewController.swift in Sources */,
|
0E57F63E20C83FC5008323CF /* ServiceViewController.swift in Sources */,
|
||||||
0E39BCF0214B9EF10035E9DE /* WebServices.swift in Sources */,
|
0E39BCF0214B9EF10035E9DE /* WebServices.swift in Sources */,
|
||||||
0EDE8DE720C93945004C739C /* Credentials.swift in Sources */,
|
0EDE8DE720C93945004C739C /* Credentials.swift in Sources */,
|
||||||
0EA068F4218475F800C320AD /* ParsedFile+Alerts.swift in Sources */,
|
0EA068F4218475F800C320AD /* ParsingResult+Alerts.swift in Sources */,
|
||||||
0ED38AF2214177920004D387 /* VPNProvider.swift in Sources */,
|
0ED38AF2214177920004D387 /* VPNProvider.swift in Sources */,
|
||||||
0E4C9CB920DB9BC600A0C59C /* TrustedNetworks.swift in Sources */,
|
0E4C9CB920DB9BC600A0C59C /* TrustedNetworks.swift in Sources */,
|
||||||
0E57F63C20C83FC5008323CF /* AppDelegate.swift in Sources */,
|
0E57F63C20C83FC5008323CF /* AppDelegate.swift in Sources */,
|
||||||
|
@ -887,7 +876,6 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
0ED38AE1213F51370004D387 /* FileConfigurationTests.swift in Sources */,
|
|
||||||
0ED31C2720CF257C0027975F /* InfrastructureTests.swift in Sources */,
|
0ED31C2720CF257C0027975F /* InfrastructureTests.swift in Sources */,
|
||||||
0EBBE8F221822B4D00106008 /* ConnectionServiceTests.swift in Sources */,
|
0EBBE8F221822B4D00106008 /* ConnectionServiceTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,14 +25,10 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ApplicationError: Error {
|
enum ApplicationError: String, Error {
|
||||||
case missingProfile
|
case missingProfile
|
||||||
|
|
||||||
case missingCredentials
|
case missingCredentials
|
||||||
|
|
||||||
case missingConfiguration(option: String)
|
|
||||||
|
|
||||||
case unsupportedConfiguration(option: String)
|
|
||||||
|
|
||||||
case migration
|
case migration
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,11 +31,11 @@ protocol EndpointDataSource {
|
||||||
|
|
||||||
var addresses: [String] { get }
|
var addresses: [String] { get }
|
||||||
|
|
||||||
var protocols: [TunnelKitProvider.EndpointProtocol] { get }
|
var protocols: [EndpointProtocol] { get }
|
||||||
|
|
||||||
var canCustomizeEndpoint: Bool { get }
|
var canCustomizeEndpoint: Bool { get }
|
||||||
|
|
||||||
var customAddress: String? { get }
|
var customAddress: String? { get }
|
||||||
|
|
||||||
var customProtocol: TunnelKitProvider.EndpointProtocol? { get }
|
var customProtocol: EndpointProtocol? { get }
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ extension HostConnectionProfile {
|
||||||
return [hostname]
|
return [hostname]
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocols: [TunnelKitProvider.EndpointProtocol] {
|
var protocols: [EndpointProtocol] {
|
||||||
return parameters.endpointProtocols
|
return parameters.endpointProtocols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ extension HostConnectionProfile {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var customProtocol: TunnelKitProvider.EndpointProtocol? {
|
var customProtocol: EndpointProtocol? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,13 +47,13 @@ class PlaceholderConnectionProfile: ConnectionProfile {
|
||||||
|
|
||||||
var addresses: [String] = []
|
var addresses: [String] = []
|
||||||
|
|
||||||
var protocols: [TunnelKitProvider.EndpointProtocol] = []
|
var protocols: [EndpointProtocol] = []
|
||||||
|
|
||||||
var canCustomizeEndpoint: Bool = false
|
var canCustomizeEndpoint: Bool = false
|
||||||
|
|
||||||
var customAddress: String?
|
var customAddress: String?
|
||||||
|
|
||||||
var customProtocol: TunnelKitProvider.EndpointProtocol?
|
var customProtocol: EndpointProtocol?
|
||||||
|
|
||||||
init(_ context: Context, _ id: String) {
|
init(_ context: Context, _ id: String) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
|
@ -55,7 +55,7 @@ class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
|
||||||
|
|
||||||
var manualAddress: String?
|
var manualAddress: String?
|
||||||
|
|
||||||
var manualProtocol: TunnelKitProvider.EndpointProtocol?
|
var manualProtocol: EndpointProtocol?
|
||||||
|
|
||||||
var usesProviderEndpoint: Bool {
|
var usesProviderEndpoint: Bool {
|
||||||
return (manualAddress != nil) || (manualProtocol != nil)
|
return (manualAddress != nil) || (manualProtocol != nil)
|
||||||
|
@ -133,8 +133,8 @@ class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
|
||||||
} else {
|
} else {
|
||||||
builder.endpointProtocols = preset.configuration.endpointProtocols
|
builder.endpointProtocols = preset.configuration.endpointProtocols
|
||||||
// builder.endpointProtocols = [
|
// builder.endpointProtocols = [
|
||||||
// TunnelKitProvider.EndpointProtocol(.udp, 8080),
|
// EndpointProtocol(.udp, 8080),
|
||||||
// TunnelKitProvider.EndpointProtocol(.tcp, 443)
|
// EndpointProtocol(.tcp, 443)
|
||||||
// ]
|
// ]
|
||||||
}
|
}
|
||||||
return builder.build()
|
return builder.build()
|
||||||
|
@ -161,7 +161,7 @@ extension ProviderConnectionProfile {
|
||||||
return pool?.addresses(sorted: true) ?? []
|
return pool?.addresses(sorted: true) ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocols: [TunnelKitProvider.EndpointProtocol] {
|
var protocols: [EndpointProtocol] {
|
||||||
return preset?.configuration.endpointProtocols ?? []
|
return preset?.configuration.endpointProtocols ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ extension ProviderConnectionProfile {
|
||||||
return manualAddress
|
return manualAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
var customProtocol: TunnelKitProvider.EndpointProtocol? {
|
var customProtocol: EndpointProtocol? {
|
||||||
return manualProtocol
|
return manualProtocol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ struct InfrastructurePreset: Codable {
|
||||||
|
|
||||||
let configuration: TunnelKitProvider.Configuration
|
let configuration: TunnelKitProvider.Configuration
|
||||||
|
|
||||||
func hasProtocol(_ proto: TunnelKitProvider.EndpointProtocol) -> Bool {
|
func hasProtocol(_ proto: EndpointProtocol) -> Bool {
|
||||||
return configuration.endpointProtocols.index(of: proto) != nil
|
return configuration.endpointProtocols.index(of: proto) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ struct InfrastructurePreset: Codable {
|
||||||
sessionBuilder.usesPIAPatches = try cfgContainer.decodeIfPresent(Bool.self, forKey: .usesPIAPatches) ?? false
|
sessionBuilder.usesPIAPatches = try cfgContainer.decodeIfPresent(Bool.self, forKey: .usesPIAPatches) ?? false
|
||||||
|
|
||||||
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
||||||
builder.endpointProtocols = try cfgContainer.decode([TunnelKitProvider.EndpointProtocol].self, forKey: .endpointProtocols)
|
builder.endpointProtocols = try cfgContainer.decode([EndpointProtocol].self, forKey: .endpointProtocols)
|
||||||
configuration = builder.build()
|
configuration = builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,16 +192,6 @@ extension StringProtocol where Index == String.Index {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension String {
|
|
||||||
func trimmedLines() -> [String] {
|
|
||||||
return components(separatedBy: .newlines).map {
|
|
||||||
$0.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
||||||
}.filter {
|
|
||||||
!$0.isEmpty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CharacterSet {
|
extension CharacterSet {
|
||||||
static let filename: CharacterSet = {
|
static let filename: CharacterSet = {
|
||||||
var chars: CharacterSet = .decimalDigits
|
var chars: CharacterSet = .decimalDigits
|
||||||
|
|
|
@ -1,402 +0,0 @@
|
||||||
//
|
|
||||||
// TunnelKitProvider+FileConfiguration.swift
|
|
||||||
// Passepartout
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 9/5/18.
|
|
||||||
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
|
|
||||||
//
|
|
||||||
// https://github.com/passepartoutvpn
|
|
||||||
//
|
|
||||||
// This file is part of Passepartout.
|
|
||||||
//
|
|
||||||
// Passepartout is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Passepartout is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import TunnelKit
|
|
||||||
import SwiftyBeaver
|
|
||||||
|
|
||||||
private let log = SwiftyBeaver.self
|
|
||||||
|
|
||||||
struct ParsedFile {
|
|
||||||
let url: URL?
|
|
||||||
|
|
||||||
let hostname: String
|
|
||||||
|
|
||||||
let configuration: TunnelKitProvider.Configuration
|
|
||||||
|
|
||||||
let strippedLines: [String]?
|
|
||||||
|
|
||||||
let warning: ApplicationError?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension TunnelKitProvider.Configuration {
|
|
||||||
private struct Regex {
|
|
||||||
static let proto = Utils.regex("^proto +(udp6?|tcp6?)")
|
|
||||||
|
|
||||||
static let port = Utils.regex("^port +\\d+")
|
|
||||||
|
|
||||||
static let remote = Utils.regex("^remote +[^ ]+( +\\d+)?( +(udp6?|tcp6?))?")
|
|
||||||
|
|
||||||
static let cipher = Utils.regex("^cipher +[\\w\\-]+")
|
|
||||||
|
|
||||||
static let auth = Utils.regex("^auth +[\\w\\-]+")
|
|
||||||
|
|
||||||
static let compLZO = Utils.regex("^comp-lzo.*")
|
|
||||||
|
|
||||||
static let compress = Utils.regex("^compress.*")
|
|
||||||
|
|
||||||
static let ping = Utils.regex("^ping +\\d+")
|
|
||||||
|
|
||||||
static let renegSec = Utils.regex("^reneg-sec +\\d+")
|
|
||||||
|
|
||||||
static let keyDirection = Utils.regex("^key-direction +\\d")
|
|
||||||
|
|
||||||
static let blockBegin = Utils.regex("^<[\\w\\-]+>")
|
|
||||||
|
|
||||||
static let blockEnd = Utils.regex("^<\\/[\\w\\-]+>")
|
|
||||||
|
|
||||||
// unsupported
|
|
||||||
|
|
||||||
// static let fragment = Utils.regex("^fragment +\\d+")
|
|
||||||
static let fragment = Utils.regex("^fragment")
|
|
||||||
|
|
||||||
static let proxy = Utils.regex("^\\w+-proxy")
|
|
||||||
|
|
||||||
static let externalFiles = Utils.regex("^(ca|cert|key|tls-auth|tls-crypt) ")
|
|
||||||
}
|
|
||||||
|
|
||||||
static func parsed(fromURL url: URL, returnsStripped: Bool = false) throws -> ParsedFile {
|
|
||||||
let lines = try String(contentsOf: url).trimmedLines()
|
|
||||||
return try parsed(fromLines: lines, originalURL: url, returnsStripped: returnsStripped)
|
|
||||||
}
|
|
||||||
|
|
||||||
static func parsed(fromLines lines: [String], originalURL: URL? = nil, returnsStripped: Bool = false) throws -> ParsedFile {
|
|
||||||
var strippedLines: [String]? = returnsStripped ? [] : nil
|
|
||||||
var warning: ApplicationError? = nil
|
|
||||||
|
|
||||||
var defaultProto: TunnelKitProvider.SocketType?
|
|
||||||
var defaultPort: UInt16?
|
|
||||||
var remotes: [(String, UInt16?, TunnelKitProvider.SocketType?)] = []
|
|
||||||
|
|
||||||
var cipher: SessionProxy.Cipher?
|
|
||||||
var digest: SessionProxy.Digest?
|
|
||||||
var compressionFraming: SessionProxy.CompressionFraming = .disabled
|
|
||||||
var optCA: CryptoContainer?
|
|
||||||
var clientCertificate: CryptoContainer?
|
|
||||||
var clientKey: CryptoContainer?
|
|
||||||
var keepAliveSeconds: TimeInterval?
|
|
||||||
var renegotiateAfterSeconds: TimeInterval?
|
|
||||||
var keyDirection: StaticKey.Direction?
|
|
||||||
var tlsStrategy: SessionProxy.TLSWrap.Strategy?
|
|
||||||
var tlsKeyLines: [Substring]?
|
|
||||||
var tlsWrap: SessionProxy.TLSWrap?
|
|
||||||
|
|
||||||
var currentBlockName: String?
|
|
||||||
var currentBlock: [String] = []
|
|
||||||
var unsupportedError: ApplicationError? = nil
|
|
||||||
|
|
||||||
log.verbose("Configuration file:")
|
|
||||||
for line in lines {
|
|
||||||
log.verbose(line)
|
|
||||||
|
|
||||||
var isHandled = false
|
|
||||||
var strippedLine = line
|
|
||||||
defer {
|
|
||||||
if isHandled {
|
|
||||||
strippedLines?.append(strippedLine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Regex.blockBegin.enumerateComponents(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
let tag = $0.first!
|
|
||||||
let from = tag.index(after: tag.startIndex)
|
|
||||||
let to = tag.index(before: tag.endIndex)
|
|
||||||
|
|
||||||
currentBlockName = String(tag[from..<to])
|
|
||||||
currentBlock = []
|
|
||||||
}
|
|
||||||
Regex.blockEnd.enumerateComponents(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
let tag = $0.first!
|
|
||||||
let from = tag.index(tag.startIndex, offsetBy: 2)
|
|
||||||
let to = tag.index(before: tag.endIndex)
|
|
||||||
|
|
||||||
let blockName = String(tag[from..<to])
|
|
||||||
guard blockName == currentBlockName else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// first is opening tag
|
|
||||||
currentBlock.removeFirst()
|
|
||||||
switch blockName {
|
|
||||||
case "ca":
|
|
||||||
optCA = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
|
||||||
|
|
||||||
case "cert":
|
|
||||||
clientCertificate = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
|
||||||
|
|
||||||
case "key":
|
|
||||||
let container = CryptoContainer(pem: currentBlock.joined(separator: "\n"))
|
|
||||||
clientKey = container
|
|
||||||
if container.isEncrypted {
|
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "encrypted client certificate key")
|
|
||||||
}
|
|
||||||
|
|
||||||
case "tls-auth":
|
|
||||||
tlsKeyLines = currentBlock.map { Substring($0) }
|
|
||||||
tlsStrategy = .auth
|
|
||||||
|
|
||||||
case "tls-crypt":
|
|
||||||
tlsKeyLines = currentBlock.map { Substring($0) }
|
|
||||||
tlsStrategy = .crypt
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
currentBlockName = nil
|
|
||||||
currentBlock = []
|
|
||||||
}
|
|
||||||
if let _ = currentBlockName {
|
|
||||||
currentBlock.append(line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
Regex.proto.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let str = $0.first else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defaultProto = TunnelKitProvider.SocketType(protoString: str)
|
|
||||||
if defaultProto == nil {
|
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "proto \(str)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Regex.port.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let str = $0.first else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defaultPort = UInt16(str)
|
|
||||||
}
|
|
||||||
Regex.remote.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let hostname = $0.first else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var port: UInt16?
|
|
||||||
var proto: TunnelKitProvider.SocketType?
|
|
||||||
var strippedComponents = ["remote", "<hostname>"]
|
|
||||||
if $0.count > 1 {
|
|
||||||
port = UInt16($0[1])
|
|
||||||
strippedComponents.append($0[1])
|
|
||||||
}
|
|
||||||
if $0.count > 2 {
|
|
||||||
proto = TunnelKitProvider.SocketType(protoString: $0[2])
|
|
||||||
strippedComponents.append($0[2])
|
|
||||||
}
|
|
||||||
remotes.append((hostname, port, proto))
|
|
||||||
|
|
||||||
// replace private data
|
|
||||||
strippedLine = strippedComponents.joined(separator: " ")
|
|
||||||
}
|
|
||||||
Regex.cipher.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let rawValue = $0.first else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cipher = SessionProxy.Cipher(rawValue: rawValue.uppercased())
|
|
||||||
if cipher == nil {
|
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "cipher \(rawValue)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Regex.auth.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let rawValue = $0.first else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
digest = SessionProxy.Digest(rawValue: rawValue.uppercased())
|
|
||||||
if digest == nil {
|
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "auth \(rawValue)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Regex.compLZO.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
compressionFraming = .compLZO
|
|
||||||
|
|
||||||
guard let arg = $0.first else {
|
|
||||||
warning = warning ?? .unsupportedConfiguration(option: line)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard arg == "no" else {
|
|
||||||
unsupportedError = .unsupportedConfiguration(option: line)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Regex.compress.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
compressionFraming = .compress
|
|
||||||
|
|
||||||
guard $0.isEmpty else {
|
|
||||||
unsupportedError = .unsupportedConfiguration(option: line)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Regex.keyDirection.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let arg = $0.first, let value = Int(arg) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keyDirection = StaticKey.Direction(rawValue: value)
|
|
||||||
}
|
|
||||||
Regex.ping.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let arg = $0.first else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keepAliveSeconds = TimeInterval(arg)
|
|
||||||
}
|
|
||||||
Regex.renegSec.enumerateArguments(in: line) {
|
|
||||||
isHandled = true
|
|
||||||
guard let arg = $0.first else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
renegotiateAfterSeconds = TimeInterval(arg)
|
|
||||||
}
|
|
||||||
Regex.fragment.enumerateArguments(in: line) { (_) in
|
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "fragment")
|
|
||||||
}
|
|
||||||
Regex.proxy.enumerateArguments(in: line) { (_) in
|
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "proxy: \"\(line)\"")
|
|
||||||
}
|
|
||||||
Regex.externalFiles.enumerateArguments(in: line) { (_) in
|
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "external file: \"\(line)\"")
|
|
||||||
}
|
|
||||||
if line.contains("mtu") || line.contains("mssfix") {
|
|
||||||
isHandled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if let error = unsupportedError {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let ca = optCA else {
|
|
||||||
throw ApplicationError.missingConfiguration(option: "ca")
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: only reads first remote
|
|
||||||
// hostnames = remotes.map { $0.0 }
|
|
||||||
guard !remotes.isEmpty else {
|
|
||||||
throw ApplicationError.missingConfiguration(option: "remote")
|
|
||||||
}
|
|
||||||
let hostname = remotes[0].0
|
|
||||||
|
|
||||||
defaultProto = defaultProto ?? .udp
|
|
||||||
defaultPort = defaultPort ?? 1194
|
|
||||||
|
|
||||||
// XXX: reads endpoints from remotes with matching hostname
|
|
||||||
var endpointProtocols: [TunnelKitProvider.EndpointProtocol] = []
|
|
||||||
remotes.forEach {
|
|
||||||
guard $0.0 == hostname else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let port = $0.1 ?? defaultPort else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let socketType = $0.2 ?? defaultProto else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
endpointProtocols.append(TunnelKitProvider.EndpointProtocol(socketType, port))
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!endpointProtocols.isEmpty, "Must define an endpoint protocol")
|
|
||||||
|
|
||||||
if let keyLines = tlsKeyLines, let strategy = tlsStrategy {
|
|
||||||
let optKey: StaticKey?
|
|
||||||
switch strategy {
|
|
||||||
case .auth:
|
|
||||||
optKey = StaticKey(lines: keyLines, direction: keyDirection)
|
|
||||||
|
|
||||||
case .crypt:
|
|
||||||
optKey = StaticKey(lines: keyLines, direction: .client)
|
|
||||||
}
|
|
||||||
if let key = optKey {
|
|
||||||
tlsWrap = SessionProxy.TLSWrap(strategy: strategy, key: key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sessionBuilder = SessionProxy.ConfigurationBuilder(ca: ca)
|
|
||||||
sessionBuilder.cipher = cipher ?? .aes128cbc
|
|
||||||
sessionBuilder.digest = digest ?? .sha1
|
|
||||||
sessionBuilder.compressionFraming = compressionFraming
|
|
||||||
sessionBuilder.tlsWrap = tlsWrap
|
|
||||||
sessionBuilder.clientCertificate = clientCertificate
|
|
||||||
sessionBuilder.clientKey = clientKey
|
|
||||||
sessionBuilder.keepAliveInterval = keepAliveSeconds
|
|
||||||
sessionBuilder.renegotiatesAfter = renegotiateAfterSeconds
|
|
||||||
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
|
||||||
builder.endpointProtocols = endpointProtocols
|
|
||||||
|
|
||||||
return ParsedFile(
|
|
||||||
url: originalURL,
|
|
||||||
hostname: hostname,
|
|
||||||
configuration: builder.build(),
|
|
||||||
strippedLines: strippedLines,
|
|
||||||
warning: warning
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension TunnelKitProvider.SocketType {
|
|
||||||
init?(protoString: String) {
|
|
||||||
var str = protoString
|
|
||||||
if str.hasSuffix("6") {
|
|
||||||
str.removeLast()
|
|
||||||
}
|
|
||||||
self.init(rawValue: str.uppercased())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension NSRegularExpression {
|
|
||||||
func enumerateComponents(in string: String, using block: ([String]) -> Void) {
|
|
||||||
enumerateMatches(in: string, options: [], range: NSMakeRange(0, string.count)) { (result, flags, stop) in
|
|
||||||
guard let range = result?.range else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let match = (string as NSString).substring(with: range)
|
|
||||||
let tokens = match.components(separatedBy: " ").filter { !$0.isEmpty }
|
|
||||||
block(tokens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enumerateArguments(in string: String, using block: ([String]) -> Void) {
|
|
||||||
enumerateMatches(in: string, options: [], range: NSMakeRange(0, string.count)) { (result, flags, stop) in
|
|
||||||
guard let range = result?.range else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let match = (string as NSString).substring(with: range)
|
|
||||||
var tokens = match.components(separatedBy: " ").filter { !$0.isEmpty }
|
|
||||||
tokens.removeFirst()
|
|
||||||
block(tokens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CryptoContainer {
|
|
||||||
var isEncrypted: Bool {
|
|
||||||
return pem.contains("ENCRYPTED")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
//
|
|
||||||
// FileConfigurationTests.swift
|
|
||||||
// PassepartoutTests-iOS
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 9/5/18.
|
|
||||||
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
|
|
||||||
//
|
|
||||||
// https://github.com/passepartoutvpn
|
|
||||||
//
|
|
||||||
// This file is part of Passepartout.
|
|
||||||
//
|
|
||||||
// Passepartout is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// Passepartout is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
import TunnelKit
|
|
||||||
@testable import Passepartout
|
|
||||||
|
|
||||||
class FileConfigurationTests: XCTestCase {
|
|
||||||
override func setUp() {
|
|
||||||
super.setUp()
|
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tearDown() {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
super.tearDown()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testPIA() throws {
|
|
||||||
let file = try TunnelKitProvider.Configuration.parsed(fromURL: url(withName: "pia-hungary"))
|
|
||||||
XCTAssertEqual(file.hostname, "hungary.privateinternetaccess.com")
|
|
||||||
XCTAssertEqual(file.configuration.sessionConfiguration.cipher, .aes128cbc)
|
|
||||||
XCTAssertEqual(file.configuration.sessionConfiguration.digest, .sha1)
|
|
||||||
XCTAssertEqual(file.configuration.endpointProtocols, [
|
|
||||||
TunnelKitProvider.EndpointProtocol(.udp, 1198),
|
|
||||||
TunnelKitProvider.EndpointProtocol(.tcp, 502)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testStripped() throws {
|
|
||||||
let lines = try TunnelKitProvider.Configuration.parsed(fromURL: url(withName: "pia-hungary"), returnsStripped: true).strippedLines!
|
|
||||||
let stripped = lines.joined(separator: "\n")
|
|
||||||
print(stripped)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCompression() throws {
|
|
||||||
let base: [String] = ["<ca>", "</ca>", "remote 1.2.3.4"]
|
|
||||||
|
|
||||||
XCTAssertNotNil(try TunnelKitProvider.Configuration.parsed(fromLines: base + ["comp-lzo"]).warning)
|
|
||||||
XCTAssertNoThrow(try TunnelKitProvider.Configuration.parsed(fromLines: base + ["comp-lzo no"]))
|
|
||||||
XCTAssertThrowsError(try TunnelKitProvider.Configuration.parsed(fromLines: base + ["comp-lzo yes"]))
|
|
||||||
|
|
||||||
XCTAssertNoThrow(try TunnelKitProvider.Configuration.parsed(fromLines: base + ["compress"]))
|
|
||||||
XCTAssertThrowsError(try TunnelKitProvider.Configuration.parsed(fromLines: base + ["compress lzo"]))
|
|
||||||
}
|
|
||||||
|
|
||||||
private func url(withName name: String) -> URL {
|
|
||||||
return Bundle(for: FileConfigurationTests.self).url(forResource: name, withExtension: "ovpn")!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -53,21 +53,21 @@ class InfrastructureTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testStableSort() {
|
func testStableSort() {
|
||||||
let original: [TunnelKitProvider.EndpointProtocol] = [
|
let original: [EndpointProtocol] = [
|
||||||
TunnelKitProvider.EndpointProtocol(.udp, 1194),
|
EndpointProtocol(.udp, 1194),
|
||||||
TunnelKitProvider.EndpointProtocol(.udp, 8080),
|
EndpointProtocol(.udp, 8080),
|
||||||
TunnelKitProvider.EndpointProtocol(.udp, 9201),
|
EndpointProtocol(.udp, 9201),
|
||||||
TunnelKitProvider.EndpointProtocol(.udp, 53),
|
EndpointProtocol(.udp, 53),
|
||||||
TunnelKitProvider.EndpointProtocol(.udp, 1197),
|
EndpointProtocol(.udp, 1197),
|
||||||
TunnelKitProvider.EndpointProtocol(.udp, 198),
|
EndpointProtocol(.udp, 198),
|
||||||
TunnelKitProvider.EndpointProtocol(.tcp, 443),
|
EndpointProtocol(.tcp, 443),
|
||||||
TunnelKitProvider.EndpointProtocol(.tcp, 110),
|
EndpointProtocol(.tcp, 110),
|
||||||
TunnelKitProvider.EndpointProtocol(.tcp, 80),
|
EndpointProtocol(.tcp, 80),
|
||||||
TunnelKitProvider.EndpointProtocol(.tcp, 500),
|
EndpointProtocol(.tcp, 500),
|
||||||
TunnelKitProvider.EndpointProtocol(.tcp, 501),
|
EndpointProtocol(.tcp, 501),
|
||||||
TunnelKitProvider.EndpointProtocol(.tcp, 502)
|
EndpointProtocol(.tcp, 502)
|
||||||
]
|
]
|
||||||
var preferredType: TunnelKitProvider.SocketType
|
var preferredType: SocketType
|
||||||
|
|
||||||
preferredType = .udp
|
preferredType = .udp
|
||||||
let sorted1 = original.stableSorted {
|
let sorted1 = original.stableSorted {
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
client
|
|
||||||
dev tun
|
|
||||||
remote hungary.privateinternetaccess.com 1198 udp
|
|
||||||
remote hungary.privateinternetaccess.com 502 tcp
|
|
||||||
resolv-retry infinite
|
|
||||||
nobind
|
|
||||||
persist-key
|
|
||||||
persist-tun
|
|
||||||
setenv CLIENT_CERT 0
|
|
||||||
|
|
||||||
<ca>
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
|
|
||||||
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
|
|
||||||
BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu
|
|
||||||
dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx
|
|
||||||
IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB
|
|
||||||
FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1
|
|
||||||
MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
|
|
||||||
EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg
|
|
||||||
QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE
|
|
||||||
AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50
|
|
||||||
ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy
|
|
||||||
bmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXD
|
|
||||||
L1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzX
|
|
||||||
lH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWp
|
|
||||||
cdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/
|
|
||||||
8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB
|
|
||||||
/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RC
|
|
||||||
OfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tL
|
|
||||||
y8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZO
|
|
||||||
sqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpM
|
|
||||||
b3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4G
|
|
||||||
A1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUg
|
|
||||||
SW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2Vz
|
|
||||||
czEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5j
|
|
||||||
b22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAn
|
|
||||||
a5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xU
|
|
||||||
ryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK3
|
|
||||||
7pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyC
|
|
||||||
GohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz
|
|
||||||
1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUt
|
|
||||||
YDQ8z9v+DMO6iwyIDRiU
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
</ca>
|
|
||||||
|
|
||||||
cipher aes-128-cbc
|
|
||||||
auth sha1
|
|
||||||
tls-client
|
|
||||||
remote-cert-tls server
|
|
||||||
auth-user-pass
|
|
||||||
comp-lzo no
|
|
||||||
verb 1
|
|
||||||
reneg-sec 0
|
|
||||||
|
|
||||||
<crl-verify>
|
|
||||||
-----BEGIN X509 CRL-----
|
|
||||||
MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI
|
|
||||||
EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl
|
|
||||||
cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw
|
|
||||||
HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0
|
|
||||||
ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl
|
|
||||||
aW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZa
|
|
||||||
MCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG
|
|
||||||
9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5
|
|
||||||
jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EW
|
|
||||||
B4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Re
|
|
||||||
ze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA
|
|
||||||
5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqy
|
|
||||||
MR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A==
|
|
||||||
-----END X509 CRL-----
|
|
||||||
</crl-verify>
|
|
4
Podfile
4
Podfile
|
@ -2,8 +2,8 @@ source 'https://github.com/cocoapods/specs.git'
|
||||||
use_frameworks!
|
use_frameworks!
|
||||||
|
|
||||||
def shared_pods
|
def shared_pods
|
||||||
#pod 'TunnelKit', '~> 1.3.0'
|
pod 'TunnelKit', '~> 1.4.0'
|
||||||
pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'caea662'
|
#pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'ac9ce0d'
|
||||||
#pod 'TunnelKit', :path => '../../personal/tunnelkit'
|
#pod 'TunnelKit', :path => '../../personal/tunnelkit'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
27
Podfile.lock
27
Podfile.lock
|
@ -2,42 +2,33 @@ PODS:
|
||||||
- MBProgressHUD (1.1.0)
|
- MBProgressHUD (1.1.0)
|
||||||
- OpenSSL-Apple (1.1.0i-v2)
|
- OpenSSL-Apple (1.1.0i-v2)
|
||||||
- SwiftyBeaver (1.6.1)
|
- SwiftyBeaver (1.6.1)
|
||||||
- TunnelKit (1.3.1):
|
- TunnelKit (1.4.0):
|
||||||
- TunnelKit/AppExtension (= 1.3.1)
|
- TunnelKit/AppExtension (= 1.4.0)
|
||||||
- TunnelKit/Core (= 1.3.1)
|
- TunnelKit/Core (= 1.4.0)
|
||||||
- TunnelKit/AppExtension (1.3.1):
|
- TunnelKit/AppExtension (1.4.0):
|
||||||
- SwiftyBeaver
|
- SwiftyBeaver
|
||||||
- TunnelKit/Core
|
- TunnelKit/Core
|
||||||
- TunnelKit/Core (1.3.1):
|
- TunnelKit/Core (1.4.0):
|
||||||
- OpenSSL-Apple (~> 1.1.0h)
|
- OpenSSL-Apple (~> 1.1.0h)
|
||||||
- SwiftyBeaver
|
- SwiftyBeaver
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- MBProgressHUD
|
- MBProgressHUD
|
||||||
- TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `caea662`)
|
- TunnelKit (~> 1.4.0)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
https://github.com/cocoapods/specs.git:
|
https://github.com/cocoapods/specs.git:
|
||||||
- MBProgressHUD
|
- MBProgressHUD
|
||||||
- OpenSSL-Apple
|
- OpenSSL-Apple
|
||||||
- SwiftyBeaver
|
- SwiftyBeaver
|
||||||
|
- TunnelKit
|
||||||
EXTERNAL SOURCES:
|
|
||||||
TunnelKit:
|
|
||||||
:commit: caea662
|
|
||||||
:git: https://github.com/keeshux/tunnelkit
|
|
||||||
|
|
||||||
CHECKOUT OPTIONS:
|
|
||||||
TunnelKit:
|
|
||||||
:commit: caea662
|
|
||||||
:git: https://github.com/keeshux/tunnelkit
|
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
|
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
|
||||||
OpenSSL-Apple: a93b8f2eec8783ff40d9a9304de180ab68bb647c
|
OpenSSL-Apple: a93b8f2eec8783ff40d9a9304de180ab68bb647c
|
||||||
SwiftyBeaver: ccfcdf85a04d429f1633f668650b0ce8020bda3a
|
SwiftyBeaver: ccfcdf85a04d429f1633f668650b0ce8020bda3a
|
||||||
TunnelKit: f98fb7d88642eda94c42007dbc501903c469a891
|
TunnelKit: 8ac6ca743020b62e63bea66b3d169c53666fb737
|
||||||
|
|
||||||
PODFILE CHECKSUM: a86923e57746e09e04296a49bdc0ad3520fd700f
|
PODFILE CHECKSUM: 286e244242c593f64bdb564cfa8a8048df3c956b
|
||||||
|
|
||||||
COCOAPODS: 1.6.0.beta.2
|
COCOAPODS: 1.6.0.beta.2
|
||||||
|
|
24
README.md
24
README.md
|
@ -3,7 +3,7 @@
|
||||||
# [Passepartout][about-website]
|
# [Passepartout][about-website]
|
||||||
|
|
||||||
![iOS 11+](https://img.shields.io/badge/ios-11+-green.svg)
|
![iOS 11+](https://img.shields.io/badge/ios-11+-green.svg)
|
||||||
[![TunnelKit 1.3.x](https://img.shields.io/badge/tunnelkit-1.3-d69c68.svg)][dep-tunnelkit]
|
[![TunnelKit 1.4.x](https://img.shields.io/badge/tunnelkit-1.3-d69c68.svg)][dep-tunnelkit]
|
||||||
[![License GPLv3](https://img.shields.io/badge/license-GPLv3-lightgray.svg)](LICENSE)
|
[![License GPLv3](https://img.shields.io/badge/license-GPLv3-lightgray.svg)](LICENSE)
|
||||||
[![Join TestFlight](https://img.shields.io/badge/beta-TestFlight-5ecdf6.svg)][about-testflight]
|
[![Join TestFlight](https://img.shields.io/badge/beta-TestFlight-5ecdf6.svg)][about-testflight]
|
||||||
[![Join Reddit](https://img.shields.io/badge/discuss-Reddit-orange.svg)][about-reddit]
|
[![Join Reddit](https://img.shields.io/badge/discuss-Reddit-orange.svg)][about-reddit]
|
||||||
|
@ -63,26 +63,9 @@ In preset mode, you can pick pre-resolved IPv4 endpoints when DNS is problematic
|
||||||
|
|
||||||
### Import .ovpn profiles
|
### Import .ovpn profiles
|
||||||
|
|
||||||
Passepartout can import .ovpn configuration files. This way you can fine-tune encryption without tweaking and reimporting a new configuration. Below are a few limitations worth mentioning.
|
Passepartout can import .ovpn configuration files. This way you can fine-tune encryption without tweaking and reimporting a new configuration.
|
||||||
|
|
||||||
Unsupported:
|
You can find details on what may or may not work in the related section of the [TunnelKit README][dep-tunnelkit-ovpn].
|
||||||
|
|
||||||
- UDP fragmentation, i.e. `--fragment`
|
|
||||||
- Compression
|
|
||||||
- `--comp-lzo` other than `no`
|
|
||||||
- `--compress` other than empty
|
|
||||||
- Proxy
|
|
||||||
- External file references (inline `<block>` only)
|
|
||||||
- Encrypted client certificate keys
|
|
||||||
|
|
||||||
Ignored:
|
|
||||||
|
|
||||||
- MTU overrides
|
|
||||||
- `--*-mtu` and variants
|
|
||||||
- `--mssfix`
|
|
||||||
- Multiple `--remote` with different `host` values (first wins)
|
|
||||||
|
|
||||||
Many other flags are ignored too but it's normally not an issue.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -152,6 +135,7 @@ Website: [passepartoutvpn.app][about-website]
|
||||||
[dep-jazzy]: https://github.com/realm/jazzy
|
[dep-jazzy]: https://github.com/realm/jazzy
|
||||||
[dep-brew]: https://brew.sh/
|
[dep-brew]: https://brew.sh/
|
||||||
[dep-tunnelkit]: https://github.com/keeshux/tunnelkit
|
[dep-tunnelkit]: https://github.com/keeshux/tunnelkit
|
||||||
|
[dep-tunnelkit-ovpn]: https://github.com/keeshux/tunnelkit#support-for-ovpn-configuration
|
||||||
[dep-openssl]: https://www.openssl.org/
|
[dep-openssl]: https://www.openssl.org/
|
||||||
|
|
||||||
[license-content]: LICENSE
|
[license-content]: LICENSE
|
||||||
|
|
Loading…
Reference in New Issue