mirror of
https://github.com/kkebo/DNSecure.git
synced 2026-03-11 08:54:36 +00:00
Merge pull request #115 from kkebo/fix-apply-with-excluded-domains
fix: Apply with excluded domains
This commit is contained in:
commit
055e40d2cb
5 changed files with 162 additions and 71 deletions
|
|
@ -0,0 +1,10 @@
|
|||
//
|
||||
// NEEvaluateConnectionRuleAction+Codable.swift
|
||||
// DNSecure
|
||||
//
|
||||
// Created by Kenta Kubo on 8/31/25.
|
||||
//
|
||||
|
||||
import NetworkExtension
|
||||
|
||||
extension NEEvaluateConnectionRuleAction: @retroactive Codable {}
|
||||
86
DNSecure/Models/OnDemandRule.swift
Normal file
86
DNSecure/Models/OnDemandRule.swift
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// OnDemandRule.swift
|
||||
// DNSecure
|
||||
//
|
||||
// Created by Kenta Kubo on 8/31/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
struct OnDemandRule {
|
||||
var id = UUID()
|
||||
var name: String
|
||||
var action: NEOnDemandRuleAction = .connect
|
||||
var interfaceType: NEOnDemandRuleInterfaceType = .any
|
||||
var ssidMatch: [String] = []
|
||||
var dnsSearchDomainMatch: [String] = []
|
||||
var dnsServerAddressMatch: [String] = []
|
||||
var probeURL: URL?
|
||||
var excludedDomains: [String]?
|
||||
}
|
||||
|
||||
extension OnDemandRule: Identifiable {}
|
||||
|
||||
extension OnDemandRule: Equatable {}
|
||||
|
||||
extension OnDemandRule: Hashable {}
|
||||
|
||||
extension OnDemandRule: Codable {}
|
||||
|
||||
extension [OnDemandRule] {
|
||||
func toNEOnDemandRules() -> [NEOnDemandRule] {
|
||||
self.map { rule in
|
||||
switch rule.action {
|
||||
case .connect:
|
||||
let newRule = NEOnDemandRuleConnect()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
return newRule
|
||||
case .disconnect:
|
||||
let newRule = NEOnDemandRuleDisconnect()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
return newRule
|
||||
case .evaluateConnection:
|
||||
let newRule = NEOnDemandRuleEvaluateConnection()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
newRule.connectionRules =
|
||||
switch rule.excludedDomains {
|
||||
case let domains? where !domains.isEmpty:
|
||||
[.init(matchDomains: domains, andAction: .neverConnect)]
|
||||
case _: []
|
||||
}
|
||||
return newRule
|
||||
case .ignore:
|
||||
let newRule = NEOnDemandRuleIgnore()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
return newRule
|
||||
@unknown case _:
|
||||
preconditionFailure("Unexpected NEOnDemandRuleAction")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -106,77 +106,6 @@ extension Configuration: CustomStringConvertible {
|
|||
}
|
||||
}
|
||||
|
||||
struct OnDemandRule {
|
||||
var id = UUID()
|
||||
var name: String
|
||||
var action: NEOnDemandRuleAction = .connect
|
||||
var interfaceType: NEOnDemandRuleInterfaceType = .any
|
||||
var ssidMatch: [String] = []
|
||||
var dnsSearchDomainMatch: [String] = []
|
||||
var dnsServerAddressMatch: [String] = []
|
||||
var probeURL: URL?
|
||||
}
|
||||
|
||||
extension OnDemandRule: Identifiable {}
|
||||
|
||||
extension OnDemandRule: Equatable {}
|
||||
|
||||
extension OnDemandRule: Hashable {}
|
||||
|
||||
extension OnDemandRule: Codable {}
|
||||
|
||||
extension Array where Self.Element == OnDemandRule {
|
||||
func toNEOnDemandRules() -> [NEOnDemandRule] {
|
||||
self.lazy
|
||||
.map { rule in
|
||||
switch rule.action {
|
||||
case .connect:
|
||||
let newRule = NEOnDemandRuleConnect()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
return newRule
|
||||
case .disconnect:
|
||||
let newRule = NEOnDemandRuleDisconnect()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
return newRule
|
||||
case .evaluateConnection:
|
||||
let newRule = NEOnDemandRuleEvaluateConnection()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
return newRule
|
||||
case .ignore:
|
||||
let newRule = NEOnDemandRuleIgnore()
|
||||
newRule.interfaceTypeMatch = rule.interfaceType
|
||||
if rule.interfaceType.isSSIDUsed {
|
||||
newRule.ssidMatch = rule.ssidMatch
|
||||
}
|
||||
newRule.dnsSearchDomainMatch = rule.dnsSearchDomainMatch
|
||||
newRule.dnsServerAddressMatch = rule.dnsServerAddressMatch
|
||||
newRule.probeURL = rule.probeURL
|
||||
return newRule
|
||||
default:
|
||||
preconditionFailure("Unexpected NEOnDemandRuleAction")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Resolver {
|
||||
var id = UUID()
|
||||
var name: String
|
||||
|
|
|
|||
49
DNSecure/Views/ExcludedDomainsView.swift
Normal file
49
DNSecure/Views/ExcludedDomainsView.swift
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ExcludedDomainsView {
|
||||
@Binding var domains: [String]
|
||||
}
|
||||
|
||||
extension ExcludedDomainsView: View {
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
ForEach(0..<self.domains.count, id: \.self) { i in
|
||||
LazyTextField(
|
||||
"Domain",
|
||||
// self.$rule.excludedDomains[i] causes crash on deletion
|
||||
text: .init(
|
||||
get: { self.domains[i] },
|
||||
set: { self.domains[i] = $0 }
|
||||
)
|
||||
)
|
||||
.textContentType(.URL)
|
||||
.keyboardType(.URL)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
}
|
||||
.onDelete { self.domains.remove(atOffsets: $0) }
|
||||
.onMove { self.domains.move(fromOffsets: $0, toOffset: $1) }
|
||||
Button("Add Domain") {
|
||||
self.domains.append("")
|
||||
}
|
||||
} footer: {
|
||||
Text(
|
||||
"Each domain is matched against the destination hostname using suffix matching, and each label in the domain must match an entire label in the hostname. For example, the domain `example.com` will match the hostname `www.example.com` but not `www.anotherexample.com`."
|
||||
)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Excluded Domains")
|
||||
.toolbar {
|
||||
EditButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 17, *)
|
||||
#Preview {
|
||||
@Previewable @State var domains = ["example.com"]
|
||||
NavigationStack {
|
||||
ExcludedDomainsView(domains: $domains)
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +67,23 @@ extension RuleView: View {
|
|||
Text($0.description)
|
||||
}
|
||||
}
|
||||
if self.rule.action == .evaluateConnection {
|
||||
NavigationLink {
|
||||
ExcludedDomainsView(
|
||||
domains: .init(
|
||||
get: { self.rule.excludedDomains ?? [] },
|
||||
set: { self.rule.excludedDomains = $0 }
|
||||
)
|
||||
)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Excluded Domains")
|
||||
Spacer()
|
||||
Text("\(self.rule.excludedDomains?.count ?? 0)")
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(self.rule.name)
|
||||
|
|
|
|||
Loading…
Reference in a new issue