Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save WireGuard obfuscation settings choice and move DNS settings to a custom view #5433

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions ios/MullvadSettings/WireGuardObfuscationSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,29 @@ public enum WireGuardObfuscationState: Codable {
case off
}

public enum WireGuardObfuscationPort: Codable {
case automatic
case port80
case port5001
public enum WireGuardObfuscationPort: UInt16, Codable {
case automatic = 0
case port80 = 80
case port5001 = 5001

public var portValue: UInt16 {
rawValue
}

public init?(rawValue: UInt16) {
switch rawValue {
case 80:
self = .port80
case 5001:
self = .port5001
default: self = .automatic
}
}
}

public struct WireGuardObfuscationSettings: Codable, Equatable {
let state: WireGuardObfuscationState
let port: WireGuardObfuscationPort
public let state: WireGuardObfuscationState
public let port: WireGuardObfuscationPort

public init(state: WireGuardObfuscationState = .automatic, port: WireGuardObfuscationPort = .automatic) {
self.state = state
Expand Down
22 changes: 18 additions & 4 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,10 @@
7A6B4F592AB8412E00123853 /* TunnelMonitorTimings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */; };
7A6F2FA52AFA3CB2006D0856 /* AccountExpiryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */; };
7A6F2FA72AFBB9AE006D0856 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; };
7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA82AFD0842006D0856 /* CustomDNSDataSource.swift */; };
7A6F2FAB2AFD3097006D0856 /* CustomDNSCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FAA2AFD3097006D0856 /* CustomDNSCellFactory.swift */; };
7A6F2FAD2AFD3DA7006D0856 /* CustomDNSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FAC2AFD3DA7006D0856 /* CustomDNSViewController.swift */; };
7A6F2FAF2AFE36E7006D0856 /* PreferencesInfoButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FAE2AFE36E7006D0856 /* PreferencesInfoButtonItem.swift */; };
7A7AD28D29DC677800480EF1 /* FirstTimeLaunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */; };
7A818F1F29F0305800C7F0F4 /* RootConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */; };
7A83C3FF2A55B72E00DFB83A /* MullvadVPNApp.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 7A83C3FE2A55B72E00DFB83A /* MullvadVPNApp.xctestplan */; };
Expand Down Expand Up @@ -515,7 +519,6 @@
A988A3E22AFE54AC0008D2C7 /* AccountExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */; };
A988DF212ADD293D00D807EF /* RESTTransportStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A1DE782AD5708E0073F689 /* RESTTransportStrategy.swift */; };
A988DF242ADD307200D807EF /* libRelaySelector.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5898D29829017DAC00EB5EBA /* libRelaySelector.a */; };
A988DF262ADE86ED00D807EF /* WireGuardObfuscationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */; };
A988DF272ADE86ED00D807EF /* WireGuardObfuscationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF252ADE86ED00D807EF /* WireGuardObfuscationSettings.swift */; };
A988DF2A2ADE880300D807EF /* TunnelSettingsV3.swift in Sources */ = {isa = PBXBuildFile; fileRef = A988DF282ADE880300D807EF /* TunnelSettingsV3.swift */; };
A9A1DE792AD5708E0073F689 /* RESTTransportStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A1DE782AD5708E0073F689 /* RESTTransportStrategy.swift */; };
Expand Down Expand Up @@ -1551,6 +1554,10 @@
7A6B4F582AB8412E00123853 /* TunnelMonitorTimings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitorTimings.swift; sourceTree = "<group>"; };
7A6F2FA42AFA3CB2006D0856 /* AccountExpiryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryTests.swift; sourceTree = "<group>"; };
7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiry.swift; sourceTree = "<group>"; };
7A6F2FA82AFD0842006D0856 /* CustomDNSDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDNSDataSource.swift; sourceTree = "<group>"; };
7A6F2FAA2AFD3097006D0856 /* CustomDNSCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDNSCellFactory.swift; sourceTree = "<group>"; };
7A6F2FAC2AFD3DA7006D0856 /* CustomDNSViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDNSViewController.swift; sourceTree = "<group>"; };
7A6F2FAE2AFE36E7006D0856 /* PreferencesInfoButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesInfoButtonItem.swift; sourceTree = "<group>"; };
7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTimeLaunch.swift; sourceTree = "<group>"; };
7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootConfiguration.swift; sourceTree = "<group>"; };
7A83C3FE2A55B72E00DFB83A /* MullvadVPNApp.xctestplan */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = MullvadVPNApp.xctestplan; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2107,9 +2114,13 @@
583FE01A29C19777006E85F9 /* Preferences */ = {
isa = PBXGroup;
children = (
7A6F2FAA2AFD3097006D0856 /* CustomDNSCellFactory.swift */,
7A6F2FA82AFD0842006D0856 /* CustomDNSDataSource.swift */,
7A6F2FAC2AFD3DA7006D0856 /* CustomDNSViewController.swift */,
5864AF0229C7879B005B0CD9 /* PreferencesCellFactory.swift */,
584D26C3270C855A004EA533 /* PreferencesDataSource.swift */,
587EB6732714520600123C75 /* PreferencesDataSourceDelegate.swift */,
7A6F2FAE2AFE36E7006D0856 /* PreferencesInfoButtonItem.swift */,
5871167E2910035700D41AAC /* PreferencesInteractor.swift */,
58ACF6482655365700ACE4B7 /* PreferencesViewController.swift */,
587EB671271451E300123C75 /* PreferencesViewModel.swift */,
Expand Down Expand Up @@ -2423,10 +2434,10 @@
587B75422669034500DEF7E9 /* Notification Providers */ = {
isa = PBXGroup;
children = (
58C8191729FAA2C400DEB1B4 /* NotificationConfiguration.swift */,
7A6F2FA62AFBB9AE006D0856 /* AccountExpiry.swift */,
587B75402668FD7700DEF7E9 /* AccountExpirySystemNotificationProvider.swift */,
58607A4C2947287800BC467D /* AccountExpiryInAppNotificationProvider.swift */,
587B75402668FD7700DEF7E9 /* AccountExpirySystemNotificationProvider.swift */,
58C8191729FAA2C400DEB1B4 /* NotificationConfiguration.swift */,
F07CFF1F29F2720E008C0343 /* RegisteredDeviceInAppNotificationProvider.swift */,
58A94AE326CFD945001CB97C /* TunnelStatusNotificationProvider.swift */,
);
Expand Down Expand Up @@ -4351,7 +4362,6 @@
5896CEF226972DEB00B0FAE8 /* AccountContentView.swift in Sources */,
7A3353932AAA089000F0A71C /* SimulatorTunnelInfo.swift in Sources */,
5867771429097BCD006F721F /* PaymentState.swift in Sources */,
A988DF262ADE86ED00D807EF /* WireGuardObfuscationSettings.swift in Sources */,
F0EF50D32A8FA47E0031E8DF /* ChangeLogInteractor.swift in Sources */,
7AC8A3AF2ABC71D600DC4939 /* TermsOfServiceCoordinator.swift in Sources */,
F0C2AEFD2A0BB5CC00986207 /* NotificationProviderIdentifier.swift in Sources */,
Expand Down Expand Up @@ -4429,6 +4439,7 @@
58F70FE52AEA707800E6890E /* StoreTransactionLog.swift in Sources */,
582AE3102440A6CA00E6733A /* InputTextFormatter.swift in Sources */,
7A9CCCBA2A96302800DD6A34 /* CreateAccountVoucherCoordinator.swift in Sources */,
7A6F2FAD2AFD3DA7006D0856 /* CustomDNSViewController.swift in Sources */,
5820EDAB288FF0D2006BF4E4 /* DeviceRowView.swift in Sources */,
F0E8CC0C2A4EE672007ED3B4 /* SetupAccountCompletedController.swift in Sources */,
5846227726E22A7C0035F7C2 /* StorePaymentManagerDelegate.swift in Sources */,
Expand All @@ -4444,6 +4455,7 @@
5864859929A0D028006C5743 /* FormsheetPresentationController.swift in Sources */,
7A9CCCB52A96302800DD6A34 /* AddCreditSucceededCoordinator.swift in Sources */,
7A0C0F632A979C4A0058EFCE /* Coordinator+Router.swift in Sources */,
7A6F2FAB2AFD3097006D0856 /* CustomDNSCellFactory.swift in Sources */,
58A99ED3240014A0006599E9 /* TermsOfServiceViewController.swift in Sources */,
58CCA0162242560B004F3011 /* UIColor+Palette.swift in Sources */,
587CBFE322807F530028DED3 /* UIColor+Helpers.swift in Sources */,
Expand Down Expand Up @@ -4502,6 +4514,7 @@
F0C6FA852A6A733700F521F0 /* InAppPurchaseInteractor.swift in Sources */,
5878F50029CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift in Sources */,
583FE01029C0F532006E85F9 /* CustomSplitViewController.swift in Sources */,
7A6F2FA92AFD0842006D0856 /* CustomDNSDataSource.swift in Sources */,
58EF580B25D69D7A00AEBA94 /* ProblemReportSubmissionOverlayView.swift in Sources */,
5892A45E265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift in Sources */,
580909D32876D09A0078138D /* RevokedDeviceViewController.swift in Sources */,
Expand Down Expand Up @@ -4560,6 +4573,7 @@
F0E8CC032A4C753B007ED3B4 /* WelcomeViewController.swift in Sources */,
584D26C4270C855B004EA533 /* PreferencesDataSource.swift in Sources */,
F0D8825B2B04F53600D3EF9A /* OutgoingConnectionData.swift in Sources */,
7A6F2FAF2AFE36E7006D0856 /* PreferencesInfoButtonItem.swift in Sources */,
7A9CCCB62A96302800DD6A34 /* OutOfTimeCoordinator.swift in Sources */,
58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */,
58B43C1925F77DB60002C8C3 /* TunnelControlView.swift in Sources */,
Expand Down
11 changes: 11 additions & 0 deletions ios/MullvadVPN/TunnelManager/TunnelManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,16 @@ final class TunnelManager: StorePaymentObserver {
)
}

func setObfuscationSettings(_ newSettings: WireGuardObfuscationSettings) {
scheduleSettingsUpdate(
taskName: "Set obfuscation settings",
modificationBlock: { settings in
settings.wireGuardObfuscation = newSettings
},
completionHandler: nil
)
}

// MARK: - Tunnel observeration

/// Add tunnel observer.
Expand Down Expand Up @@ -982,6 +992,7 @@ final class TunnelManager: StorePaymentObserver {
let updatedConstraints = updatedSettings.relayConstraints
let selectNewRelay = currentConstraints != updatedConstraints

// TODO: Handle using an obfuscator here
self.setSettings(updatedSettings, persist: true)
self.reconnectTunnel(selectNewRelay: selectNewRelay, completionHandler: nil)
}
Expand Down
214 changes: 214 additions & 0 deletions ios/MullvadVPN/View controllers/Preferences/CustomDNSCellFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
//
// CustomDNSCellFactory.swift
// MullvadVPN
//
// Created by Jon Petersson on 2023-11-09.
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
//

import MullvadSettings
import UIKit

protocol CustomDNSCellEventHandler {
func addDNSEntry()
func didChangeDNSEntry(with identifier: UUID, inputString: String) -> Bool
func didChangeState(for preference: CustomDNSDataSource.Item, isOn: Bool)
func showInfo(for button: PreferencesInfoButtonItem)
}

final class CustomDNSCellFactory: CellFactoryProtocol {
let tableView: UITableView
var viewModel: PreferencesViewModel
var delegate: CustomDNSCellEventHandler?
var isEditing = false

init(tableView: UITableView, viewModel: PreferencesViewModel) {
self.tableView = tableView
self.viewModel = viewModel
}

func makeCell(for item: CustomDNSDataSource.Item, indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: item.reuseIdentifier.rawValue, for: indexPath)

configureCell(cell, item: item, indexPath: indexPath)

return cell
}

func configure(
_ cell: UITableViewCell,
toggleSetting: Bool,
title: String,
for preference: CustomDNSDataSource.Item
) {
guard let cell = cell as? SettingsSwitchCell else { return }

cell.titleLabel.text = title
cell.accessibilityHint = nil
cell.applySubCellStyling()
cell.setOn(toggleSetting, animated: false)
cell.action = { [weak self] isOn in
self?.delegate?.didChangeState(
for: preference,
isOn: isOn
)
}
}

// swiftlint:disable:next function_body_length
func configureCell(_ cell: UITableViewCell, item: CustomDNSDataSource.Item, indexPath: IndexPath) {
switch item {
case .blockAdvertising:
let localizedString = NSLocalizedString(
"BLOCK_ADS_CELL_LABEL",
tableName: "Preferences",
value: "Ads",
comment: ""
)

configure(
cell,
toggleSetting: viewModel.blockAdvertising,
title: localizedString,
for: .blockAdvertising
)

case .blockTracking:
let localizedString = NSLocalizedString(
"BLOCK_TRACKERS_CELL_LABEL",
tableName: "Preferences",
value: "Trackers",
comment: ""
)
configure(
cell,
toggleSetting: viewModel.blockTracking,
title: localizedString,
for: .blockTracking
)

case .blockMalware:
guard let cell = cell as? SettingsSwitchCell else { return }

let localizedString = NSLocalizedString(
"BLOCK_MALWARE_CELL_LABEL",
tableName: "Preferences",
value: "Malware",
comment: ""
)
configure(
cell,
toggleSetting: viewModel.blockMalware,
title: localizedString,
for: .blockMalware
)
cell.infoButtonHandler = { [weak self] in
self?.delegate?.showInfo(for: .blockMalware)
}

case .blockAdultContent:
let localizedString = NSLocalizedString(
"BLOCK_ADULT_CELL_LABEL",
tableName: "Preferences",
value: "Adult content",
comment: ""
)
configure(
cell,
toggleSetting: viewModel.blockAdultContent,
title: localizedString,
for: .blockAdultContent
)

case .blockGambling:
let localizedString = NSLocalizedString(
"BLOCK_GAMBLING_CELL_LABEL",
tableName: "Preferences",
value: "Gambling",
comment: ""
)
configure(
cell,
toggleSetting: viewModel.blockGambling,
title: localizedString,
for: .blockGambling
)

case .blockSocialMedia:
let localizedString = NSLocalizedString(
"BLOCK_SOCIAL_MEDIA_CELL_LABEL",
tableName: "Preferences",
value: "Social media",
comment: ""
)
configure(
cell,
toggleSetting: viewModel.blockSocialMedia,
title: localizedString,
for: .blockSocialMedia
)

case .useCustomDNS:
guard let cell = cell as? SettingsSwitchCell else { return }

cell.titleLabel.text = NSLocalizedString(
"CUSTOM_DNS_CELL_LABEL",
tableName: "Preferences",
value: "Use custom DNS",
comment: ""
)
cell.setEnabled(viewModel.customDNSPrecondition == .satisfied)
cell.setOn(viewModel.effectiveEnableCustomDNS, animated: false)
cell.accessibilityHint = viewModel.customDNSPrecondition
.localizedDescription(isEditing: isEditing)
cell.action = { [weak self] isOn in
self?.delegate?.didChangeState(for: .useCustomDNS, isOn: isOn)
}

case .addDNSServer:
guard let cell = cell as? SettingsAddDNSEntryCell else { return }

cell.titleLabel.text = NSLocalizedString(
"ADD_CUSTOM_DNS_SERVER_CELL_LABEL",
tableName: "Preferences",
value: "Add a server",
comment: ""
)
cell.action = { [weak self] in
self?.delegate?.addDNSEntry()
}

case let .dnsServer(entryIdentifier):
guard let cell = cell as? SettingsDNSTextCell else { return }

let dnsServerEntry = viewModel.dnsEntry(entryIdentifier: entryIdentifier)!

cell.textField.text = dnsServerEntry.address
cell.isValidInput = dnsEntryIsValid(identifier: entryIdentifier, cell: cell)

cell.onTextChange = { [weak self] cell in
cell.isValidInput = self?
.dnsEntryIsValid(identifier: entryIdentifier, cell: cell) ?? false
}

cell.onReturnKey = { cell in
cell.endEditing(false)
}

case .dnsServerInfo:
guard let cell = cell as? SettingsDNSInfoCell else { return }

cell.titleLabel.attributedText = viewModel.customDNSPrecondition.attributedLocalizedDescription(
isEditing: isEditing,
preferredFont: .systemFont(ofSize: 14)
)
}
}

private func dnsEntryIsValid(identifier: UUID, cell: SettingsDNSTextCell) -> Bool {
delegate?.didChangeDNSEntry(
with: identifier,
inputString: cell.textField.text ?? ""
) ?? false
}
}
Loading
Loading