diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8fe5777..bff53cc0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## Unreleased
+
+### Added
+
+- Override network settings. [#77](https://github.com/passepartoutvpn/passepartout-ios/pull/77)
+
## 1.6.0 (2019-05-01)
### Added
diff --git a/Passepartout-iOS/Base.lproj/Main.storyboard b/Passepartout-iOS/Base.lproj/Main.storyboard
index f9056474..c8807375 100644
--- a/Passepartout-iOS/Base.lproj/Main.storyboard
+++ b/Passepartout-iOS/Base.lproj/Main.storyboard
@@ -233,6 +233,7 @@
+
@@ -384,6 +385,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Passepartout-iOS/Global/SwiftGen+Segues.swift b/Passepartout-iOS/Global/SwiftGen+Segues.swift
index a7163d8a..eea1a797 100644
--- a/Passepartout-iOS/Global/SwiftGen+Segues.swift
+++ b/Passepartout-iOS/Global/SwiftGen+Segues.swift
@@ -21,6 +21,7 @@ internal enum StoryboardSegue {
case debugLogSegueIdentifier = "DebugLogSegueIdentifier"
case endpointSegueIdentifier = "EndpointSegueIdentifier"
case hostParametersSegueIdentifier = "HostParametersSegueIdentifier"
+ case networkSettingsSegueIdentifier = "NetworkSettingsSegueIdentifier"
case providerPoolSegueIdentifier = "ProviderPoolSegueIdentifier"
case providerPresetSegueIdentifier = "ProviderPresetSegueIdentifier"
}
diff --git a/Passepartout-iOS/Scenes/ConfigurationViewController.swift b/Passepartout-iOS/Scenes/ConfigurationViewController.swift
index ec2e5316..2eb8460a 100644
--- a/Passepartout-iOS/Scenes/ConfigurationViewController.swift
+++ b/Passepartout-iOS/Scenes/ConfigurationViewController.swift
@@ -59,7 +59,7 @@ class ConfigurationViewController: UIViewController, TableModelHost {
}
model.add(.tls)
model.add(.compression)
- model.add(.network)
+// model.add(.network)
model.add(.other)
// headers
diff --git a/Passepartout-iOS/Scenes/NetworkSettingsViewController.swift b/Passepartout-iOS/Scenes/NetworkSettingsViewController.swift
new file mode 100644
index 00000000..2dee145c
--- /dev/null
+++ b/Passepartout-iOS/Scenes/NetworkSettingsViewController.swift
@@ -0,0 +1,610 @@
+//
+// NetworkSettingsViewController.swift
+// Passepartout-iOS
+//
+// Created by Davide De Rosa on 4/29/19.
+// Copyright (c) 2019 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 .
+//
+
+import UIKit
+import Passepartout_Core
+import TunnelKit
+import SwiftyBeaver
+
+private let log = SwiftyBeaver.self
+
+private enum FieldTag: Int {
+ case dnsDomain = 101
+
+ case dnsAddress = 200
+
+ case proxyAddress = 301
+
+ case proxyPort = 302
+
+ case proxyBypass = 400
+}
+
+private struct Offsets {
+ static let dnsAddress = 1
+
+ static let proxyBypass = 2
+}
+
+// FIXME: init networkSettings with HOST profile.sessionConfiguration
+// FIXME: omit "Client" for PROVIDER
+
+class NetworkSettingsViewController: UITableViewController {
+ var profile: ConnectionProfile?
+
+ private lazy var networkChoices: ProfileNetworkChoices = {
+ if let choices = profile?.networkChoices {
+ return choices
+ }
+ if let _ = profile as? ProviderConnectionProfile {
+ return ProfileNetworkChoices(choice: .server)
+ }
+ return ProfileNetworkChoices(choice: .client)
+ }()
+
+ private let networkSettings = ProfileNetworkSettings()
+
+ private lazy var clientNetworkSettings: ProfileNetworkSettings? = {
+ guard let hostProfile = profile as? HostConnectionProfile else {
+ return nil
+ }
+ return ProfileNetworkSettings(from: hostProfile.parameters.sessionConfiguration)
+ }()
+
+ private var choices: [NetworkChoice] {
+ guard let _ = clientNetworkSettings else {
+ return [.server, .manual]
+ }
+ return [.client, .server, .manual]
+ }
+
+ // MARK: TableModelHost
+
+ let model: TableModel = TableModel()
+
+ func reloadModel() {
+ model.clear()
+
+ // sections
+ model.add(.choices)
+ if networkChoices.gateway != .server {
+ model.add(.manualGateway)
+ }
+ if networkChoices.dns != .server {
+ model.add(.manualDNS)
+ }
+ if networkChoices.proxy != .server {
+ model.add(.manualProxy)
+ }
+
+ // headers
+ model.setHeader("", for: .choices)
+ model.setHeader(L10n.Configuration.Cells.DefaultGateway.caption, for: .manualGateway)
+ model.setHeader(L10n.Configuration.Cells.DnsServer.caption, for: .manualDNS)
+ model.setHeader(L10n.Configuration.Cells.ProxyHttp.caption, for: .manualProxy)
+
+ // footers
+// model.setFooter(L10n.Configuration.Sections.Reset.footer, for: .reset)
+
+ // rows
+ model.set([.gateway, .dns, .proxy], in: .choices)
+ model.set([.gatewayIPv4, .gatewayIPv6], in: .manualGateway)
+
+ var dnsRows: [RowType] = Array(repeating: .dnsAddress, count: networkSettings.dnsServers?.count ?? 0)
+ dnsRows.insert(.dnsDomain, at: 0)
+ if networkChoices.dns == .manual {
+ dnsRows.append(.dnsAddAddress)
+ }
+ model.set(dnsRows, in: .manualDNS)
+
+ var proxyRows: [RowType] = Array(repeating: .proxyBypass, count: networkSettings.proxyBypassDomains?.count ?? 0)
+ proxyRows.insert(.proxyAddress, at: 0)
+ proxyRows.insert(.proxyPort, at: 1)
+ if networkChoices.proxy == .manual {
+ proxyRows.append(.proxyAddBypass)
+ }
+ model.set(proxyRows, in: .manualProxy)
+ }
+
+ // MARK: UIViewController
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ updateGateway(networkChoices.gateway)
+ updateDNS(networkChoices.dns)
+ updateProxy(networkChoices.proxy)
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ reloadModel()
+ tableView.reloadData()
+ }
+
+ override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+
+ profile?.networkChoices = networkChoices
+ if networkChoices.gateway == .manual {
+ let settings = profile?.manualNetworkSettings ?? ProfileNetworkSettings()
+ settings.copyGateway(from: networkSettings)
+ profile?.manualNetworkSettings = settings
+ }
+ if networkChoices.dns == .manual {
+ let settings = profile?.manualNetworkSettings ?? ProfileNetworkSettings()
+ settings.copyDNS(from: networkSettings)
+ profile?.manualNetworkSettings = settings
+ }
+ if networkChoices.proxy == .manual {
+ let settings = profile?.manualNetworkSettings ?? ProfileNetworkSettings()
+ settings.copyProxy(from: networkSettings)
+ profile?.manualNetworkSettings = settings
+ }
+ }
+
+ // MARK: Actions
+
+ private func updateGateway(_ choice: NetworkChoice) {
+ networkChoices.gateway = choice
+ switch networkChoices.gateway {
+ case .client:
+ if let settings = clientNetworkSettings {
+ networkSettings.copyGateway(from: settings)
+ }
+
+ case .server:
+ break
+
+ case .manual:
+ if let settings = profile?.manualNetworkSettings {
+ networkSettings.copyGateway(from: settings)
+ }
+ }
+ }
+
+ private func updateDNS(_ choice: NetworkChoice) {
+ networkChoices.dns = choice
+ switch networkChoices.dns {
+ case .client:
+ if let settings = clientNetworkSettings {
+ networkSettings.copyDNS(from: settings)
+ }
+
+ case .server:
+ break
+
+ case .manual:
+ if let settings = profile?.manualNetworkSettings {
+ networkSettings.copyDNS(from: settings)
+ }
+ }
+ }
+
+ private func updateProxy(_ choice: NetworkChoice) {
+ networkChoices.proxy = choice
+ switch networkChoices.proxy {
+ case .client:
+ if let settings = clientNetworkSettings {
+ networkSettings.copyProxy(from: settings)
+ }
+
+ case .server:
+ break
+
+ case .manual:
+ if let settings = profile?.manualNetworkSettings {
+ networkSettings.copyProxy(from: settings)
+ }
+ }
+ }
+
+ private func commitTextField(_ field: UITextField) {
+
+ // DNS: domain, servers
+ // Proxy: address, port, bypass domains
+
+ if field.tag == FieldTag.dnsDomain.rawValue {
+ networkSettings.dnsDomainName = field.text
+ } else if field.tag == FieldTag.proxyAddress.rawValue {
+ networkSettings.proxyAddress = field.text
+ } else if field.tag == FieldTag.proxyPort.rawValue {
+ networkSettings.proxyPort = UInt16(field.text ?? "0")
+ } else if field.tag >= FieldTag.dnsAddress.rawValue && field.tag < FieldTag.proxyAddress.rawValue {
+ let i = field.tag - FieldTag.dnsAddress.rawValue
+ networkSettings.dnsServers?[i] = field.text ?? ""
+ } else if field.tag >= FieldTag.proxyBypass.rawValue {
+ let i = field.tag - FieldTag.proxyBypass.rawValue
+ networkSettings.proxyBypassDomains?[i] = field.text ?? ""
+ }
+
+ log.debug("Network settings: \(networkSettings)")
+ }
+}
+
+// MARK: -
+
+extension NetworkSettingsViewController {
+ enum SectionType: Int {
+ case choices
+
+ case manualGateway
+
+ case manualDNS
+
+ case manualProxy
+ }
+
+ enum RowType: Int {
+ case gateway
+
+ case dns
+
+ case proxy
+
+ case gatewayIPv4
+
+ case gatewayIPv6
+
+ case dnsDomain
+
+ case dnsAddress
+
+ case dnsAddAddress
+
+ case proxyAddress
+
+ case proxyPort
+
+ case proxyBypass
+
+ case proxyAddBypass
+ }
+
+ override func numberOfSections(in tableView: UITableView) -> Int {
+ return model.count
+ }
+
+ override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
+ return model.header(for: section)
+ }
+
+ override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
+ return model.footer(for: section)
+ }
+
+ override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+ return model.headerHeight(for: section)
+ }
+
+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return model.count(for: section)
+ }
+
+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let row = model.row(at: indexPath)
+
+ switch row {
+ case .gateway:
+ let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
+ cell.leftText = model.header(for: .manualGateway)
+ cell.rightText = networkChoices.gateway.description
+ return cell
+
+ case .dns:
+ let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
+ cell.leftText = model.header(for: .manualDNS)
+ cell.rightText = networkChoices.dns.description
+ return cell
+
+ case .proxy:
+ let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
+ cell.leftText = model.header(for: .manualProxy)
+ cell.rightText = networkChoices.proxy.description
+ return cell
+
+ case .gatewayIPv4:
+ let cell = Cells.toggle.dequeue(from: tableView, for: indexPath, tag: row.rawValue, delegate: self)
+ cell.caption = "IPv4"
+ cell.toggle.isEnabled = (networkChoices.gateway == .manual)
+ cell.isOn = networkSettings.gatewayPolicies?.contains(.IPv4) ?? false
+ return cell
+
+ case .gatewayIPv6:
+ let cell = Cells.toggle.dequeue(from: tableView, for: indexPath, tag: row.rawValue, delegate: self)
+ cell.caption = "IPv6"
+ cell.toggle.isEnabled = (networkChoices.gateway == .manual)
+ cell.isOn = networkSettings.gatewayPolicies?.contains(.IPv6) ?? false
+ return cell
+
+ case .dnsDomain:
+ let cell = Cells.field.dequeue(from: tableView, for: indexPath)
+ cell.caption = L10n.Configuration.Cells.DnsDomain.caption
+ cell.field.tag = FieldTag.dnsDomain.rawValue
+ cell.field.text = networkSettings.dnsDomainName
+ cell.field.clearButtonMode = .always
+ cell.field.keyboardType = .asciiCapable
+ cell.captionWidth = 160.0
+ cell.delegate = self
+ if networkChoices.dns == .manual {
+ cell.field.isEnabled = true
+ cell.field.placeholder = L10n.Global.Cells.none
+ } else {
+ cell.field.isEnabled = false
+ cell.field.placeholder = nil
+ }
+ return cell
+
+ case .dnsAddress:
+ let i = indexPath.row - Offsets.dnsAddress
+
+ let cell = Cells.field.dequeue(from: tableView, for: indexPath)
+ cell.caption = L10n.NetworkSettings.Cells.Address.caption
+ cell.field.tag = FieldTag.dnsAddress.rawValue + i
+ cell.field.text = networkSettings.dnsServers?[i]
+ cell.field.clearButtonMode = .always
+ cell.field.keyboardType = .decimalPad
+ cell.captionWidth = 160.0
+ cell.delegate = self
+ if networkChoices.dns == .manual {
+ cell.field.isEnabled = true
+ cell.field.placeholder = L10n.Global.Cells.none
+ } else {
+ cell.field.isEnabled = false
+ cell.field.placeholder = nil
+ }
+ return cell
+
+ case .dnsAddAddress:
+ let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
+ cell.applyAction(Theme.current)
+ cell.leftText = L10n.NetworkSettings.Cells.AddDnsServer.caption
+ return cell
+
+ case .proxyAddress:
+ let cell = Cells.field.dequeue(from: tableView, for: indexPath)
+ cell.caption = L10n.NetworkSettings.Cells.Address.caption
+ cell.field.tag = FieldTag.proxyAddress.rawValue
+ cell.field.text = networkSettings.proxyAddress
+ cell.field.clearButtonMode = .always
+ cell.field.keyboardType = .decimalPad
+ cell.captionWidth = 160.0
+ cell.delegate = self
+ if networkChoices.proxy == .manual {
+ cell.field.isEnabled = true
+ cell.field.placeholder = L10n.Global.Cells.none
+ } else {
+ cell.field.isEnabled = false
+ cell.field.placeholder = nil
+ }
+ return cell
+
+ case .proxyPort:
+ let cell = Cells.field.dequeue(from: tableView, for: indexPath)
+ cell.caption = L10n.NetworkSettings.Cells.Port.caption
+ cell.field.tag = FieldTag.proxyPort.rawValue
+ cell.field.text = networkSettings.proxyPort?.description
+ cell.field.clearButtonMode = .always
+ cell.field.keyboardType = .numberPad
+ cell.captionWidth = 160.0
+ cell.delegate = self
+ if networkChoices.proxy == .manual {
+ cell.field.isEnabled = true
+ cell.field.placeholder = L10n.Global.Cells.none
+ } else {
+ cell.field.isEnabled = false
+ cell.field.placeholder = nil
+ }
+ return cell
+
+ case .proxyBypass:
+ let i = indexPath.row - Offsets.proxyBypass
+
+ let cell = Cells.field.dequeue(from: tableView, for: indexPath)
+ cell.caption = L10n.NetworkSettings.Cells.ProxyBypass.caption
+ cell.field.tag = FieldTag.proxyBypass.rawValue + i
+ cell.field.text = networkSettings.proxyBypassDomains?[i]
+ cell.field.clearButtonMode = .always
+ cell.field.keyboardType = .asciiCapable
+ cell.captionWidth = 160.0
+ cell.delegate = self
+ if networkChoices.proxy == .manual {
+ cell.field.isEnabled = true
+ cell.field.placeholder = L10n.Global.Cells.none
+ } else {
+ cell.field.isEnabled = false
+ cell.field.placeholder = nil
+ }
+ return cell
+
+ case .proxyAddBypass:
+ let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
+ cell.applyAction(Theme.current)
+ cell.leftText = L10n.NetworkSettings.Cells.AddProxyBypass.caption
+ return cell
+ }
+ }
+
+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ let cell = tableView.cellForRow(at: indexPath)
+
+ switch model.row(at: indexPath) {
+ case .gateway:
+ let vc = OptionViewController()
+ vc.title = (cell as? SettingTableViewCell)?.leftText
+ vc.options = choices
+ vc.descriptionBlock = { $0.description }
+
+ vc.selectedOption = networkChoices.gateway
+ vc.selectionBlock = { [weak self] in
+ self?.updateGateway($0)
+ self?.navigationController?.popViewController(animated: true)
+ }
+ navigationController?.pushViewController(vc, animated: true)
+
+ case .dns:
+ let vc = OptionViewController()
+ vc.title = (cell as? SettingTableViewCell)?.leftText
+ vc.options = choices
+ vc.descriptionBlock = { $0.description }
+
+ vc.selectedOption = networkChoices.dns
+ vc.selectionBlock = { [weak self] in
+ self?.updateDNS($0)
+ self?.navigationController?.popViewController(animated: true)
+ }
+ navigationController?.pushViewController(vc, animated: true)
+
+ case .proxy:
+ let vc = OptionViewController()
+ vc.title = (cell as? SettingTableViewCell)?.leftText
+ vc.options = choices
+ vc.descriptionBlock = { $0.description }
+
+ vc.selectedOption = networkChoices.proxy
+ vc.selectionBlock = { [weak self] in
+ self?.updateProxy($0)
+ self?.navigationController?.popViewController(animated: true)
+ }
+ navigationController?.pushViewController(vc, animated: true)
+
+ case .dnsAddAddress:
+ tableView.deselectRow(at: indexPath, animated: true)
+
+ var dnsServers = networkSettings.dnsServers ?? []
+ dnsServers.append("")
+ networkSettings.dnsServers = dnsServers
+ reloadModel()
+ tableView.insertRows(at: [indexPath], with: .automatic)
+
+ case .proxyAddBypass:
+ tableView.deselectRow(at: indexPath, animated: true)
+
+ var bypassDomains = networkSettings.proxyBypassDomains ?? []
+ bypassDomains.append("")
+ networkSettings.proxyBypassDomains = bypassDomains
+ reloadModel()
+ tableView.insertRows(at: [indexPath], with: .automatic)
+
+ default:
+ break
+ }
+ }
+
+ private func handle(row: RowType, cell: ToggleTableViewCell) {
+ switch row {
+ case .gatewayIPv4:
+ guard networkChoices.gateway == .manual else {
+ return
+ }
+ var policies = networkSettings.gatewayPolicies ?? []
+ if cell.toggle.isOn {
+ policies.append(.IPv4)
+ } else {
+ policies.removeAll { $0 == .IPv4 }
+ }
+ policies.sort { $0.rawValue < $1.rawValue }
+ networkSettings.gatewayPolicies = policies
+
+ case .gatewayIPv6:
+ guard networkChoices.gateway == .manual else {
+ return
+ }
+ var policies = networkSettings.gatewayPolicies ?? []
+ if cell.toggle.isOn {
+ policies.append(.IPv6)
+ } else {
+ policies.removeAll { $0 == .IPv6 }
+ }
+ policies.sort { $0.rawValue < $1.rawValue }
+ networkSettings.gatewayPolicies = policies
+
+ default:
+ break
+ }
+ }
+
+ override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
+ switch model.row(at: indexPath) {
+ case .dnsAddress, .proxyBypass:
+ return true
+
+ default:
+ return false
+ }
+ }
+
+ override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
+ switch model.row(at: indexPath) {
+ case .dnsAddress:
+ // start at row 1
+ networkSettings.dnsServers?.remove(at: indexPath.row - Offsets.dnsAddress)
+
+ case .proxyBypass:
+ // start at row 2
+ networkSettings.proxyBypassDomains?.remove(at: indexPath.row - Offsets.proxyBypass)
+
+ default:
+ break
+ }
+
+ reloadModel()
+ tableView.deleteRows(at: [indexPath], with: .automatic)
+ }
+}
+
+extension NetworkSettingsViewController: ToggleTableViewCellDelegate {
+ func toggleCell(_ cell: ToggleTableViewCell, didToggleToValue value: Bool) {
+ guard let item = RowType(rawValue: cell.tag) else {
+ return
+ }
+ handle(row: item, cell: cell)
+ }
+}
+
+extension NetworkSettingsViewController: FieldTableViewCellDelegate {
+ func fieldCellDidEdit(_ cell: FieldTableViewCell) {
+ commitTextField(cell.field)
+ }
+
+ func fieldCellDidEnter(_: FieldTableViewCell) {
+ }
+}
+
+extension NetworkChoice: CustomStringConvertible {
+ public var description: String {
+ switch self {
+ case .client:
+ return L10n.NetworkSettings.Cells.Choice.client
+
+ case .server:
+ return L10n.NetworkSettings.Cells.Choice.server
+
+ case .manual:
+ return L10n.Global.Cells.manual
+ }
+ }
+}
diff --git a/Passepartout-iOS/Scenes/ServiceViewController.swift b/Passepartout-iOS/Scenes/ServiceViewController.swift
index ad76a344..188552d5 100644
--- a/Passepartout-iOS/Scenes/ServiceViewController.swift
+++ b/Passepartout-iOS/Scenes/ServiceViewController.swift
@@ -174,6 +174,11 @@ class ServiceViewController: UIViewController, TableModelHost {
vc?.originalConfigurationURL = service.configurationURL(for: uncheckedHostProfile)
vc?.delegate = self
+ case .networkSettingsSegueIdentifier:
+ let vc = destination as? NetworkSettingsViewController
+ vc?.title = L10n.Service.Cells.NetworkSettings.caption
+ vc?.profile = profile
+
case .debugLogSegueIdentifier:
break
}
@@ -580,6 +585,8 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
case hostParameters
+ case networkSettings
+
case vpnResolvesHostname
case vpnSurvivesSleep
@@ -720,6 +727,11 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
}
return cell
+ case .networkSettings:
+ let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
+ cell.leftText = L10n.Service.Cells.NetworkSettings.caption
+ return cell
+
// provider cells
case .providerPool:
@@ -908,6 +920,10 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
perform(segue: StoryboardSegue.Main.hostParametersSegueIdentifier, sender: cell)
return true
+ case .networkSettings:
+ perform(segue: StoryboardSegue.Main.networkSettingsSegueIdentifier, sender: cell)
+ return true
+
case .trustedAddCurrentWiFi:
if #available(iOS 12, *) {
IntentDispatcher.donateTrustCurrentNetwork()
@@ -1059,10 +1075,10 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
}
if isProvider {
model.set([.account], in: .authentication)
- model.set([.providerPool, .endpoint, .providerPreset], in: .configuration)
+ model.set([.providerPool, .endpoint, .providerPreset, .networkSettings], in: .configuration)
model.set([.providerRefresh], in: .providerInfrastructure)
} else {
- model.set([.account, .endpoint, .hostParameters], in: .configuration)
+ model.set([.account, .endpoint, .hostParameters, .networkSettings], in: .configuration)
}
if isActiveProfile {
if isProvider {
diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj
index 6779c01c..848480e2 100644
--- a/Passepartout.xcodeproj/project.pbxproj
+++ b/Passepartout.xcodeproj/project.pbxproj
@@ -110,6 +110,8 @@
0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */; };
0EF5CF252141CE58004FF1BD /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5CF242141CE58004FF1BD /* HUD.swift */; };
0EF5CF292141F31F004FF1BD /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; };
+ 0EFB901822764689006405E4 /* ProfileNetworkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */; };
+ 0EFB901A2276D7F1006405E4 /* NetworkSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */; };
0EFBFAC121AC464800887A8C /* CreditsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFBFAC021AC464800887A8C /* CreditsViewController.swift */; };
0EFD943E215BE10800529B64 /* IssueReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFD943D215BE10800529B64 /* IssueReporter.swift */; };
0EFD9440215BED8E00529B64 /* LabelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFD943F215BED8E00529B64 /* LabelViewController.swift */; };
@@ -286,6 +288,8 @@
0EEB53B1225D525B00746300 /* Downloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = ""; };
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = ""; };
0EF5CF242141CE58004FF1BD /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = ""; };
+ 0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNetworkSettings.swift; sourceTree = ""; };
+ 0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingsViewController.swift; sourceTree = ""; };
0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = ""; };
0EFD943D215BE10800529B64 /* IssueReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueReporter.swift; sourceTree = ""; };
0EFD943F215BED8E00529B64 /* LabelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelViewController.swift; sourceTree = ""; };
@@ -510,6 +514,7 @@
0EC7F20420E24308004EA58E /* DebugLog.swift */,
0ED38AE621404F100004D387 /* EndpointDataSource.swift */,
0E89DFC4213DF7AE00741BA1 /* Preferences.swift */,
+ 0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */,
0E89DFC7213E8FC500741BA1 /* SessionProxy+Communication.swift */,
0E2B494120FD16540094784C /* TransientStore.swift */,
0E4C9CB820DB9BC600A0C59C /* TrustedNetworks.swift */,
@@ -610,6 +615,7 @@
0E1D72B3213C118500BA1586 /* ConfigurationViewController.swift */,
0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */,
0E158AD920E11B0B00C85A82 /* EndpointViewController.swift */,
+ 0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */,
0ED31C2B20CF2D6F0027975F /* ProviderPoolViewController.swift */,
0E1D72B1213BFFCF00BA1586 /* ProviderPresetViewController.swift */,
0E57F63D20C83FC5008323CF /* ServiceViewController.swift */,
@@ -1047,6 +1053,7 @@
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */,
0E3152D3223FA05400F61841 /* EndpointDataSource.swift in Sources */,
0E3152D4223FA05400F61841 /* Preferences.swift in Sources */,
+ 0EFB901822764689006405E4 /* ProfileNetworkSettings.swift in Sources */,
0E3152C0223FA03D00F61841 /* Utils.swift in Sources */,
0E3152CB223FA04D00F61841 /* Pool.swift in Sources */,
0E3152C7223FA04800F61841 /* VPNStatus.swift in Sources */,
@@ -1075,6 +1082,7 @@
0E4FD7F120D58618002221FF /* Macros.swift in Sources */,
0EF5CF252141CE58004FF1BD /* HUD.swift in Sources */,
0E05C5D720D1645F006EE732 /* ToggleTableViewCell.swift in Sources */,
+ 0EFB901A2276D7F1006405E4 /* NetworkSettingsViewController.swift in Sources */,
0E05C5D420D1645F006EE732 /* FieldTableViewCell.swift in Sources */,
0E36D25C224034AD006AF062 /* ShortcutsConnectToViewController.swift in Sources */,
0E05C61D20D27C82006EE732 /* Theme.swift in Sources */,
diff --git a/Passepartout/Resources/en.lproj/Localizable.strings b/Passepartout/Resources/en.lproj/Localizable.strings
index 0a5aea00..f73c650b 100644
--- a/Passepartout/Resources/en.lproj/Localizable.strings
+++ b/Passepartout/Resources/en.lproj/Localizable.strings
@@ -109,6 +109,7 @@
"service.cells.provider.preset.caption" = "Preset";
"service.cells.provider.refresh.caption" = "Refresh infrastructure";
"service.cells.host.parameters.caption" = "Parameters";
+"service.cells.network_settings.caption" = "Network settings";
"service.cells.vpn_survives_sleep.caption" = "Keep alive on sleep";
"service.cells.vpn_resolves_hostname.caption" = "Resolve server hostname";
//"service.cells.vpn_prefers_udp.caption" = "Prefer UDP socket";
@@ -200,6 +201,14 @@
"configuration.cells.renegotiation_seconds.value.after" = "after %@";
"configuration.cells.random_endpoint.caption" = "Randomize endpoint";
+"network_settings.cells.choice.client" = "Read .ovpn";
+"network_settings.cells.choice.server" = "Pull from server";
+"network_settings.cells.address.caption" = "Address";
+"network_settings.cells.port.caption" = "Port";
+"network_settings.cells.add_dns_server.caption" = "Add address";
+"network_settings.cells.proxy_bypass.caption" = "Bypass domain";
+"network_settings.cells.add_proxy_bypass.caption" = "Add bypass domain";
+
"debug_log.buttons.previous" = "Previous";
"debug_log.buttons.next" = "Next";
"debug_log.alerts.empty_log.message" = "The debug log is empty.";
diff --git a/Passepartout/Sources/Model/ConnectionProfile.swift b/Passepartout/Sources/Model/ConnectionProfile.swift
index b0569e13..55603f27 100644
--- a/Passepartout/Sources/Model/ConnectionProfile.swift
+++ b/Passepartout/Sources/Model/ConnectionProfile.swift
@@ -42,6 +42,10 @@ public protocol ConnectionProfile: class, EndpointDataSource, CustomStringConver
var requiresCredentials: Bool { get }
+ var networkChoices: ProfileNetworkChoices? { get set }
+
+ var manualNetworkSettings: ProfileNetworkSettings? { get set }
+
func generate(from configuration: TunnelKitProvider.Configuration, preferences: Preferences) throws -> TunnelKitProvider.Configuration
func with(newId: String) -> ConnectionProfile
diff --git a/Passepartout/Sources/Model/ConnectionService.swift b/Passepartout/Sources/Model/ConnectionService.swift
index 8e6ca9a9..5afb68cf 100644
--- a/Passepartout/Sources/Model/ConnectionService.swift
+++ b/Passepartout/Sources/Model/ConnectionService.swift
@@ -299,6 +299,7 @@ public class ConnectionService: Codable {
return nil
}
}
+
return profile
}
@@ -352,7 +353,9 @@ public class ConnectionService: Codable {
// fall back to the safer option
var builder = host.parameters.builder()
- builder.sessionConfiguration.routingPolicies = [.IPv4, .IPv6]
+ var sessionBuilder = builder.sessionConfiguration.builder()
+ sessionBuilder.routingPolicies = [.IPv4, .IPv6]
+ builder.sessionConfiguration = sessionBuilder.build()
host.parameters = builder.build()
}
cache[entry.key] = host
@@ -529,7 +532,19 @@ public class ConnectionService: Codable {
}
}
- let cfg = try profile.generate(from: baseConfiguration, preferences: preferences)
+ var cfg = try profile.generate(from: baseConfiguration, preferences: preferences)
+
+ // override network settings
+ if let choices = profile.networkChoices, let settings = profile.manualNetworkSettings {
+ var builder = cfg.builder()
+ var sessionBuilder = builder.sessionConfiguration.builder()
+ sessionBuilder.applyGateway(from: choices, settings: settings)
+ sessionBuilder.applyDNS(from: choices, settings: settings)
+ sessionBuilder.applyProxy(from: choices, settings: settings)
+ builder.sessionConfiguration = sessionBuilder.build()
+ cfg = builder.build()
+ }
+
let protocolConfiguration = try cfg.generatedTunnelProtocol(
withBundleIdentifier: GroupConstants.App.tunnelIdentifier,
appGroup: appGroup,
diff --git a/Passepartout/Sources/Model/ProfileNetworkSettings.swift b/Passepartout/Sources/Model/ProfileNetworkSettings.swift
new file mode 100644
index 00000000..1a72af6d
--- /dev/null
+++ b/Passepartout/Sources/Model/ProfileNetworkSettings.swift
@@ -0,0 +1,168 @@
+//
+// ProfileNetworkSettings.swift
+// Passepartout
+//
+// Created by Davide De Rosa on 04/28/19.
+// Copyright (c) 2019 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 .
+//
+
+import Foundation
+import TunnelKit
+
+public enum NetworkChoice: String, Codable {
+ case client
+
+ case server // erase client settings
+
+ case manual
+}
+
+public class ProfileNetworkChoices: Codable {
+ public var gateway: NetworkChoice
+
+ public var dns: NetworkChoice
+
+ public var proxy: NetworkChoice
+
+ public init(choice: NetworkChoice) {
+ gateway = choice
+ dns = choice
+ proxy = choice
+ }
+}
+
+public class ProfileNetworkSettings: Codable, CustomStringConvertible {
+ public var gatewayPolicies: [SessionProxy.RoutingPolicy]?
+
+ public var dnsServers: [String]?
+
+ public var dnsDomainName: String?
+
+ public var proxyAddress: String?
+
+ public var proxyPort: UInt16?
+
+ public var proxyServer: Proxy? {
+ guard let address = proxyAddress, let port = proxyPort, !address.isEmpty, port > 0 else {
+ return nil
+ }
+ return Proxy(address, port)
+ }
+
+ public var proxyBypassDomains: [String]?
+
+ public init() {
+ gatewayPolicies = [.IPv4, .IPv6]
+ }
+
+ public init(from configuration: SessionProxy.Configuration) {
+ gatewayPolicies = configuration.routingPolicies
+ dnsDomainName = configuration.searchDomain
+ dnsServers = configuration.dnsServers
+ proxyAddress = configuration.httpProxy?.address
+ proxyPort = configuration.httpProxy?.port
+ proxyBypassDomains = configuration.proxyBypassDomains
+ }
+
+ public func copy(from settings: ProfileNetworkSettings) {
+ copyGateway(from: settings)
+ copyDNS(from: settings)
+ copyProxy(from: settings)
+ }
+
+ public func copyGateway(from settings: ProfileNetworkSettings) {
+ gatewayPolicies = settings.gatewayPolicies
+ }
+
+ public func copyDNS(from settings: ProfileNetworkSettings) {
+ dnsDomainName = settings.dnsDomainName
+ dnsServers = settings.dnsServers?.filter { !$0.isEmpty }
+ }
+
+ public func copyProxy(from settings: ProfileNetworkSettings) {
+ proxyAddress = settings.proxyAddress
+ proxyPort = settings.proxyPort
+ proxyBypassDomains = settings.proxyBypassDomains?.filter { !$0.isEmpty }
+ }
+
+ // MARK: CustomStringConvertible
+
+ public var description: String {
+ let comps: [String] = [
+ "gw: \(gatewayPolicies?.description ?? "")",
+ "dns: {domain: \(dnsDomainName ?? ""), servers: \(dnsServers?.description ?? "[]")}",
+ "proxy: {address: \(proxyAddress ?? ""), port: \(proxyPort?.description ?? ""), bypass: \(proxyBypassDomains?.description ?? "[]")}"
+ ]
+ return "{\(comps.joined(separator: ", "))}"
+ }
+}
+
+extension SessionProxy.ConfigurationBuilder {
+ public mutating func applyGateway(from choices: ProfileNetworkChoices, settings: ProfileNetworkSettings) {
+ switch choices.gateway {
+ case .client:
+ break
+
+ case .server:
+ routingPolicies = nil
+
+ case .manual:
+ routingPolicies = settings.gatewayPolicies
+ }
+ }
+
+ public mutating func applyDNS(from choices: ProfileNetworkChoices, settings: ProfileNetworkSettings) {
+ switch choices.dns {
+ case .client:
+ break
+
+ case .server:
+ dnsServers = nil
+ searchDomain = nil
+
+ case .manual:
+ dnsServers = settings.dnsServers?.filter { !$0.isEmpty }
+ searchDomain = settings.dnsDomainName
+ }
+ }
+
+ public mutating func applyProxy(from choices: ProfileNetworkChoices, settings: ProfileNetworkSettings) {
+ switch choices.proxy {
+ case .client:
+ break
+
+ case .server:
+ httpProxy = nil
+ httpsProxy = nil
+ proxyBypassDomains = nil
+
+ case .manual:
+ if let proxyServer = settings.proxyServer {
+ httpProxy = proxyServer
+ httpsProxy = proxyServer
+ proxyBypassDomains = settings.proxyBypassDomains?.filter { !$0.isEmpty }
+ } else {
+ httpProxy = nil
+ httpsProxy = nil
+ proxyBypassDomains = nil
+ }
+ }
+ }
+}
diff --git a/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift b/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift
index 9b8770ad..939f9634 100644
--- a/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift
+++ b/Passepartout/Sources/Model/Profiles/HostConnectionProfile.swift
@@ -54,6 +54,10 @@ public class HostConnectionProfile: ConnectionProfile, Codable, Equatable {
return false
}
+ public var networkChoices: ProfileNetworkChoices?
+
+ public var manualNetworkSettings: ProfileNetworkSettings?
+
public func generate(from configuration: TunnelKitProvider.Configuration, preferences: Preferences) throws -> TunnelKitProvider.Configuration {
guard let endpointProtocols = parameters.sessionConfiguration.endpointProtocols, !endpointProtocols.isEmpty else {
preconditionFailure("No endpointProtocols")
@@ -67,7 +71,9 @@ public class HostConnectionProfile: ConnectionProfile, Codable, Equatable {
builder.masksPrivateData = configuration.masksPrivateData
// forcibly override hostname with profile hostname (never nil)
- builder.sessionConfiguration.hostname = hostname
+ var sessionBuilder = builder.sessionConfiguration.builder()
+ sessionBuilder.hostname = hostname
+ builder.sessionConfiguration = sessionBuilder.build()
return builder.build()
}
diff --git a/Passepartout/Sources/Model/Profiles/PlaceholderConnectionProfile.swift b/Passepartout/Sources/Model/Profiles/PlaceholderConnectionProfile.swift
index 0b8e454f..43c267a1 100644
--- a/Passepartout/Sources/Model/Profiles/PlaceholderConnectionProfile.swift
+++ b/Passepartout/Sources/Model/Profiles/PlaceholderConnectionProfile.swift
@@ -35,6 +35,10 @@ public class PlaceholderConnectionProfile: ConnectionProfile {
public var requiresCredentials: Bool = false
+ public var networkChoices: ProfileNetworkChoices?
+
+ public var manualNetworkSettings: ProfileNetworkSettings?
+
public func generate(from configuration: TunnelKitProvider.Configuration, preferences: Preferences) throws -> TunnelKitProvider.Configuration {
fatalError("Generating configuration from a PlaceholderConnectionProfile")
}
diff --git a/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift b/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift
index ba7b0bae..b7feb08d 100644
--- a/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift
+++ b/Passepartout/Sources/Model/Profiles/ProviderConnectionProfile.swift
@@ -57,6 +57,10 @@ public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
public var manualProtocol: EndpointProtocol?
+ public var networkChoices: ProfileNetworkChoices?
+
+ public var manualNetworkSettings: ProfileNetworkSettings?
+
public var usesProviderEndpoint: Bool {
return (manualAddress != nil) || (manualProtocol != nil)
}
@@ -134,8 +138,9 @@ public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
builder.resolvedAddresses = pool.addresses()
}
+ var sessionBuilder = builder.sessionConfiguration.builder()
if let proto = manualProtocol {
- builder.sessionConfiguration.endpointProtocols = [proto]
+ sessionBuilder.endpointProtocols = [proto]
} else {
// restrict "Any" protocol to UDP, unless there are no UDP endpoints
@@ -145,12 +150,14 @@ public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
endpoints = allEndpoints
}
- builder.sessionConfiguration.endpointProtocols = endpoints
-// builder.sessionConfiguration.endpointProtocols = [
+ sessionBuilder.endpointProtocols = endpoints
+// sessionBuilder.endpointProtocols = [
// EndpointProtocol(.udp, 8080),
// EndpointProtocol(.tcp, 443)
// ]
}
+ builder.sessionConfiguration = sessionBuilder.build()
+
return builder.build()
}
diff --git a/Passepartout/Sources/SwiftGen+Strings.swift b/Passepartout/Sources/SwiftGen+Strings.swift
index 17e972d8..f6cf9903 100644
--- a/Passepartout/Sources/SwiftGen+Strings.swift
+++ b/Passepartout/Sources/SwiftGen+Strings.swift
@@ -417,6 +417,37 @@ public enum L10n {
}
}
+ public enum NetworkSettings {
+ public enum Cells {
+ public enum AddDnsServer {
+ /// Add address
+ public static let caption = L10n.tr("Localizable", "network_settings.cells.add_dns_server.caption")
+ }
+ public enum AddProxyBypass {
+ /// Add bypass domain
+ public static let caption = L10n.tr("Localizable", "network_settings.cells.add_proxy_bypass.caption")
+ }
+ public enum Address {
+ /// Address
+ public static let caption = L10n.tr("Localizable", "network_settings.cells.address.caption")
+ }
+ public enum Choice {
+ /// Read .ovpn
+ public static let client = L10n.tr("Localizable", "network_settings.cells.choice.client")
+ /// Pull from server
+ public static let server = L10n.tr("Localizable", "network_settings.cells.choice.server")
+ }
+ public enum Port {
+ /// Port
+ public static let caption = L10n.tr("Localizable", "network_settings.cells.port.caption")
+ }
+ public enum ProxyBypass {
+ /// Bypass domain
+ public static let caption = L10n.tr("Localizable", "network_settings.cells.proxy_bypass.caption")
+ }
+ }
+ }
+
public enum Organizer {
public enum Alerts {
public enum AddHost {
@@ -699,6 +730,10 @@ public enum L10n {
/// Mask network data
public static let caption = L10n.tr("Localizable", "service.cells.masks_private_data.caption")
}
+ public enum NetworkSettings {
+ /// Network settings
+ public static let caption = L10n.tr("Localizable", "service.cells.network_settings.caption")
+ }
public enum Provider {
public enum Pool {
/// Location
diff --git a/Podfile b/Podfile
index cd30af4b..93ba1f87 100644
--- a/Podfile
+++ b/Podfile
@@ -3,10 +3,10 @@ platform :ios, '11.0'
use_frameworks!
def shared_pods
- pod 'TunnelKit', '~> 1.7.0'
- pod 'TunnelKit/LZO', '~> 1.7.0'
- #pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'f799f47'
- #pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => 'f799f47'
+ #pod 'TunnelKit', '~> 1.7.0'
+ #pod 'TunnelKit/LZO', '~> 1.7.0'
+ pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => '4da64c5'
+ pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => '4da64c5'
#pod 'TunnelKit', :path => '../../personal/tunnelkit'
#pod 'TunnelKit/LZO', :path => '../../personal/tunnelkit'
pod 'SSZipArchive'
diff --git a/Podfile.lock b/Podfile.lock
index d754bc60..9cd0002e 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -3,22 +3,22 @@ PODS:
- OpenSSL-Apple (1.1.0j.2)
- SSZipArchive (2.1.4)
- SwiftyBeaver (1.7.0)
- - TunnelKit (1.7.0):
- - TunnelKit/AppExtension (= 1.7.0)
- - TunnelKit/Core (= 1.7.0)
- - TunnelKit/AppExtension (1.7.0):
+ - TunnelKit (1.7.1):
+ - TunnelKit/AppExtension (= 1.7.1)
+ - TunnelKit/Core (= 1.7.1)
+ - TunnelKit/AppExtension (1.7.1):
- SwiftyBeaver
- TunnelKit/Core
- - TunnelKit/Core (1.7.0):
+ - TunnelKit/Core (1.7.1):
- OpenSSL-Apple (~> 1.1.0j.2)
- SwiftyBeaver
- - TunnelKit/LZO (1.7.0)
+ - TunnelKit/LZO (1.7.1)
DEPENDENCIES:
- MBProgressHUD
- SSZipArchive
- - TunnelKit (~> 1.7.0)
- - TunnelKit/LZO (~> 1.7.0)
+ - TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `4da64c5`)
+ - TunnelKit/LZO (from `https://github.com/keeshux/tunnelkit`, commit `4da64c5`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
@@ -26,15 +26,24 @@ SPEC REPOS:
- OpenSSL-Apple
- SSZipArchive
- SwiftyBeaver
- - TunnelKit
+
+EXTERNAL SOURCES:
+ TunnelKit:
+ :commit: 4da64c5
+ :git: https://github.com/keeshux/tunnelkit
+
+CHECKOUT OPTIONS:
+ TunnelKit:
+ :commit: 4da64c5
+ :git: https://github.com/keeshux/tunnelkit
SPEC CHECKSUMS:
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
OpenSSL-Apple: e88e1eb314acb4a05e2348069790c4aa49f6d319
SSZipArchive: 41455d4b8d2b6ab93990820b50dc697c2554a322
SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
- TunnelKit: 7355382cd0ff4fce26e48c51dc680d0497b35b17
+ TunnelKit: dd00b33e4c6d84f2d32390448a33ef5e37b1ac52
-PODFILE CHECKSUM: 5c6c177586b156662aaed2bc5953ab15560cb112
+PODFILE CHECKSUM: 32c9f11727e184e6ffbbdf5bd88ea65f79f1f155
COCOAPODS: 1.6.1
diff --git a/README.md b/README.md
index 914387c1..176324b2 100644
--- a/README.md
+++ b/README.md
@@ -43,6 +43,10 @@ Passepartout strives for transparency, by showing a fairly detailed yet understa
[](res/snap-parameters.png)
+### Override network settings
+
+Override default gateway, DNS and proxy settings right from the app. Don't bother editing the .ovpn file or your pushed server settings. This is especially useful if you want to override your provider settings, e.g. to integrate your own DNS-based ad blocking.
+
### Disconnect on sleep
Keeping the VPN active in the background provides smoother operation, but may be tough for the battery. You might want to use this feature if you're concerned about battery life. When the device goes to sleep, the VPN will disconnect to then reconnect on device wake-up.
diff --git a/fastlane/metadata/en-US/description.txt b/fastlane/metadata/en-US/description.txt
index a2028a8a..0d71b60a 100644
--- a/fastlane/metadata/en-US/description.txt
+++ b/fastlane/metadata/en-US/description.txt
@@ -22,6 +22,7 @@ EASY TO USE
- Handle your profiles in one single place.
- Import .ovpn configuration files.
- Fine-tune encryption without tweaking any configuration file.
+- Override network settings in a snap (default gateway, DNS, proxy).
OS INTEGRATION