Optionally resort to CloudFlare when no DNS settings are provided (#1165)

Fixes #1152
This commit is contained in:
Davide 2025-02-11 18:50:02 +01:00 committed by GitHub
parent cc8a500dab
commit c2a8db1cdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 136 additions and 11 deletions

View File

@ -0,0 +1,81 @@
//
// CommonLibrary.swift
// Passepartout
//
// Created by Davide De Rosa on 2/11/25.
// Copyright (c) 2025 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 PassepartoutKit
public final class CommonLibrary {
public enum Target {
case app
case tunnel
}
public init() {
}
public func configure(_ target: Target) {
switch target {
case .app:
configureApp()
case .tunnel:
configureTunnel()
}
}
}
private extension CommonLibrary {
func configureApp() {
configureShared()
PassepartoutConfiguration.shared.configureLogging(
to: BundleConfiguration.urlForAppLog,
parameters: Constants.shared.log,
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
)
}
func configureTunnel() {
configureShared()
PassepartoutConfiguration.shared.configureLogging(
to: BundleConfiguration.urlForTunnelLog,
parameters: Constants.shared.log,
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
)
if UserDefaults.appGroup.bool(forKey: AppPreference.dnsFallsBack.key) {
let servers = Constants.shared.tunnel.dnsFallbackServers
PassepartoutConfiguration.shared.dnsFallbackServers = servers
pp_log(.app, .info, "Enable DNS fallback servers: \(servers)")
}
}
func configureShared() {
UserDefaults.appGroup.register(defaults: [
AppPreference.logsPrivateData.key: false,
AppPreference.dnsFallsBack.key: true
])
}
}

View File

@ -26,6 +26,9 @@
import Foundation
public enum AppPreference: String, PreferenceProtocol {
case dnsFallsBack
// case dnsFallbackServers
case lastUsedProfileId
case logsPrivateData

View File

@ -114,6 +114,8 @@ public struct Constants: Decodable, Sendable {
public let refreshInterval: TimeInterval
public let dnsFallbackServers: [String]
public let verification: Verification
public func verificationDelayMinutes(isBeta: Bool) -> Int {

View File

@ -27,6 +27,10 @@
"tunnel": {
"profileTitleFormat": "Passepartout: %@",
"refreshInterval": 3.0,
"dnsFallbackServers": [
"1.1.1.1",
"1.0.0.1"
],
"verification": {
"production": {
"delay": 120.0,

View File

@ -828,6 +828,8 @@ public enum Strings {
}
}
public enum Preferences {
/// DNS fallback
public static let dnsFallsBack = Strings.tr("Localizable", "views.preferences.dns_falls_back", fallback: "DNS fallback")
/// Erase profiles from iCloud
public static let eraseIcloud = Strings.tr("Localizable", "views.preferences.erase_icloud", fallback: "Erase profiles from iCloud")
/// Keep in menu bar
@ -838,6 +840,10 @@ public enum Strings {
public static let locksInBackground = Strings.tr("Localizable", "views.preferences.locks_in_background", fallback: "Lock in background")
/// Pin active profile
public static let pinsActiveProfile = Strings.tr("Localizable", "views.preferences.pins_active_profile", fallback: "Pin active profile")
public enum DnsFallsBack {
/// Fall back to CloudFlare servers when the VPN does not provide DNS settings.
public static let footer = Strings.tr("Localizable", "views.preferences.dns_falls_back.footer", fallback: "Fall back to CloudFlare servers when the VPN does not provide DNS settings.")
}
public enum EraseIcloud {
/// To erase all profiles from the iCloud store securely, do so on all your synced devices. This will not affect local profiles.
public static let footer = Strings.tr("Localizable", "views.preferences.erase_icloud.footer", fallback: "To erase all profiles from the iCloud store securely, do so on all your synced devices. This will not affect local profiles.")

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Wenn du diese App oder Funktion in der Vergangenheit gekauft hast, kannst du deine Käufe wiederherstellen.";
"views.paywall.sections.restore.header" = "Wiederherstellen";
"views.paywall.sections.suggested_product.header" = "Einmaliger Kauf";
"views.preferences.dns_falls_back" = "DNS-Fallback";
"views.preferences.dns_falls_back.footer" = "Fällt auf CloudFlare-Server zurück, wenn das VPN keine DNS-Einstellungen bereitstellt.";
"views.preferences.erase_icloud" = "Profile aus iCloud löschen";
"views.preferences.erase_icloud.footer" = "Um alle Profile sicher aus dem iCloud-Speicher zu löschen, führen Sie diese Aktion auf allen synchronisierten Geräten aus. Lokale Profile werden nicht betroffen.";
"views.preferences.keeps_in_menu" = "In der Menüleiste behalten";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Εάν αγοράσατε αυτήν την εφαρμογή ή λειτουργία στο παρελθόν, μπορείτε να επαναφέρετε τις αγορές σας.";
"views.paywall.sections.restore.header" = "Επαναφορά";
"views.paywall.sections.suggested_product.header" = "Εφάπαξ αγορά";
"views.preferences.dns_falls_back" = "Εναλλακτικό DNS";
"views.preferences.dns_falls_back.footer" = "Χρησιμοποιεί τους διακομιστές CloudFlare όταν το VPN δεν παρέχει ρυθμίσεις DNS.";
"views.preferences.erase_icloud" = "Διαγραφή προφίλ από το iCloud";
"views.preferences.erase_icloud.footer" = "Για να διαγράψετε όλα τα προφίλ από το iCloud με ασφάλεια, κάντε το σε όλες τις συγχρονισμένες συσκευές σας. Αυτό δεν θα επηρεάσει τα τοπικά προφίλ.";
"views.preferences.keeps_in_menu" = "Διατήρηση στη γραμμή μενού";

View File

@ -105,6 +105,8 @@
"views.preferences.pins_active_profile.footer" = "Also show the active profile on top for quick access.";
"views.preferences.locks_in_background" = "Lock in background";
"views.preferences.locks_in_background.footer" = "Lock the app with FaceID when sent to the background.";
"views.preferences.dns_falls_back" = "DNS fallback";
"views.preferences.dns_falls_back.footer" = "Fall back to CloudFlare servers when the VPN does not provide DNS settings.";
"views.preferences.erase_icloud" = "Erase profiles from iCloud";
"views.preferences.erase_icloud.footer" = "To erase all profiles from the iCloud store securely, do so on all your synced devices. This will not affect local profiles.";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Si compraste esta app o característica en el pasado, puedes restaurar tus compras.";
"views.paywall.sections.restore.header" = "Restaurar";
"views.paywall.sections.suggested_product.header" = "Compra única";
"views.preferences.dns_falls_back" = "DNS de respaldo";
"views.preferences.dns_falls_back.footer" = "Usa los servidores de CloudFlare cuando el VPN no proporciona configuraciones de DNS.";
"views.preferences.erase_icloud" = "Eliminar perfiles de iCloud";
"views.preferences.erase_icloud.footer" = "Para eliminar de forma segura todos los perfiles del almacenamiento de iCloud, hágalo en todos sus dispositivos sincronizados. Esto no afectará los perfiles locales.";
"views.preferences.keeps_in_menu" = "Mantener en la barra de menús";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Si vous avez acheté cette application ou cette fonctionnalité dans le passé, vous pouvez restaurer vos achats.";
"views.paywall.sections.restore.header" = "Restaurer";
"views.paywall.sections.suggested_product.header" = "Achat unique";
"views.preferences.dns_falls_back" = "Basculement DNS";
"views.preferences.dns_falls_back.footer" = "Bascule vers les serveurs CloudFlare lorsque le VPN ne fournit pas de paramètres DNS.";
"views.preferences.erase_icloud" = "Supprimer les profils d'iCloud";
"views.preferences.erase_icloud.footer" = "Pour supprimer tous les profils du stockage iCloud de manière sécurisée, faites-le sur tous vos appareils synchronisés. Cela n'affectera pas les profils locaux.";
"views.preferences.keeps_in_menu" = "Conserver dans la barre de menu";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Se hai acquistato questa app o funzionalità in passato, puoi ripristinare i tuoi acquisti.";
"views.paywall.sections.restore.header" = "Ripristina";
"views.paywall.sections.suggested_product.header" = "Acquisto una tantum";
"views.preferences.dns_falls_back" = "DNS di fallback";
"views.preferences.dns_falls_back.footer" = "Ripiega sui server CloudFlare quando il VPN non fornisce impostazioni DNS.";
"views.preferences.erase_icloud" = "Elimina i profili da iCloud";
"views.preferences.erase_icloud.footer" = "Per eliminare in modo sicuro tutti i profili dall'archivio iCloud, fallo su tutti i tuoi dispositivi sincronizzati. Questo non influirà sui profili locali.";
"views.preferences.keeps_in_menu" = "Mantieni nella barra dei menu";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Als je deze app of functie eerder hebt gekocht, kun je je aankopen herstellen.";
"views.paywall.sections.restore.header" = "Herstellen";
"views.paywall.sections.suggested_product.header" = "Eenmalige aankoop";
"views.preferences.dns_falls_back" = "DNS-terugval";
"views.preferences.dns_falls_back.footer" = "Valt terug op CloudFlare-servers wanneer het VPN geen DNS-instellingen biedt.";
"views.preferences.erase_icloud" = "Verwijder profielen van iCloud";
"views.preferences.erase_icloud.footer" = "Om alle profielen veilig van de iCloud-opslag te verwijderen, doe dit op al uw gesynchroniseerde apparaten. Dit heeft geen invloed op lokale profielen.";
"views.preferences.keeps_in_menu" = "In menubalk houden";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Jeśli wcześniej kupiłeś tę aplikację lub funkcję, możesz przywrócić swoje zakupy.";
"views.paywall.sections.restore.header" = "Przywróć";
"views.paywall.sections.suggested_product.header" = "Jednorazowy zakup";
"views.preferences.dns_falls_back" = "Awaryjny DNS";
"views.preferences.dns_falls_back.footer" = "Przełącza się na serwery CloudFlare, gdy VPN nie zapewnia ustawień DNS.";
"views.preferences.erase_icloud" = "Usuń profile z iCloud";
"views.preferences.erase_icloud.footer" = "Aby bezpiecznie usunąć wszystkie profile z iCloud, wykonaj to na wszystkich synchronizowanych urządzeniach. Nie wpłynie to na profile lokalne.";
"views.preferences.keeps_in_menu" = "Trzymaj w pasku menu";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Se você comprou este app ou recurso no passado, pode restaurar suas compras.";
"views.paywall.sections.restore.header" = "Restaurar";
"views.paywall.sections.suggested_product.header" = "Compra única";
"views.preferences.dns_falls_back" = "Fallback de DNS";
"views.preferences.dns_falls_back.footer" = "Reverte para os servidores CloudFlare quando o VPN não fornece configurações de DNS.";
"views.preferences.erase_icloud" = "Apagar perfis do iCloud";
"views.preferences.erase_icloud.footer" = "Para apagar com segurança todos os perfis do armazenamento do iCloud, faça isso em todos os seus dispositivos sincronizados. Isso não afetará os perfis locais.";
"views.preferences.keeps_in_menu" = "Manter na barra de menu";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Если вы уже купили это приложение или функцию в прошлом, вы можете восстановить свои покупки.";
"views.paywall.sections.restore.header" = "Восстановить";
"views.paywall.sections.suggested_product.header" = "Единоразовая покупка";
"views.preferences.dns_falls_back" = "Резервный DNS";
"views.preferences.dns_falls_back.footer" = "Переключается на серверы CloudFlare, если VPN не предоставляет настройки DNS.";
"views.preferences.erase_icloud" = "Удалить профили из iCloud";
"views.preferences.erase_icloud.footer" = "Чтобы безопасно удалить все профили из iCloud, выполните это действие на всех ваших синхронизированных устройствах. Это не затронет локальные профили.";
"views.preferences.keeps_in_menu" = "Оставить в строке меню";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Om du har köpt denna app eller funktion tidigare kan du återställa dina köp.";
"views.paywall.sections.restore.header" = "Återställ";
"views.paywall.sections.suggested_product.header" = "Engångsköp";
"views.preferences.dns_falls_back" = "DNS-backup";
"views.preferences.dns_falls_back.footer" = "Återgår till CloudFlare-servrar när VPN inte tillhandahåller DNS-inställningar.";
"views.preferences.erase_icloud" = "Radera profiler från iCloud";
"views.preferences.erase_icloud.footer" = "För att säkert radera alla profiler från iCloud-lagringen, gör det på alla dina synkroniserade enheter. Detta påverkar inte lokala profiler.";
"views.preferences.keeps_in_menu" = "Behåll i menyraden";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "Якщо ви раніше купували цей додаток або функцію, ви можете відновити свої покупки.";
"views.paywall.sections.restore.header" = "Відновлення";
"views.paywall.sections.suggested_product.header" = "Одноразова покупка";
"views.preferences.dns_falls_back" = "Резервний DNS";
"views.preferences.dns_falls_back.footer" = "Перемикається на сервери CloudFlare, коли VPN не надає налаштування DNS.";
"views.preferences.erase_icloud" = "Видалити профілі з iCloud";
"views.preferences.erase_icloud.footer" = "Щоб безпечно видалити всі профілі з iCloud, виконайте цю дію на всіх ваших синхронізованих пристроях. Це не вплине на локальні профілі.";
"views.preferences.keeps_in_menu" = "Залишати в рядку меню";

View File

@ -274,6 +274,8 @@
"views.paywall.sections.restore.footer" = "如果您过去购买过此应用或功能,可以恢复您的购买记录。";
"views.paywall.sections.restore.header" = "恢复";
"views.paywall.sections.suggested_product.header" = "一次性购买";
"views.preferences.dns_falls_back" = "DNS 备用";
"views.preferences.dns_falls_back.footer" = "当 VPN 不提供 DNS 设置时,回退到 CloudFlare 服务器。";
"views.preferences.erase_icloud" = "从 iCloud 中删除配置文件";
"views.preferences.erase_icloud.footer" = "要安全地从 iCloud 存储中删除所有配置文件,请在所有已同步的设备上执行此操作。这不会影响本地配置文件。";
"views.preferences.keeps_in_menu" = "保持在菜单栏";

View File

@ -41,11 +41,8 @@ public final class UILibrary: UILibraryConfiguring {
}
public func configure(with context: AppContext) {
PassepartoutConfiguration.shared.configureLogging(
to: BundleConfiguration.urlForAppLog,
parameters: Constants.shared.log,
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
)
CommonLibrary().configure(.app)
assertMissingImplementations(with: context.registry)
uiConfiguring?.configure(with: context)
}

View File

@ -40,6 +40,9 @@ public struct PreferencesGroup: View {
private var settings: MacSettingsModel
#endif
@AppStorage(AppPreference.dnsFallsBack.key, store: .appGroup)
private var dnsFallsBack = true
private let profileManager: ProfileManager
@State
@ -60,6 +63,7 @@ public struct PreferencesGroup: View {
keepsInMenuToggle
#endif
pinActiveProfileToggle
dnsFallsBackToggle
eraseCloudKitButton
}
}
@ -87,6 +91,11 @@ private extension PreferencesGroup {
.themeSectionWithSingleRow(footer: Strings.Views.Preferences.PinsActiveProfile.footer)
}
var dnsFallsBackToggle: some View {
Toggle(Strings.Views.Preferences.dnsFallsBack, isOn: $dnsFallsBack)
.themeSectionWithSingleRow(footer: Strings.Views.Preferences.DnsFallsBack.footer)
}
var eraseCloudKitButton: some View {
Button(Strings.Views.Preferences.eraseIcloud, role: .destructive) {
isConfirmingEraseiCloud = true

@ -1 +1 @@
Subproject commit 191369cb4a9a34acac747f2fffd19ea470f8f79e
Subproject commit 595ebe59c71151d0661a018cd61d3af77114cd79

View File

@ -38,11 +38,8 @@ final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
private var fwd: NEPTPForwarder?
override func startTunnel(options: [String: NSObject]? = nil) async throws {
PassepartoutConfiguration.shared.configureLogging(
to: BundleConfiguration.urlForTunnelLog,
parameters: Constants.shared.log,
logsPrivateData: UserDefaults.appGroup.bool(forKey: AppPreference.logsPrivateData.key)
)
CommonLibrary().configure(.tunnel)
pp_log(.app, .info, "Tunnel started with options: \(options?.description ?? "nil")")
let environment = await dependencies.tunnelEnvironment()