Merge branch 'use-ovpn-parser-from-tunnelkit'

This commit is contained in:
Davide De Rosa 2018-11-12 11:03:11 +01:00
commit 38db06d2ea
23 changed files with 95 additions and 696 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 {
static let filename: CharacterSet = {
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() {
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 {

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!
def shared_pods
#pod 'TunnelKit', '~> 1.3.0'
pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'caea662'
pod 'TunnelKit', '~> 1.4.0'
#pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'ac9ce0d'
#pod 'TunnelKit', :path => '../../personal/tunnelkit'
end

View File

@ -2,42 +2,33 @@ 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 (~> 1.4.0)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- MBProgressHUD
- OpenSSL-Apple
- SwiftyBeaver
EXTERNAL SOURCES:
TunnelKit:
:commit: caea662
:git: https://github.com/keeshux/tunnelkit
CHECKOUT OPTIONS:
TunnelKit:
:commit: caea662
:git: https://github.com/keeshux/tunnelkit
- TunnelKit
SPEC CHECKSUMS:
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
OpenSSL-Apple: a93b8f2eec8783ff40d9a9304de180ab68bb647c
SwiftyBeaver: ccfcdf85a04d429f1633f668650b0ce8020bda3a
TunnelKit: f98fb7d88642eda94c42007dbc501903c469a891
TunnelKit: 8ac6ca743020b62e63bea66b3d169c53666fb737
PODFILE CHECKSUM: a86923e57746e09e04296a49bdc0ad3520fd700f
PODFILE CHECKSUM: 286e244242c593f64bdb564cfa8a8048df3c956b
COCOAPODS: 1.6.0.beta.2

View File

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