mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-01-18 14:39:09 +00:00
Merge pull request #77 from passepartoutvpn/override-network-settings
Override network settings
This commit is contained in:
commit
98756438b1
@ -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
|
||||
|
@ -233,6 +233,7 @@
|
||||
<segue destination="NJT-2N-LTa" kind="show" identifier="HostParametersSegueIdentifier" id="aL8-H6-znA"/>
|
||||
<segue destination="9Kr-G1-asf" kind="show" identifier="ProviderPresetSegueIdentifier" id="kGf-Yu-KdY"/>
|
||||
<segue destination="KmS-dJ-DVx" kind="show" identifier="DebugLogSegueIdentifier" id="XyI-by-AhD"/>
|
||||
<segue destination="BCT-c7-ovS" kind="show" identifier="NetworkSettingsSegueIdentifier" id="5nM-PM-q9h"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
@ -384,6 +385,84 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1622" y="-270"/>
|
||||
</scene>
|
||||
<!--Network Settings View Controller-->
|
||||
<scene sceneID="BPW-VO-Hgb">
|
||||
<objects>
|
||||
<tableViewController id="BCT-c7-ovS" customClass="NetworkSettingsViewController" customModule="Passepartout" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="qQL-jO-j0W">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="SettingTableViewCell" textLabel="NYG-Gq-hRp" detailTextLabel="iDZ-BL-Un5" style="IBUITableViewCellStyleValue1" id="KTK-qM-Z3I" customClass="SettingTableViewCell" customModule="Passepartout" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="KTK-qM-Z3I" id="pZZ-fb-3Rb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="NYG-Gq-hRp">
|
||||
<rect key="frame" x="16" y="12" width="33.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="iDZ-BL-Un5">
|
||||
<rect key="frame" x="315" y="12" width="44" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="ToggleTableViewCell" textLabel="chG-HK-4WY" style="IBUITableViewCellStyleDefault" id="JNm-q5-qBn" customClass="ToggleTableViewCell" customModule="Passepartout" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JNm-q5-qBn" id="NG6-Jk-qkq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="chG-HK-4WY">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="FieldTableViewCell" textLabel="dXB-Id-HH0" style="IBUITableViewCellStyleDefault" id="3Zq-6c-3Yc" customClass="FieldTableViewCell" customModule="Passepartout" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="143.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3Zq-6c-3Yc" id="h7B-7Q-NQr">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dXB-Id-HH0">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="BCT-c7-ovS" id="b8X-oR-HW4"/>
|
||||
<outlet property="delegate" destination="BCT-c7-ovS" id="L7V-gR-mRb"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="MWy-o4-Doa" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-1098" y="1042"/>
|
||||
</scene>
|
||||
<!--Debug Log View Controller-->
|
||||
<scene sceneID="Icu-BA-Eze">
|
||||
<objects>
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class ConfigurationViewController: UIViewController, TableModelHost {
|
||||
}
|
||||
model.add(.tls)
|
||||
model.add(.compression)
|
||||
model.add(.network)
|
||||
// model.add(.network)
|
||||
model.add(.other)
|
||||
|
||||
// headers
|
||||
|
610
Passepartout-iOS/Scenes/NetworkSettingsViewController.swift
Normal file
610
Passepartout-iOS/Scenes/NetworkSettingsViewController.swift
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
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<SectionType, RowType> = 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<NetworkChoice>()
|
||||
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<NetworkChoice>()
|
||||
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<NetworkChoice>()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 = "<group>"; };
|
||||
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = "<group>"; };
|
||||
0EF5CF242141CE58004FF1BD /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; };
|
||||
0EFB901722764689006405E4 /* ProfileNetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNetworkSettings.swift; sourceTree = "<group>"; };
|
||||
0EFB90192276D7F1006405E4 /* NetworkSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingsViewController.swift; sourceTree = "<group>"; };
|
||||
0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = "<group>"; };
|
||||
0EFD943D215BE10800529B64 /* IssueReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueReporter.swift; sourceTree = "<group>"; };
|
||||
0EFD943F215BED8E00529B64 /* LabelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelViewController.swift; sourceTree = "<group>"; };
|
||||
@ -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 */,
|
||||
|
@ -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.";
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
168
Passepartout/Sources/Model/ProfileNetworkSettings.swift
Normal file
168
Passepartout/Sources/Model/ProfileNetworkSettings.swift
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
8
Podfile
8
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'
|
||||
|
31
Podfile.lock
31
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
|
||||
|
@ -43,6 +43,10 @@ Passepartout strives for transparency, by showing a fairly detailed yet understa
|
||||
|
||||
[<img src="res/snap-parameters.png" width="300">](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.
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user