Move .ovpn parser to TunnelKit
This commit is contained in:
parent
aeb042b225
commit
3d5c2bb8f5
|
@ -24,6 +24,7 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import TunnelKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
|
||||
|
@ -93,32 +94,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
|||
let topmost = root.presentedViewController ?? root
|
||||
|
||||
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)
|
||||
return true
|
||||
}
|
||||
if let warning = parsedFile.warning {
|
||||
ParsedFile.alertImportWarning(url: url, in: topmost, withWarning: warning) {
|
||||
if let warning = parsingResult.warning {
|
||||
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: topmost, withWarning: warning) {
|
||||
if $0 {
|
||||
self.handleParsedFile(parsedFile, in: topmost)
|
||||
self.handleParsingResult(parsingResult, in: topmost)
|
||||
} else {
|
||||
try? fm.removeItem(at: url)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
handleParsedFile(parsedFile, in: topmost)
|
||||
handleParsingResult(parsingResult, in: topmost)
|
||||
return true
|
||||
}
|
||||
|
||||
private func handleParsedFile(_ parsedFile: ParsedFile, in target: UIViewController) {
|
||||
private func handleParsingResult(_ parsingResult: ConfigurationParser.ParsingResult, in target: UIViewController) {
|
||||
|
||||
// already presented: update parsed configuration
|
||||
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)
|
||||
}
|
||||
wizard.parsedFile = parsedFile
|
||||
wizard.parsingResult = parsingResult
|
||||
wizard.removesConfigurationOnCancel = true
|
||||
return
|
||||
}
|
||||
|
@ -128,7 +129,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
|||
guard let wizard = wizardNav.topViewController as? WizardHostViewController else {
|
||||
fatalError("Expected WizardHostViewController from storyboard")
|
||||
}
|
||||
wizard.parsedFile = parsedFile
|
||||
wizard.parsingResult = parsingResult
|
||||
wizard.removesConfigurationOnCancel = true
|
||||
|
||||
wizardNav.modalPresentationStyle = .formSheet
|
||||
|
|
|
@ -91,7 +91,7 @@ class IssueReporter: NSObject {
|
|||
}
|
||||
if let url = configurationURL {
|
||||
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) {
|
||||
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
|
||||
//
|
||||
// Created by Davide De Rosa on 10/27/18.
|
||||
|
@ -30,18 +30,18 @@ import SwiftyBeaver
|
|||
|
||||
private let log = SwiftyBeaver.self
|
||||
|
||||
extension ParsedFile {
|
||||
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController) -> ParsedFile? {
|
||||
let file: ParsedFile
|
||||
extension ConfigurationParser.ParsingResult {
|
||||
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController) -> ConfigurationParser.ParsingResult? {
|
||||
let result: ConfigurationParser.ParsingResult
|
||||
log.debug("Parsing configuration URL: \(url)")
|
||||
do {
|
||||
file = try TunnelKitProvider.Configuration.parsed(fromURL: url)
|
||||
result = try ConfigurationParser.parsed(fromURL: url)
|
||||
} catch let e {
|
||||
let message = localizedMessage(forError: e)
|
||||
alertImportError(url: url, in: viewController, withMessage: message)
|
||||
return nil
|
||||
}
|
||||
return file
|
||||
return result
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 alert = Macros.alert(url.normalizedFilename, L10n.ParsedFile.Alerts.PotentiallyUnsupported.message(message))
|
||||
alert.addDefaultAction(L10n.Global.ok) {
|
||||
|
@ -68,7 +68,7 @@ extension ParsedFile {
|
|||
}
|
||||
|
||||
private static func localizedMessage(forError error: Error) -> String {
|
||||
if let appError = error as? ApplicationError {
|
||||
if let appError = error as? ConfigurationParser.ParsingError {
|
||||
switch appError {
|
||||
case .missingConfiguration(let option):
|
||||
log.error("Could not parse configuration URL: missing configuration, \(option)")
|
||||
|
@ -77,25 +77,19 @@ extension ParsedFile {
|
|||
case .unsupportedConfiguration(let option):
|
||||
log.error("Could not parse configuration URL: unsupported configuration, \(option)")
|
||||
return L10n.ParsedFile.Alerts.Unsupported.message(option)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
log.error("Could not parse configuration URL: \(error)")
|
||||
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 {
|
||||
case .missingConfiguration(let option):
|
||||
return option
|
||||
|
||||
case .unsupportedConfiguration(let 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?")
|
||||
return
|
||||
}
|
||||
let parsedFile: ParsedFile
|
||||
let parsingResult: ConfigurationParser.ParsingResult
|
||||
do {
|
||||
parsedFile = try TunnelKitProvider.Configuration.parsed(fromURL: originalURL)
|
||||
parsingResult = try ConfigurationParser.parsed(fromURL: originalURL)
|
||||
} catch let e {
|
||||
log.error("Could not parse original configuration: \(e)")
|
||||
return
|
||||
}
|
||||
configuration = parsedFile.configuration.sessionConfiguration.builder()
|
||||
configuration = parsingResult.configuration.builder()
|
||||
itemRefresh.isEnabled = !configuration.canCommunicate(with: initialConfiguration)
|
||||
initialConfiguration = parsedFile.configuration.sessionConfiguration
|
||||
initialConfiguration = parsingResult.configuration
|
||||
tableView.reloadData()
|
||||
|
||||
delegate?.configuration(didUpdate: initialConfiguration)
|
||||
|
|
|
@ -27,7 +27,7 @@ import UIKit
|
|||
import TunnelKit
|
||||
|
||||
protocol EndpointViewControllerDelegate: class {
|
||||
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: TunnelKitProvider.EndpointProtocol?)
|
||||
func endpointController(_: EndpointViewController, didUpdateWithNewAddress newAddress: String?, newProtocol: EndpointProtocol?)
|
||||
}
|
||||
|
||||
class EndpointViewController: UIViewController, TableModelHost {
|
||||
|
@ -37,15 +37,15 @@ class EndpointViewController: UIViewController, TableModelHost {
|
|||
|
||||
private var endpointAddresses: [String] = []
|
||||
|
||||
private var endpointProtocols: [TunnelKitProvider.EndpointProtocol] = []
|
||||
private var endpointProtocols: [EndpointProtocol] = []
|
||||
|
||||
private var initialAddress: String?
|
||||
|
||||
private var initialProtocol: TunnelKitProvider.EndpointProtocol?
|
||||
private var initialProtocol: EndpointProtocol?
|
||||
|
||||
private var currentAddress: String?
|
||||
|
||||
private var currentProtocol: TunnelKitProvider.EndpointProtocol?
|
||||
private var currentProtocol: EndpointProtocol?
|
||||
|
||||
private var currentAddressIndexPath: IndexPath?
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ private let log = SwiftyBeaver.self
|
|||
class ImportedHostsViewController: UITableViewController {
|
||||
private lazy var pendingConfigurationURLs = TransientStore.shared.service.pendingConfigurationURLs().sortedCaseInsensitive()
|
||||
|
||||
private var parsedFile: ParsedFile?
|
||||
private var parsingResult: ConfigurationParser.ParsingResult?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
@ -43,7 +43,7 @@ class ImportedHostsViewController: UITableViewController {
|
|||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
parsedFile = nil
|
||||
parsingResult = nil
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
|
@ -70,25 +70,25 @@ class ImportedHostsViewController: UITableViewController {
|
|||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||
|
||||
// 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 {
|
||||
return false
|
||||
}
|
||||
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()
|
||||
return false
|
||||
}
|
||||
self.parsedFile = parsedFile
|
||||
self.parsingResult = parsingResult
|
||||
|
||||
// postpone segue until alert dismissal
|
||||
if let warning = parsedFile.warning {
|
||||
ParsedFile.alertImportWarning(url: url, in: self, withWarning: warning) {
|
||||
if let warning = parsingResult.warning {
|
||||
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: self, withWarning: warning) {
|
||||
self.deselectSelectedRow()
|
||||
if $0 {
|
||||
self.perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier)
|
||||
} else {
|
||||
self.parsedFile = nil
|
||||
self.parsingResult = nil
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -101,7 +101,7 @@ class ImportedHostsViewController: UITableViewController {
|
|||
guard let wizard = segue.destination as? WizardHostViewController else {
|
||||
return
|
||||
}
|
||||
wizard.parsedFile = parsedFile
|
||||
wizard.parsingResult = parsingResult
|
||||
|
||||
// retain back button
|
||||
wizard.navigationItem.leftBarButtonItem = nil
|
||||
|
|
|
@ -36,7 +36,7 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
|||
return TransientStore.shared.service.ids(forContext: .host).sortedCaseInsensitive()
|
||||
}()
|
||||
|
||||
var parsedFile: ParsedFile? {
|
||||
var parsingResult: ConfigurationParser.ParsingResult? {
|
||||
didSet {
|
||||
useSuggestedTitle()
|
||||
}
|
||||
|
@ -88,19 +88,21 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
|||
// MARK: Actions
|
||||
|
||||
private func useSuggestedTitle() {
|
||||
cellTitle?.field.text = parsedFile?.url?.normalizedFilename
|
||||
cellTitle?.field.text = parsingResult?.url?.normalizedFilename
|
||||
}
|
||||
|
||||
@IBAction private func next() {
|
||||
guard let enteredTitle = cellTitle?.field.text?.trimmingCharacters(in: .whitespaces), !enteredTitle.isEmpty else {
|
||||
return
|
||||
}
|
||||
guard let file = parsedFile else {
|
||||
guard let result = parsingResult else {
|
||||
return
|
||||
}
|
||||
|
||||
let profile = HostConnectionProfile(title: enteredTitle, hostname: file.hostname)
|
||||
profile.parameters = file.configuration
|
||||
let profile = HostConnectionProfile(title: enteredTitle, hostname: result.hostname)
|
||||
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: result.configuration)
|
||||
builder.endpointProtocols = result.protocols
|
||||
profile.parameters = builder.build()
|
||||
|
||||
let service = TransientStore.shared.service
|
||||
guard !service.containsProfile(profile) else {
|
||||
|
@ -132,7 +134,7 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
|||
fatalError("No profile created?")
|
||||
}
|
||||
let service = TransientStore.shared.service
|
||||
if let url = parsedFile?.url {
|
||||
if let url = parsingResult?.url {
|
||||
do {
|
||||
let savedURL = try service.save(configurationURL: url, for: profile)
|
||||
log.debug("Associated .ovpn configuration file to profile '\(profile.id)': \(savedURL)")
|
||||
|
@ -149,7 +151,7 @@ class WizardHostViewController: UITableViewController, TableModelHost {
|
|||
}
|
||||
|
||||
@IBAction private func close() {
|
||||
if removesConfigurationOnCancel, let url = parsedFile?.url {
|
||||
if removesConfigurationOnCancel, let url = parsingResult?.url {
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
}
|
||||
dismiss(animated: true, completion: nil)
|
||||
|
|
|
@ -1050,7 +1050,7 @@ extension ServiceViewController: AccountViewControllerDelegate {
|
|||
}
|
||||
|
||||
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 {
|
||||
providerProfile.manualAddress = newAddress
|
||||
providerProfile.manualProtocol = newProtocol
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; };
|
||||
0E8D97E221388B52006FB4A0 /* InfrastructurePreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */; };
|
||||
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 */; };
|
||||
0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB60FD92111136E00AD27F3 /* UITextView+Search.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 */; };
|
||||
0ED38AD9213F33150004D387 /* WizardHostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AD8213F33150004D387 /* WizardHostViewController.swift */; };
|
||||
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 */; };
|
||||
0ED38AEA214054A50004D387 /* OptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED38AE9214054A50004D387 /* OptionViewController.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -197,9 +194,6 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -283,7 +277,6 @@
|
|||
0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */,
|
||||
0E4FD7E020D3E4C5002221FF /* MockVPNProvider.swift */,
|
||||
0E4FD7DD20D3E49A002221FF /* StandardVPNProvider.swift */,
|
||||
0ED38ADD213F4E980004D387 /* TunnelKitProvider+FileConfiguration.swift */,
|
||||
0ED31C3620CF38D10027975F /* VPN.swift */,
|
||||
0E5E5DE1215119DD00E318A3 /* VPNConfiguration.swift */,
|
||||
0ED38AF1214177920004D387 /* VPNProvider.swift */,
|
||||
|
@ -337,10 +330,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0EBBE8F121822B4D00106008 /* ConnectionService.json */,
|
||||
0ED38AE2213F517D0004D387 /* pia-hungary.ovpn */,
|
||||
0E57F65220C83FC7008323CF /* Info.plist */,
|
||||
0EBBE8F021822B4D00106008 /* ConnectionServiceTests.swift */,
|
||||
0ED38AE0213F51370004D387 /* FileConfigurationTests.swift */,
|
||||
0ED31C2620CF257C0027975F /* InfrastructureTests.swift */,
|
||||
);
|
||||
path = "PassepartoutTests-iOS";
|
||||
|
@ -445,7 +436,7 @@
|
|||
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
||||
0E4FD7F020D58618002221FF /* Macros.swift */,
|
||||
0ED38AE9214054A50004D387 /* OptionViewController.swift */,
|
||||
0EA068F3218475F800C320AD /* ParsedFile+Alerts.swift */,
|
||||
0EA068F3218475F800C320AD /* ParsingResult+Alerts.swift */,
|
||||
0EDE8DE320C89028004C739C /* SwiftGen+Scenes.swift */,
|
||||
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */,
|
||||
0E05C61C20D27C82006EE732 /* Theme.swift */,
|
||||
|
@ -671,7 +662,6 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0ED38AE3213F517D0004D387 /* pia-hungary.ovpn in Resources */,
|
||||
0EBBE8F321822B4D00106008 /* ConnectionService.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -826,7 +816,6 @@
|
|||
0EBE3AA5213DC1B000BFA2F5 /* HostConnectionProfile.swift in Sources */,
|
||||
0E05C5D420D1645F006EE732 /* FieldTableViewCell.swift in Sources */,
|
||||
0E05C61D20D27C82006EE732 /* Theme.swift in Sources */,
|
||||
0ED38ADE213F4E980004D387 /* TunnelKitProvider+FileConfiguration.swift in Sources */,
|
||||
0EDE8DEE20C93E4C004C739C /* GroupConstants.swift in Sources */,
|
||||
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */,
|
||||
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */,
|
||||
|
@ -866,7 +855,7 @@
|
|||
0E57F63E20C83FC5008323CF /* ServiceViewController.swift in Sources */,
|
||||
0E39BCF0214B9EF10035E9DE /* WebServices.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 */,
|
||||
0E4C9CB920DB9BC600A0C59C /* TrustedNetworks.swift in Sources */,
|
||||
0E57F63C20C83FC5008323CF /* AppDelegate.swift in Sources */,
|
||||
|
@ -887,7 +876,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0ED38AE1213F51370004D387 /* FileConfigurationTests.swift in Sources */,
|
||||
0ED31C2720CF257C0027975F /* InfrastructureTests.swift in Sources */,
|
||||
0EBBE8F221822B4D00106008 /* ConnectionServiceTests.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -25,14 +25,10 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum ApplicationError: Error {
|
||||
enum ApplicationError: String, Error {
|
||||
case missingProfile
|
||||
|
||||
case missingCredentials
|
||||
|
||||
case missingConfiguration(option: String)
|
||||
|
||||
case unsupportedConfiguration(option: String)
|
||||
|
||||
case migration
|
||||
}
|
||||
|
|
|
@ -31,11 +31,11 @@ protocol EndpointDataSource {
|
|||
|
||||
var addresses: [String] { get }
|
||||
|
||||
var protocols: [TunnelKitProvider.EndpointProtocol] { get }
|
||||
var protocols: [EndpointProtocol] { get }
|
||||
|
||||
var canCustomizeEndpoint: Bool { get }
|
||||
|
||||
var customAddress: String? { get }
|
||||
|
||||
var customProtocol: TunnelKitProvider.EndpointProtocol? { get }
|
||||
var customProtocol: EndpointProtocol? { get }
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ extension HostConnectionProfile {
|
|||
return [hostname]
|
||||
}
|
||||
|
||||
var protocols: [TunnelKitProvider.EndpointProtocol] {
|
||||
var protocols: [EndpointProtocol] {
|
||||
return parameters.endpointProtocols
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ extension HostConnectionProfile {
|
|||
return nil
|
||||
}
|
||||
|
||||
var customProtocol: TunnelKitProvider.EndpointProtocol? {
|
||||
var customProtocol: EndpointProtocol? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,13 +47,13 @@ class PlaceholderConnectionProfile: ConnectionProfile {
|
|||
|
||||
var addresses: [String] = []
|
||||
|
||||
var protocols: [TunnelKitProvider.EndpointProtocol] = []
|
||||
var protocols: [EndpointProtocol] = []
|
||||
|
||||
var canCustomizeEndpoint: Bool = false
|
||||
|
||||
var customAddress: String?
|
||||
|
||||
var customProtocol: TunnelKitProvider.EndpointProtocol?
|
||||
var customProtocol: EndpointProtocol?
|
||||
|
||||
init(_ context: Context, _ id: String) {
|
||||
self.context = context
|
||||
|
|
|
@ -55,7 +55,7 @@ class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
|
|||
|
||||
var manualAddress: String?
|
||||
|
||||
var manualProtocol: TunnelKitProvider.EndpointProtocol?
|
||||
var manualProtocol: EndpointProtocol?
|
||||
|
||||
var usesProviderEndpoint: Bool {
|
||||
return (manualAddress != nil) || (manualProtocol != nil)
|
||||
|
@ -133,8 +133,8 @@ class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
|
|||
} else {
|
||||
builder.endpointProtocols = preset.configuration.endpointProtocols
|
||||
// builder.endpointProtocols = [
|
||||
// TunnelKitProvider.EndpointProtocol(.udp, 8080),
|
||||
// TunnelKitProvider.EndpointProtocol(.tcp, 443)
|
||||
// EndpointProtocol(.udp, 8080),
|
||||
// EndpointProtocol(.tcp, 443)
|
||||
// ]
|
||||
}
|
||||
return builder.build()
|
||||
|
@ -161,7 +161,7 @@ extension ProviderConnectionProfile {
|
|||
return pool?.addresses(sorted: true) ?? []
|
||||
}
|
||||
|
||||
var protocols: [TunnelKitProvider.EndpointProtocol] {
|
||||
var protocols: [EndpointProtocol] {
|
||||
return preset?.configuration.endpointProtocols ?? []
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ extension ProviderConnectionProfile {
|
|||
return manualAddress
|
||||
}
|
||||
|
||||
var customProtocol: TunnelKitProvider.EndpointProtocol? {
|
||||
var customProtocol: EndpointProtocol? {
|
||||
return manualProtocol
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ struct InfrastructurePreset: Codable {
|
|||
|
||||
let configuration: TunnelKitProvider.Configuration
|
||||
|
||||
func hasProtocol(_ proto: TunnelKitProvider.EndpointProtocol) -> Bool {
|
||||
func hasProtocol(_ proto: EndpointProtocol) -> Bool {
|
||||
return configuration.endpointProtocols.index(of: proto) != nil
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ struct InfrastructurePreset: Codable {
|
|||
sessionBuilder.usesPIAPatches = try cfgContainer.decodeIfPresent(Bool.self, forKey: .usesPIAPatches) ?? false
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
static let filename: CharacterSet = {
|
||||
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() {
|
||||
let original: [TunnelKitProvider.EndpointProtocol] = [
|
||||
TunnelKitProvider.EndpointProtocol(.udp, 1194),
|
||||
TunnelKitProvider.EndpointProtocol(.udp, 8080),
|
||||
TunnelKitProvider.EndpointProtocol(.udp, 9201),
|
||||
TunnelKitProvider.EndpointProtocol(.udp, 53),
|
||||
TunnelKitProvider.EndpointProtocol(.udp, 1197),
|
||||
TunnelKitProvider.EndpointProtocol(.udp, 198),
|
||||
TunnelKitProvider.EndpointProtocol(.tcp, 443),
|
||||
TunnelKitProvider.EndpointProtocol(.tcp, 110),
|
||||
TunnelKitProvider.EndpointProtocol(.tcp, 80),
|
||||
TunnelKitProvider.EndpointProtocol(.tcp, 500),
|
||||
TunnelKitProvider.EndpointProtocol(.tcp, 501),
|
||||
TunnelKitProvider.EndpointProtocol(.tcp, 502)
|
||||
let original: [EndpointProtocol] = [
|
||||
EndpointProtocol(.udp, 1194),
|
||||
EndpointProtocol(.udp, 8080),
|
||||
EndpointProtocol(.udp, 9201),
|
||||
EndpointProtocol(.udp, 53),
|
||||
EndpointProtocol(.udp, 1197),
|
||||
EndpointProtocol(.udp, 198),
|
||||
EndpointProtocol(.tcp, 443),
|
||||
EndpointProtocol(.tcp, 110),
|
||||
EndpointProtocol(.tcp, 80),
|
||||
EndpointProtocol(.tcp, 500),
|
||||
EndpointProtocol(.tcp, 501),
|
||||
EndpointProtocol(.tcp, 502)
|
||||
]
|
||||
var preferredType: TunnelKitProvider.SocketType
|
||||
var preferredType: SocketType
|
||||
|
||||
preferredType = .udp
|
||||
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!
|
||||
|
||||
def shared_pods
|
||||
#pod 'TunnelKit', '~> 1.3.0'
|
||||
pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'caea662'
|
||||
#pod 'TunnelKit', '~> 1.3.1'
|
||||
pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'ac9ce0d'
|
||||
#pod 'TunnelKit', :path => '../../personal/tunnelkit'
|
||||
end
|
||||
|
||||
|
|
20
Podfile.lock
20
Podfile.lock
|
@ -2,19 +2,19 @@ PODS:
|
|||
- MBProgressHUD (1.1.0)
|
||||
- OpenSSL-Apple (1.1.0i-v2)
|
||||
- SwiftyBeaver (1.6.1)
|
||||
- TunnelKit (1.3.1):
|
||||
- TunnelKit/AppExtension (= 1.3.1)
|
||||
- TunnelKit/Core (= 1.3.1)
|
||||
- TunnelKit/AppExtension (1.3.1):
|
||||
- TunnelKit (1.4.0):
|
||||
- TunnelKit/AppExtension (= 1.4.0)
|
||||
- TunnelKit/Core (= 1.4.0)
|
||||
- TunnelKit/AppExtension (1.4.0):
|
||||
- SwiftyBeaver
|
||||
- TunnelKit/Core
|
||||
- TunnelKit/Core (1.3.1):
|
||||
- TunnelKit/Core (1.4.0):
|
||||
- OpenSSL-Apple (~> 1.1.0h)
|
||||
- SwiftyBeaver
|
||||
|
||||
DEPENDENCIES:
|
||||
- MBProgressHUD
|
||||
- TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `caea662`)
|
||||
- TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `ac9ce0d`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
|
@ -24,20 +24,20 @@ SPEC REPOS:
|
|||
|
||||
EXTERNAL SOURCES:
|
||||
TunnelKit:
|
||||
:commit: caea662
|
||||
:commit: ac9ce0d
|
||||
:git: https://github.com/keeshux/tunnelkit
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
TunnelKit:
|
||||
:commit: caea662
|
||||
:commit: ac9ce0d
|
||||
:git: https://github.com/keeshux/tunnelkit
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
|
||||
OpenSSL-Apple: a93b8f2eec8783ff40d9a9304de180ab68bb647c
|
||||
SwiftyBeaver: ccfcdf85a04d429f1633f668650b0ce8020bda3a
|
||||
TunnelKit: f98fb7d88642eda94c42007dbc501903c469a891
|
||||
TunnelKit: 8ac6ca743020b62e63bea66b3d169c53666fb737
|
||||
|
||||
PODFILE CHECKSUM: a86923e57746e09e04296a49bdc0ad3520fd700f
|
||||
PODFILE CHECKSUM: 07c9d064a6f7b053986d305449fa44258029bb35
|
||||
|
||||
COCOAPODS: 1.6.0.beta.2
|
||||
|
|
24
README.md
24
README.md
|
@ -3,7 +3,7 @@
|
|||
# [Passepartout][about-website]
|
||||
|
||||
![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)
|
||||
[![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]
|
||||
|
@ -63,26 +63,9 @@ In preset mode, you can pick pre-resolved IPv4 endpoints when DNS is problematic
|
|||
|
||||
### 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:
|
||||
|
||||
- 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.
|
||||
You can find details on what may or may not work in the related section of the [TunnelKit README][dep-tunnelkit-ovpn].
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -152,6 +135,7 @@ Website: [passepartoutvpn.app][about-website]
|
|||
[dep-jazzy]: https://github.com/realm/jazzy
|
||||
[dep-brew]: https://brew.sh/
|
||||
[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/
|
||||
|
||||
[license-content]: LICENSE
|
||||
|
|
Loading…
Reference in New Issue