Move .ovpn parser to TunnelKit

This commit is contained in:
Davide De Rosa 2018-11-10 10:29:51 +01:00
parent aeb042b225
commit 3d5c2bb8f5
23 changed files with 96 additions and 688 deletions

View File

@ -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

View File

@ -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)
} }

View File

@ -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")
} }
} }
} }

View File

@ -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)

View File

@ -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?

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 */,
); );

View File

@ -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
} }

View File

@ -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 }
} }

View File

@ -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
} }
} }

View File

@ -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

View File

@ -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
} }
} }

View File

@ -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()
} }

View File

@ -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

View File

@ -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")
}
}

View File

@ -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")!
}
}

View File

@ -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 {

View File

@ -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>

View File

@ -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.3.1'
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

View File

@ -2,19 +2,19 @@ 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 (from `https://github.com/keeshux/tunnelkit`, commit `ac9ce0d`)
SPEC REPOS: SPEC REPOS:
https://github.com/cocoapods/specs.git: https://github.com/cocoapods/specs.git:
@ -24,20 +24,20 @@ SPEC REPOS:
EXTERNAL SOURCES: EXTERNAL SOURCES:
TunnelKit: TunnelKit:
:commit: caea662 :commit: ac9ce0d
:git: https://github.com/keeshux/tunnelkit :git: https://github.com/keeshux/tunnelkit
CHECKOUT OPTIONS: CHECKOUT OPTIONS:
TunnelKit: TunnelKit:
:commit: caea662 :commit: ac9ce0d
:git: https://github.com/keeshux/tunnelkit :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: 07c9d064a6f7b053986d305449fa44258029bb35
COCOAPODS: 1.6.0.beta.2 COCOAPODS: 1.6.0.beta.2

View File

@ -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