Merge branch 'secure-dns'

This commit is contained in:
Davide De Rosa 2021-01-26 11:23:17 +01:00
commit e1d2370564
11 changed files with 329 additions and 55 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Support `--data-ciphers` from OpenVPN 2.5 [tunnelkit#193](https://github.com/passepartoutvpn/tunnelkit/issues/193) - Support `--data-ciphers` from OpenVPN 2.5 [tunnelkit#193](https://github.com/passepartoutvpn/tunnelkit/issues/193)
- Support DNS over HTTPS/TLS in "Network settings". [#91](https://github.com/passepartoutvpn/passepartout-apple/issues/91)
### Changed ### Changed

View File

@ -32,6 +32,8 @@ import Convenience
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
private enum FieldTag: Int { private enum FieldTag: Int {
case dnsCustom = 50
case dnsAddress = 100 case dnsAddress = 100
case dnsDomain = 200 case dnsDomain = 200
@ -76,7 +78,14 @@ class NetworkSettingsViewController: UITableViewController {
sections.append(.manualGateway) sections.append(.manualGateway)
} }
if networkChoices.dns != .server { if networkChoices.dns != .server {
sections.append(.manualDNSProtocol)
switch networkSettings.dnsProtocol {
case .https:
break
default:
sections.append(.manualDNSServers) sections.append(.manualDNSServers)
}
sections.append(.manualDNSDomains) sections.append(.manualDNSDomains)
} }
if networkChoices.proxy != .server { if networkChoices.proxy != .server {
@ -100,6 +109,16 @@ class NetworkSettingsViewController: UITableViewController {
model.set([.gatewayIPv4, .gatewayIPv6], forSection: .manualGateway) model.set([.gatewayIPv4, .gatewayIPv6], forSection: .manualGateway)
model.set([.mtuBytes], forSection: .manualMTU) model.set([.mtuBytes], forSection: .manualMTU)
var dnsProtocolRows: [RowType] = [.dnsProtocol]
switch networkSettings.dnsProtocol {
case .https, .tls:
dnsProtocolRows.append(.dnsCustom)
default:
break
}
model.set(dnsProtocolRows, forSection: .manualDNSProtocol)
var dnsServers: [RowType] = Array(repeating: .dnsAddress, count: networkSettings.dnsServers?.count ?? 0) var dnsServers: [RowType] = Array(repeating: .dnsAddress, count: networkSettings.dnsServers?.count ?? 0)
if networkChoices.dns == .manual { if networkChoices.dns == .manual {
dnsServers.append(.dnsAddAddress) dnsServers.append(.dnsAddAddress)
@ -122,11 +141,10 @@ class NetworkSettingsViewController: UITableViewController {
model.set(proxyRows, forSection: .manualProxy) model.set(proxyRows, forSection: .manualProxy)
// refine sections before add (DNS is tricky) // refine sections before add (DNS is tricky)
model.setHeader(L10n.Core.NetworkSettings.Dns.title, forSection: .manualDNSProtocol)
if !dnsServers.isEmpty { if !dnsServers.isEmpty {
model.setHeader(L10n.Core.NetworkSettings.Dns.title, forSection: .manualDNSServers)
} else if !dnsDomains.isEmpty { } else if !dnsDomains.isEmpty {
sections.removeAll { $0 == .manualDNSServers } sections.removeAll { $0 == .manualDNSServers }
model.setHeader(L10n.Core.NetworkSettings.Dns.title, forSection: .manualDNSDomains)
} else { } else {
sections.removeAll { $0 == .manualDNSServers } sections.removeAll { $0 == .manualDNSServers }
sections.removeAll { $0 == .manualDNSDomains } sections.removeAll { $0 == .manualDNSDomains }
@ -241,7 +259,21 @@ class NetworkSettingsViewController: UITableViewController {
let text = field.text ?? "" let text = field.text ?? ""
if field.tag >= FieldTag.dnsAddress.rawValue && field.tag < FieldTag.dnsDomain.rawValue { if field.tag == FieldTag.dnsCustom.rawValue {
switch networkSettings.dnsProtocol {
case .https:
guard let string = field.text, let url = URL(string: string) else {
break
}
networkSettings.dnsHTTPSURL = url
case .tls:
networkSettings.dnsTLSServerName = field.text
default:
break
}
} else if field.tag >= FieldTag.dnsAddress.rawValue && field.tag < FieldTag.dnsDomain.rawValue {
let i = field.tag - FieldTag.dnsAddress.rawValue let i = field.tag - FieldTag.dnsAddress.rawValue
if let _ = networkSettings.dnsServers { if let _ = networkSettings.dnsServers {
networkSettings.dnsServers?[i] = text networkSettings.dnsServers?[i] = text
@ -304,6 +336,8 @@ extension NetworkSettingsViewController {
case manualGateway case manualGateway
case manualDNSProtocol
case manualDNSServers case manualDNSServers
case manualDNSDomains case manualDNSDomains
@ -326,6 +360,10 @@ extension NetworkSettingsViewController {
case gatewayIPv6 case gatewayIPv6
case dnsProtocol
case dnsCustom
case dnsAddress case dnsAddress
case dnsAddAddress case dnsAddAddress
@ -409,6 +447,42 @@ extension NetworkSettingsViewController {
cell.isOn = networkSettings.gatewayPolicies?.contains(.IPv6) ?? false cell.isOn = networkSettings.gatewayPolicies?.contains(.IPv6) ?? false
return cell return cell
case .dnsProtocol:
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
cell.leftText = L10n.Core.Global.Captions.protocol
cell.rightText = (networkSettings.dnsProtocol ?? .fallback)?.description
if networkChoices.dns == .manual {
cell.accessoryType = .disclosureIndicator
cell.isTappable = true
} else {
cell.accessoryType = .none
cell.isTappable = false
}
return cell
case .dnsCustom:
let cell = Cells.field.dequeue(from: tableView, for: indexPath)
cell.caption = nil
cell.field.tag = FieldTag.dnsCustom.rawValue
cell.field.isEnabled = (networkChoices.dns == .manual)
switch networkSettings.dnsProtocol {
case .https:
cell.field.placeholder = AppConstants.Placeholders.dohURL
cell.field.text = networkSettings.dnsHTTPSURL?.absoluteString
case .tls:
cell.field.placeholder = AppConstants.Placeholders.dotServerName
cell.field.text = networkSettings.dnsTLSServerName
default:
break
}
cell.field.clearButtonMode = .always
cell.field.keyboardType = .asciiCapable
cell.captionWidth = 0.0
cell.delegate = self
return cell
case .dnsAddress: case .dnsAddress:
let i = indexPath.row - Offsets.dnsAddress let i = indexPath.row - Offsets.dnsAddress
@ -579,6 +653,28 @@ extension NetworkSettingsViewController {
} }
navigationController?.pushViewController(vc, animated: true) navigationController?.pushViewController(vc, animated: true)
case .dnsProtocol:
guard networkChoices.dns == .manual else {
break
}
let vc = SingleOptionViewController<DNSProtocol>()
vc.applyTint(.current)
vc.title = (cell as? SettingTableViewCell)?.leftText
if #available(iOS 14, macOS 11, *) {
vc.options = [.plain, .https, .tls]
} else {
vc.options = [.plain]
}
vc.descriptionBlock = { $0.description }
vc.selectedOption = networkSettings.dnsProtocol ?? .fallback
vc.selectionBlock = { [weak self] in
self?.networkSettings.dnsProtocol = $0
self?.navigationController?.popViewController(animated: true)
}
navigationController?.pushViewController(vc, animated: true)
case .dnsAddAddress: case .dnsAddAddress:
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)

View File

@ -136,14 +136,14 @@
<objects> <objects>
<viewController title="DNS" id="mbI-Io-BZh" userLabel="DNS" customClass="DNSViewController" customModule="Passepartout" customModuleProvider="target" sceneMemberID="viewController"> <viewController title="DNS" id="mbI-Io-BZh" userLabel="DNS" customClass="DNSViewController" customModule="Passepartout" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="We2-9p-1Vt"> <view key="view" id="We2-9p-1Vt">
<rect key="frame" x="0.0" y="0.0" width="520" height="400"/> <rect key="frame" x="0.0" y="0.0" width="610" height="467"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Zao-dc-fzq"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="Zao-dc-fzq">
<rect key="frame" x="0.0" y="20" width="500" height="360"/> <rect key="frame" x="0.0" y="20" width="590" height="427"/>
<subviews> <subviews>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bFA-yD-Roy"> <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bFA-yD-Roy">
<rect key="frame" x="147" y="336" width="247" height="25"/> <rect key="frame" x="147" y="403" width="247" height="25"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="240" id="euy-38-pqL"/> <constraint firstAttribute="width" constant="240" id="euy-38-pqL"/>
</constraints> </constraints>
@ -163,28 +163,88 @@
</connections> </connections>
</popUpButton> </popUpButton>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Zfd-mA-qV9"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="Zfd-mA-qV9">
<rect key="frame" x="0.0" y="0.0" width="500" height="330"/> <rect key="frame" x="0.0" y="0.0" width="590" height="397"/>
<subviews> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ef3-Zx-nCS">
<rect key="frame" x="-2" y="379" width="144" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="PROTO" id="lpP-C3-tYW">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CSz-iK-Imy">
<rect key="frame" x="147" y="373" width="247" height="25"/>
<constraints>
<constraint firstAttribute="width" constant="240" id="2y9-wd-q0g"/>
</constraints>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="210-ZR-L2J" id="KD6-ly-ONG">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="Kvm-gz-uaS">
<items>
<menuItem title="Item 1" state="on" id="210-ZR-L2J"/>
<menuItem title="Item 2" id="sVf-le-KwE"/>
<menuItem title="Item 3" id="VbS-Rk-gsh"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="pickProtocol:" target="mbI-Io-BZh" id="ik0-nd-ODu"/>
</connections>
</popUpButton>
<stackView distribution="fill" orientation="vertical" alignment="leading" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VB1-6I-men">
<rect key="frame" x="150" y="0.0" width="440" height="357"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oRM-XY-fH6">
<rect key="frame" x="0.0" y="336" width="344" height="21"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="pa5-Wv-hRq">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="L6f-kb-mEG"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="L6f-kb-mEG">
<rect key="frame" x="150" y="170" width="350" height="160"/> <rect key="frame" x="0.0" y="168" width="440" height="160"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="160" id="zCq-d8-x9i"/> <constraint firstAttribute="height" constant="160" id="zCq-d8-x9i"/>
</constraints> </constraints>
</customView> </customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Baa-Z1-JJJ"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="Baa-Z1-JJJ">
<rect key="frame" x="150" y="0.0" width="350" height="160"/> <rect key="frame" x="0.0" y="0.0" width="440" height="160"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="160" id="BTk-TZ-Dat"/> <constraint firstAttribute="height" constant="160" id="BTk-TZ-Dat"/>
</constraints> </constraints>
</customView> </customView>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstAttribute="bottom" secondItem="Baa-Z1-JJJ" secondAttribute="bottom" id="DZf-vr-lkr"/> <constraint firstItem="Baa-Z1-JJJ" firstAttribute="leading" secondItem="VB1-6I-men" secondAttribute="leading" id="E4U-iU-57G"/>
<constraint firstAttribute="trailing" secondItem="Baa-Z1-JJJ" secondAttribute="trailing" id="Kc3-oq-1nC"/> <constraint firstItem="L6f-kb-mEG" firstAttribute="leading" secondItem="VB1-6I-men" secondAttribute="leading" id="U89-Q1-C6d"/>
<constraint firstItem="Baa-Z1-JJJ" firstAttribute="leading" secondItem="L6f-kb-mEG" secondAttribute="leading" id="OAq-vY-0dj"/> <constraint firstAttribute="trailing" secondItem="L6f-kb-mEG" secondAttribute="trailing" id="UN9-uj-ZRd"/>
<constraint firstAttribute="trailing" secondItem="L6f-kb-mEG" secondAttribute="trailing" id="Ufy-l9-JnV"/> <constraint firstAttribute="trailing" secondItem="Baa-Z1-JJJ" secondAttribute="trailing" id="g14-fv-nkI"/>
<constraint firstItem="L6f-kb-mEG" firstAttribute="top" secondItem="Zfd-mA-qV9" secondAttribute="top" id="ht1-Z0-GZL"/> </constraints>
<constraint firstItem="Baa-Z1-JJJ" firstAttribute="top" secondItem="L6f-kb-mEG" secondAttribute="bottom" constant="10" id="qwn-Pc-7xS"/> <visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstItem="CSz-iK-Imy" firstAttribute="top" secondItem="Zfd-mA-qV9" secondAttribute="top" id="0rT-Xa-fsr"/>
<constraint firstItem="ef3-Zx-nCS" firstAttribute="leading" secondItem="Zfd-mA-qV9" secondAttribute="leading" id="9hc-Mm-7v7"/>
<constraint firstAttribute="trailing" secondItem="VB1-6I-men" secondAttribute="trailing" id="P3L-ud-eD0"/>
<constraint firstItem="ef3-Zx-nCS" firstAttribute="centerY" secondItem="CSz-iK-Imy" secondAttribute="centerY" id="PLm-Ng-tlW"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="CSz-iK-Imy" secondAttribute="trailing" id="Z6g-uk-L7s"/>
<constraint firstAttribute="bottom" secondItem="VB1-6I-men" secondAttribute="bottom" id="hFM-7e-UdP"/>
<constraint firstItem="VB1-6I-men" firstAttribute="leading" secondItem="CSz-iK-Imy" secondAttribute="leading" id="lUN-SE-VwO"/>
<constraint firstItem="CSz-iK-Imy" firstAttribute="leading" secondItem="ef3-Zx-nCS" secondAttribute="trailing" constant="10" id="nOE-xl-CUa"/>
<constraint firstItem="VB1-6I-men" firstAttribute="top" secondItem="CSz-iK-Imy" secondAttribute="bottom" constant="20" id="zYP-pf-qiS"/>
</constraints> </constraints>
</customView> </customView>
</subviews> </subviews>
@ -194,7 +254,7 @@
<constraint firstItem="Zfd-mA-qV9" firstAttribute="leading" secondItem="Zao-dc-fzq" secondAttribute="leading" id="C1K-EU-y2R"/> <constraint firstItem="Zfd-mA-qV9" firstAttribute="leading" secondItem="Zao-dc-fzq" secondAttribute="leading" id="C1K-EU-y2R"/>
<constraint firstItem="bFA-yD-Roy" firstAttribute="leading" secondItem="Zao-dc-fzq" secondAttribute="leading" constant="150" id="DSy-uq-QJd"/> <constraint firstItem="bFA-yD-Roy" firstAttribute="leading" secondItem="Zao-dc-fzq" secondAttribute="leading" constant="150" id="DSy-uq-QJd"/>
<constraint firstAttribute="bottom" secondItem="Zfd-mA-qV9" secondAttribute="bottom" id="IoU-2m-d07"/> <constraint firstAttribute="bottom" secondItem="Zfd-mA-qV9" secondAttribute="bottom" id="IoU-2m-d07"/>
<constraint firstItem="L6f-kb-mEG" firstAttribute="leading" secondItem="bFA-yD-Roy" secondAttribute="leading" id="hqH-9b-APZ"/> <constraint firstItem="CSz-iK-Imy" firstAttribute="leading" secondItem="bFA-yD-Roy" secondAttribute="leading" id="lTF-D2-GBG"/>
<constraint firstAttribute="trailing" secondItem="Zfd-mA-qV9" secondAttribute="trailing" id="mkp-8f-ZKo"/> <constraint firstAttribute="trailing" secondItem="Zfd-mA-qV9" secondAttribute="trailing" id="mkp-8f-ZKo"/>
<constraint firstItem="bFA-yD-Roy" firstAttribute="top" secondItem="Zao-dc-fzq" secondAttribute="top" id="suK-gn-ven"/> <constraint firstItem="bFA-yD-Roy" firstAttribute="top" secondItem="Zao-dc-fzq" secondAttribute="top" id="suK-gn-ven"/>
<constraint firstItem="Zfd-mA-qV9" firstAttribute="top" secondItem="bFA-yD-Roy" secondAttribute="bottom" constant="10" id="ymz-zd-mjc"/> <constraint firstItem="Zfd-mA-qV9" firstAttribute="top" secondItem="bFA-yD-Roy" secondAttribute="bottom" constant="10" id="ymz-zd-mjc"/>
@ -211,7 +271,10 @@
<connections> <connections>
<outlet property="constraintChoiceBottom" destination="0mN-72-zeu" id="Lf7-4v-Gae"/> <outlet property="constraintChoiceBottom" destination="0mN-72-zeu" id="Lf7-4v-Gae"/>
<outlet property="constraintSettingsTop" destination="ymz-zd-mjc" id="sN1-KM-hgC"/> <outlet property="constraintSettingsTop" destination="ymz-zd-mjc" id="sN1-KM-hgC"/>
<outlet property="labelDNSProtocol" destination="ef3-Zx-nCS" id="nec-BT-J2J"/>
<outlet property="popupChoice" destination="bFA-yD-Roy" id="lYe-Ho-P0K"/> <outlet property="popupChoice" destination="bFA-yD-Roy" id="lYe-Ho-P0K"/>
<outlet property="popupDNSProtocol" destination="CSz-iK-Imy" id="gIh-zq-W3E"/>
<outlet property="textDNSCustom" destination="oRM-XY-fH6" id="rmb-Ue-MZR"/>
<outlet property="viewDNSAddresses" destination="L6f-kb-mEG" id="cbb-T7-MeD"/> <outlet property="viewDNSAddresses" destination="L6f-kb-mEG" id="cbb-T7-MeD"/>
<outlet property="viewDNSDomains" destination="Baa-Z1-JJJ" id="msU-UP-bJZ"/> <outlet property="viewDNSDomains" destination="Baa-Z1-JJJ" id="msU-UP-bJZ"/>
<outlet property="viewSettings" destination="Zfd-mA-qV9" id="831-9h-AXu"/> <outlet property="viewSettings" destination="Zfd-mA-qV9" id="831-9h-AXu"/>
@ -219,7 +282,7 @@
</viewController> </viewController>
<customObject id="Gxf-Bl-2bj" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="Gxf-Bl-2bj" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="579" y="2339"/> <point key="canvasLocation" x="631" y="2362"/>
</scene> </scene>
<!--Service View Controller--> <!--Service View Controller-->
<scene sceneID="5Tf-QF-BT2"> <scene sceneID="5Tf-QF-BT2">

View File

@ -5,11 +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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 1.14.0 (2021-01-08) ## Unreleased
### Added ### Added
- Country flags in provider infrastructure menu. - Country flags in provider infrastructure menu.
- Support DNS over HTTPS/TLS in "Network settings". [#91](https://github.com/passepartoutvpn/passepartout-apple/issues/91)
### Changed ### Changed

View File

@ -25,22 +25,23 @@
import Cocoa import Cocoa
import PassepartoutCore import PassepartoutCore
import TunnelKit
class DNSViewController: NSViewController, ProfileCustomization { class DNSViewController: NSViewController, ProfileCustomization {
private struct Templates {
static let server = "0.0.0.0"
static let domain = ""
}
@IBOutlet private weak var popupChoice: NSPopUpButton! @IBOutlet private weak var popupChoice: NSPopUpButton!
@IBOutlet private weak var viewSettings: NSView! @IBOutlet private weak var viewSettings: NSView!
@IBOutlet private weak var textDNSCustom: NSTextField!
@IBOutlet private weak var viewDNSAddresses: NSView! @IBOutlet private weak var viewDNSAddresses: NSView!
@IBOutlet private weak var viewDNSDomains: NSView! @IBOutlet private weak var viewDNSDomains: NSView!
@IBOutlet private weak var labelDNSProtocol: NSTextField!
@IBOutlet private weak var popupDNSProtocol: NSPopUpButton!
@IBOutlet private var constraintChoiceBottom: NSLayoutConstraint! @IBOutlet private var constraintChoiceBottom: NSLayoutConstraint!
@IBOutlet private var constraintSettingsTop: NSLayoutConstraint! @IBOutlet private var constraintSettingsTop: NSLayoutConstraint!
@ -49,8 +50,6 @@ class DNSViewController: NSViewController, ProfileCustomization {
private lazy var tableDNSAddresses: TextTableView = .get() private lazy var tableDNSAddresses: TextTableView = .get()
private lazy var choices = NetworkChoice.choices(for: profile)
private lazy var currentChoice = profile?.networkChoices?.dns ?? ProfileNetworkChoices.with(profile: profile).dns private lazy var currentChoice = profile?.networkChoices?.dns ?? ProfileNetworkChoices.with(profile: profile).dns
private lazy var clientNetworkSettings = profile?.clientNetworkSettings private lazy var clientNetworkSettings = profile?.clientNetworkSettings
@ -66,6 +65,8 @@ class DNSViewController: NSViewController, ProfileCustomization {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
labelDNSProtocol.stringValue = L10n.Core.Global.Captions.protocol.asCaption
tableDNSAddresses.title = L10n.App.NetworkSettings.Dns.Cells.Addresses.title.asCaption tableDNSAddresses.title = L10n.App.NetworkSettings.Dns.Cells.Addresses.title.asCaption
viewDNSAddresses.addSubview(tableDNSAddresses) viewDNSAddresses.addSubview(tableDNSAddresses)
tableDNSAddresses.translatesAutoresizingMaskIntoConstraints = false tableDNSAddresses.translatesAutoresizingMaskIntoConstraints = false
@ -89,31 +90,80 @@ class DNSViewController: NSViewController, ProfileCustomization {
loadSettings(from: currentChoice) loadSettings(from: currentChoice)
popupChoice.removeAllItems() popupChoice.removeAllItems()
for choice in choices { popupDNSProtocol.removeAllItems()
popupChoice.addItem(withTitle: choice.description) let menuChoice = NSMenu()
var indexOfChoice = 0
for (i, choice) in NetworkChoice.choices(for: profile).enumerated() {
let item = NSMenuItem(title: choice.description, action: nil, keyEquivalent: "")
item.representedObject = choice
menuChoice.addItem(item)
if choice == currentChoice { if choice == currentChoice {
popupChoice.selectItem(at: popupChoice.numberOfItems - 1) indexOfChoice = i
} }
} }
tableDNSAddresses.rowTemplate = Templates.server popupChoice.menu = menuChoice
tableDNSDomains.rowTemplate = Templates.domain tableDNSAddresses.rowTemplate = AppConstants.Placeholders.dnsAddress
tableDNSDomains.rowTemplate = AppConstants.Placeholders.dnsDomain
let menuProtocol = NSMenu()
var availableProtocols: [DNSProtocol] = [.plain]
if #available(iOS 14, macOS 11, *) {
availableProtocols.append(.https)
availableProtocols.append(.tls)
}
var indexOfDNSProtocol = 0
for (i, proto) in availableProtocols.enumerated() {
let item = NSMenuItem(title: proto.description, action: nil, keyEquivalent: "")
item.representedObject = proto
menuProtocol.addItem(item)
if proto == networkSettings.dnsProtocol {
indexOfDNSProtocol = i
}
}
popupChoice.menu = menuChoice
popupChoice.selectItem(at: indexOfChoice)
popupDNSProtocol.menu = menuProtocol
popupDNSProtocol.selectItem(at: indexOfDNSProtocol)
} }
// MARK: Actions // MARK: Actions
@IBAction private func pickChoice(_ sender: Any?) { @IBAction private func pickChoice(_ sender: Any?) {
let choice = choices[popupChoice.indexOfSelectedItem] guard let choice = popupChoice.selectedItem?.representedObject as? NetworkChoice else {
return
}
loadSettings(from: choice) loadSettings(from: choice)
delegate?.profileCustomization(self, didUpdateDNS: choice, withManualSettings: networkSettings) delegate?.profileCustomization(self, didUpdateDNS: choice, withManualSettings: networkSettings)
} }
@IBAction private func pickProtocol(_ sender: Any?) {
guard let choice = popupChoice.selectedItem?.representedObject as? NetworkChoice else {
return
}
guard let proto = popupDNSProtocol.selectedItem?.representedObject as? DNSProtocol else {
return
}
networkSettings.dnsProtocol = proto
updateProtocolVisibility()
delegate?.profileCustomization(self, didUpdateDNS: choice, withManualSettings: networkSettings)
}
func commitManualSettings() { func commitManualSettings() {
guard currentChoice == .manual else { guard currentChoice == .manual else {
return return
} }
view.endEditing() view.endEditing()
switch networkSettings.dnsProtocol {
case .https:
networkSettings.dnsHTTPSURL = URL(string: textDNSCustom.stringValue)
case .tls:
networkSettings.dnsTLSServerName = textDNSCustom.stringValue
default:
networkSettings.dnsServers = tableDNSAddresses.rows networkSettings.dnsServers = tableDNSAddresses.rows
}
networkSettings.dnsSearchDomains = tableDNSDomains.rows networkSettings.dnsSearchDomains = tableDNSDomains.rows
delegate?.profileCustomization(self, didUpdateDNS: .manual, withManualSettings: networkSettings) delegate?.profileCustomization(self, didUpdateDNS: .manual, withManualSettings: networkSettings)
@ -138,12 +188,38 @@ class DNSViewController: NSViewController, ProfileCustomization {
} }
} }
tableDNSAddresses.reset(withRows: networkSettings.dnsServers ?? [], isAddEnabled: currentChoice == .manual) let isManual = (currentChoice == .manual)
tableDNSDomains.reset(withRows: networkSettings.dnsSearchDomains ?? [], isAddEnabled: currentChoice == .manual) popupDNSProtocol.isEnabled = isManual
textDNSCustom.isEnabled = isManual
tableDNSAddresses.reset(withRows: networkSettings.dnsServers ?? [], isAddEnabled: isManual)
tableDNSDomains.reset(withRows: networkSettings.dnsSearchDomains ?? [], isAddEnabled: isManual)
let isServer = (currentChoice == .server) let isServer = (currentChoice == .server)
constraintChoiceBottom.priority = isServer ? .defaultHigh : .defaultLow constraintChoiceBottom.priority = isServer ? .defaultHigh : .defaultLow
constraintSettingsTop.priority = isServer ? .defaultLow : .defaultHigh constraintSettingsTop.priority = isServer ? .defaultLow : .defaultHigh
viewSettings.isHidden = isServer viewSettings.isHidden = isServer
updateProtocolVisibility()
}
private func updateProtocolVisibility() {
let isManual = (currentChoice == .manual)
switch networkSettings.dnsProtocol {
case .https:
textDNSCustom.placeholderString = isManual ? AppConstants.Placeholders.dohURL : ""
textDNSCustom.stringValue = networkSettings.dnsHTTPSURL?.absoluteString ?? ""
textDNSCustom.isHidden = false
viewDNSAddresses.isHidden = true
case .tls:
textDNSCustom.placeholderString = isManual ? AppConstants.Placeholders.dotServerName : ""
textDNSCustom.stringValue = networkSettings.dnsTLSServerName ?? ""
textDNSCustom.isHidden = false
viewDNSAddresses.isHidden = false
default:
textDNSCustom.isHidden = true
viewDNSAddresses.isHidden = false
}
} }
} }

View File

@ -201,8 +201,11 @@ extension ProfileCustomizationContainerViewController: ProfileCustomizationDeleg
func profileCustomization(_ profileCustomization: ProfileCustomization, didUpdateDNS choice: NetworkChoice, withManualSettings newSettings: ProfileNetworkSettings) { func profileCustomization(_ profileCustomization: ProfileCustomization, didUpdateDNS choice: NetworkChoice, withManualSettings newSettings: ProfileNetworkSettings) {
pendingChoices?.dns = choice pendingChoices?.dns = choice
pendingManualNetworkSettings.dnsSearchDomains = newSettings.dnsSearchDomains pendingManualNetworkSettings.dnsProtocol = newSettings.dnsProtocol
pendingManualNetworkSettings.dnsHTTPSURL = newSettings.dnsHTTPSURL
pendingManualNetworkSettings.dnsTLSServerName = newSettings.dnsTLSServerName
pendingManualNetworkSettings.dnsServers = newSettings.dnsServers pendingManualNetworkSettings.dnsServers = newSettings.dnsServers
pendingManualNetworkSettings.dnsSearchDomains = newSettings.dnsSearchDomains
} }
func profileCustomization(_ profileCustomization: ProfileCustomization, didUpdateProxy choice: NetworkChoice, withManualSettings newSettings: ProfileNetworkSettings) { func profileCustomization(_ profileCustomization: ProfileCustomization, didUpdateProxy choice: NetworkChoice, withManualSettings newSettings: ProfileNetworkSettings) {

View File

@ -79,6 +79,7 @@ class ProxyViewController: NSViewController, ProfileCustomization {
tableProxyBypass.leftAnchor.constraint(equalTo: viewProxyBypass.leftAnchor), tableProxyBypass.leftAnchor.constraint(equalTo: viewProxyBypass.leftAnchor),
tableProxyBypass.rightAnchor.constraint(equalTo: viewProxyBypass.rightAnchor), tableProxyBypass.rightAnchor.constraint(equalTo: viewProxyBypass.rightAnchor),
]) ])
tableProxyBypass.rowTemplate = Templates.bypass
loadSettings(from: currentChoice) loadSettings(from: currentChoice)
@ -89,7 +90,6 @@ class ProxyViewController: NSViewController, ProfileCustomization {
popupChoice.selectItem(at: popupChoice.numberOfItems - 1) popupChoice.selectItem(at: popupChoice.numberOfItems - 1)
} }
} }
tableProxyBypass.rowTemplate = Templates.bypass
} }
// MARK: Actions // MARK: Actions

View File

@ -268,6 +268,22 @@ public class AppConstants {
public static let api = githubRaw(repo: "api") public static let api = githubRaw(repo: "api")
} }
public struct Placeholders {
public static let empty = ""
public static let address = "0.0.0.0"
public static let hostname = "example.com"
public static let dohURL = "https://example.com/dns-query"
public static let dotServerName = hostname
public static let dnsAddress = address
public static let dnsDomain = empty
}
public struct Credits { public struct Credits {
public static let author = "Davide De Rosa" public static let author = "Davide De Rosa"

View File

@ -75,8 +75,14 @@ public class ProfileNetworkSettings: Codable, CustomStringConvertible {
public var gatewayPolicies: [OpenVPN.RoutingPolicy]? public var gatewayPolicies: [OpenVPN.RoutingPolicy]?
public var dnsProtocol: DNSProtocol?
public var dnsServers: [String]? public var dnsServers: [String]?
public var dnsHTTPSURL: URL?
public var dnsTLSServerName: String?
public var dnsSearchDomains: [String]? public var dnsSearchDomains: [String]?
public var proxyAddress: String? public var proxyAddress: String?
@ -102,8 +108,11 @@ public class ProfileNetworkSettings: Codable, CustomStringConvertible {
public init(from configuration: OpenVPN.Configuration) { public init(from configuration: OpenVPN.Configuration) {
gatewayPolicies = configuration.routingPolicies gatewayPolicies = configuration.routingPolicies
dnsSearchDomains = configuration.searchDomains dnsProtocol = configuration.dnsProtocol
dnsServers = configuration.dnsServers dnsServers = configuration.dnsServers
dnsHTTPSURL = configuration.dnsHTTPSURL
dnsTLSServerName = configuration.dnsTLSServerName
dnsSearchDomains = configuration.searchDomains
proxyAddress = configuration.httpProxy?.address proxyAddress = configuration.httpProxy?.address
proxyPort = configuration.httpProxy?.port proxyPort = configuration.httpProxy?.port
proxyAutoConfigurationURL = configuration.proxyAutoConfigurationURL proxyAutoConfigurationURL = configuration.proxyAutoConfigurationURL
@ -123,8 +132,11 @@ public class ProfileNetworkSettings: Codable, CustomStringConvertible {
} }
public func copyDNS(from settings: ProfileNetworkSettings) { public func copyDNS(from settings: ProfileNetworkSettings) {
dnsSearchDomains = settings.dnsSearchDomains dnsProtocol = settings.dnsProtocol
dnsServers = settings.dnsServers?.filter { !$0.isEmpty } dnsServers = settings.dnsServers?.filter { !$0.isEmpty }
dnsHTTPSURL = settings.dnsHTTPSURL
dnsTLSServerName = settings.dnsTLSServerName
dnsSearchDomains = settings.dnsSearchDomains
} }
public func copyProxy(from settings: ProfileNetworkSettings) { public func copyProxy(from settings: ProfileNetworkSettings) {
@ -143,7 +155,7 @@ public class ProfileNetworkSettings: Codable, CustomStringConvertible {
public var description: String { public var description: String {
let comps: [String] = [ let comps: [String] = [
"gw: \(gatewayPolicies?.description ?? "")", "gw: \(gatewayPolicies?.description ?? "")",
"dns: {domains: \(dnsSearchDomains?.description ?? "[]"), servers: \(dnsServers?.description ?? "[]")}", "dns: {protocol: \(dnsProtocol ?? .fallback), https: \(dnsHTTPSURL?.absoluteString ?? ""), tls: \(dnsTLSServerName?.description ?? ""), servers: \(dnsServers?.description ?? "[]"), domains: \(dnsSearchDomains?.description ?? "[]")}",
"proxy: {address: \(proxyAddress ?? ""), port: \(proxyPort?.description ?? ""), PAC: \(proxyAutoConfigurationURL?.absoluteString ?? ""), bypass: \(proxyBypassDomains?.description ?? "[]")}", "proxy: {address: \(proxyAddress ?? ""), port: \(proxyPort?.description ?? ""), PAC: \(proxyAutoConfigurationURL?.absoluteString ?? ""), bypass: \(proxyBypassDomains?.description ?? "[]")}",
"mtu: {bytes: \(mtuBytes?.description ?? "default")}" "mtu: {bytes: \(mtuBytes?.description ?? "default")}"
] ]
@ -171,11 +183,17 @@ extension OpenVPN.ConfigurationBuilder {
break break
case .server: case .server:
dnsProtocol = nil
dnsServers = nil dnsServers = nil
dnsHTTPSURL = nil
dnsTLSServerName = nil
searchDomains = nil searchDomains = nil
case .manual: case .manual:
dnsProtocol = settings.dnsProtocol
dnsServers = settings.dnsServers?.filter { !$0.isEmpty } dnsServers = settings.dnsServers?.filter { !$0.isEmpty }
dnsHTTPSURL = settings.dnsHTTPSURL
dnsTLSServerName = settings.dnsTLSServerName
searchDomains = settings.dnsSearchDomains searchDomains = settings.dnsSearchDomains
} }
} }

View File

@ -8,7 +8,7 @@ $tunnelkit_specs = ['Protocols/OpenVPN', 'Extra/LZO']
def shared_pods def shared_pods
#pod_version $tunnelkit_name, $tunnelkit_specs, '~> 3.1.0' #pod_version $tunnelkit_name, $tunnelkit_specs, '~> 3.1.0'
pod_git $tunnelkit_name, $tunnelkit_specs, 'e388842' pod_git $tunnelkit_name, $tunnelkit_specs, '790ec27'
#pod_path $tunnelkit_name, $tunnelkit_specs, '..' #pod_path $tunnelkit_name, $tunnelkit_specs, '..'
pod 'SSZipArchive' pod 'SSZipArchive'
pod 'Kvitto', :git => 'https://github.com/keeshux/Kvitto', :branch => 'enable-macos-spec' pod 'Kvitto', :git => 'https://github.com/keeshux/Kvitto', :branch => 'enable-macos-spec'

View File

@ -52,8 +52,8 @@ DEPENDENCIES:
- Kvitto (from `https://github.com/keeshux/Kvitto`, branch `enable-macos-spec`) - Kvitto (from `https://github.com/keeshux/Kvitto`, branch `enable-macos-spec`)
- MBProgressHUD - MBProgressHUD
- SSZipArchive - SSZipArchive
- TunnelKit/Extra/LZO (from `https://github.com/passepartoutvpn/tunnelkit`, commit `e388842`) - TunnelKit/Extra/LZO (from `https://github.com/passepartoutvpn/tunnelkit`, commit `790ec27`)
- TunnelKit/Protocols/OpenVPN (from `https://github.com/passepartoutvpn/tunnelkit`, commit `e388842`) - TunnelKit/Protocols/OpenVPN (from `https://github.com/passepartoutvpn/tunnelkit`, commit `790ec27`)
SPEC REPOS: SPEC REPOS:
https://github.com/cocoapods/specs.git: https://github.com/cocoapods/specs.git:
@ -71,7 +71,7 @@ EXTERNAL SOURCES:
:branch: enable-macos-spec :branch: enable-macos-spec
:git: https://github.com/keeshux/Kvitto :git: https://github.com/keeshux/Kvitto
TunnelKit: TunnelKit:
:commit: e388842 :commit: 790ec27
:git: https://github.com/passepartoutvpn/tunnelkit :git: https://github.com/passepartoutvpn/tunnelkit
CHECKOUT OPTIONS: CHECKOUT OPTIONS:
@ -82,7 +82,7 @@ CHECKOUT OPTIONS:
:commit: e263fcd1f40a6a482a0f1e424ba98009c4ad2b96 :commit: e263fcd1f40a6a482a0f1e424ba98009c4ad2b96
:git: https://github.com/keeshux/Kvitto :git: https://github.com/keeshux/Kvitto
TunnelKit: TunnelKit:
:commit: e388842 :commit: 790ec27
:git: https://github.com/passepartoutvpn/tunnelkit :git: https://github.com/passepartoutvpn/tunnelkit
SPEC CHECKSUMS: SPEC CHECKSUMS:
@ -95,6 +95,6 @@ SPEC CHECKSUMS:
SwiftyBeaver: 2e8acd6fc90c6d0a27055867a290794926d57c02 SwiftyBeaver: 2e8acd6fc90c6d0a27055867a290794926d57c02
TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db TunnelKit: 2a6aadea2d772a2760b153aee27d1c334c9ca6db
PODFILE CHECKSUM: d6449ccbaad5d2ca50f6a27a651baaa17ef9db1a PODFILE CHECKSUM: cc3df077424e69b4f02e5ed3d26fe4be4786b733
COCOAPODS: 1.10.0 COCOAPODS: 1.10.0