global: support DNS search domains

This has been supported by Windows and Linux for quite some time. Add
support here for iOS and macOS.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2020-12-15 13:49:21 +01:00
parent 27b32e60b2
commit 9231c03513
6 changed files with 28 additions and 16 deletions

View File

@ -133,8 +133,10 @@ extension TunnelConfiguration {
let addressString = interface.addresses.map { $0.stringRepresentation }.joined(separator: ", ") let addressString = interface.addresses.map { $0.stringRepresentation }.joined(separator: ", ")
output.append("Address = \(addressString)\n") output.append("Address = \(addressString)\n")
} }
if !interface.dns.isEmpty { if !interface.dns.isEmpty || !interface.dnsSearch.isEmpty {
let dnsString = interface.dns.map { $0.stringRepresentation }.joined(separator: ", ") var dnsLine = interface.dns.map { $0.stringRepresentation }
dnsLine.append(contentsOf: interface.dnsSearch)
let dnsString = dnsLine.joined(separator: ", ")
output.append("DNS = \(dnsString)\n") output.append("DNS = \(dnsString)\n")
} }
if let mtu = interface.mtu { if let mtu = interface.mtu {
@ -188,13 +190,16 @@ extension TunnelConfiguration {
} }
if let dnsString = attributes["dns"] { if let dnsString = attributes["dns"] {
var dnsServers = [DNSServer]() var dnsServers = [DNSServer]()
var dnsSearch = [String]()
for dnsServerString in dnsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) { for dnsServerString in dnsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) {
guard let dnsServer = DNSServer(from: dnsServerString) else { if let dnsServer = DNSServer(from: dnsServerString) {
throw ParseError.interfaceHasInvalidDNS(dnsServerString)
}
dnsServers.append(dnsServer) dnsServers.append(dnsServer)
} else {
dnsSearch.append(dnsServerString)
}
} }
interface.dns = dnsServers interface.dns = dnsServers
interface.dnsSearch = dnsSearch
} }
if let mtuString = attributes["mtu"] { if let mtuString = attributes["mtu"] {
guard let mtu = UInt16(mtuString) else { guard let mtu = UInt16(mtuString) else {

View File

@ -74,6 +74,7 @@ extension TunnelConfiguration {
interfaceConfiguration?.addresses = base?.interface.addresses ?? [] interfaceConfiguration?.addresses = base?.interface.addresses ?? []
interfaceConfiguration?.dns = base?.interface.dns ?? [] interfaceConfiguration?.dns = base?.interface.dns ?? []
interfaceConfiguration?.dnsSearch = base?.interface.dnsSearch ?? []
interfaceConfiguration?.mtu = base?.interface.mtu interfaceConfiguration?.mtu = base?.interface.mtu
if let interfaceConfiguration = interfaceConfiguration { if let interfaceConfiguration = interfaceConfiguration {

View File

@ -139,8 +139,10 @@ class TunnelViewModel {
if let mtu = config.mtu { if let mtu = config.mtu {
scratchpad[.mtu] = String(mtu) scratchpad[.mtu] = String(mtu)
} }
if !config.dns.isEmpty { if !config.dns.isEmpty || !config.dnsSearch.isEmpty {
scratchpad[.dns] = config.dns.map { $0.stringRepresentation }.joined(separator: ", ") var dns = config.dns.map { $0.stringRepresentation }
dns.append(contentsOf: config.dnsSearch)
scratchpad[.dns] = dns.joined(separator: ", ")
} }
return scratchpad return scratchpad
} }
@ -194,15 +196,16 @@ class TunnelViewModel {
} }
if let dnsString = scratchpad[.dns] { if let dnsString = scratchpad[.dns] {
var dnsServers = [DNSServer]() var dnsServers = [DNSServer]()
var dnsSearch = [String]()
for dnsServerString in dnsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) { for dnsServerString in dnsString.splitToArray(trimmingCharacters: .whitespacesAndNewlines) {
if let dnsServer = DNSServer(from: dnsServerString) { if let dnsServer = DNSServer(from: dnsServerString) {
dnsServers.append(dnsServer) dnsServers.append(dnsServer)
} else { } else {
fieldsWithError.insert(.dns) dnsSearch.append(dnsServerString)
errorMessages.append(tr("alertInvalidInterfaceMessageDNSInvalid"))
} }
} }
config.dns = dnsServers config.dns = dnsServers
config.dnsSearch = dnsSearch
} }
guard errorMessages.isEmpty else { return .error(errorMessages.first!) } guard errorMessages.isEmpty else { return .error(errorMessages.first!) }

View File

@ -337,11 +337,6 @@ static bool is_valid_network(string_span_t s)
return is_valid_ipv4(s) || is_valid_ipv6(s); return is_valid_ipv4(s) || is_valid_ipv6(s);
} }
static bool is_valid_dns(string_span_t s)
{
return is_valid_ipv4(s) || is_valid_ipv6(s);
}
enum field { enum field {
InterfaceSection, InterfaceSection,
PrivateKey, PrivateKey,
@ -451,7 +446,12 @@ static void highlight_multivalue_value(struct highlight_span_array *ret, const s
{ {
switch (section) { switch (section) {
case DNS: case DNS:
append_highlight_span(ret, parent.s, s, is_valid_dns(s) ? HighlightIP : HighlightError); if (is_valid_ipv4(s) || is_valid_ipv6(s))
append_highlight_span(ret, parent.s, s, HighlightIP);
else if (is_valid_hostname(s))
append_highlight_span(ret, parent.s, s, HighlightHost);
else
append_highlight_span(ret, parent.s, s, HighlightError);
break; break;
case Address: case Address:
case AllowedIPs: { case AllowedIPs: {

View File

@ -10,6 +10,7 @@ public struct InterfaceConfiguration {
public var listenPort: UInt16? public var listenPort: UInt16?
public var mtu: UInt16? public var mtu: UInt16?
public var dns = [DNSServer]() public var dns = [DNSServer]()
public var dnsSearch = [String]()
public init(privateKey: PrivateKey) { public init(privateKey: PrivateKey) {
self.privateKey = privateKey self.privateKey = privateKey
@ -25,6 +26,7 @@ extension InterfaceConfiguration: Equatable {
lhsAddresses == rhsAddresses && lhsAddresses == rhsAddresses &&
lhs.listenPort == rhs.listenPort && lhs.listenPort == rhs.listenPort &&
lhs.mtu == rhs.mtu && lhs.mtu == rhs.mtu &&
lhs.dns == rhs.dns lhs.dns == rhs.dns &&
lhs.dnsSearch == rhs.dnsSearch
} }
} }

View File

@ -85,6 +85,7 @@ class PacketTunnelSettingsGenerator {
let dnsServerStrings = tunnelConfiguration.interface.dns.map { $0.stringRepresentation } let dnsServerStrings = tunnelConfiguration.interface.dns.map { $0.stringRepresentation }
let dnsSettings = NEDNSSettings(servers: dnsServerStrings) let dnsSettings = NEDNSSettings(servers: dnsServerStrings)
dnsSettings.searchDomains = tunnelConfiguration.interface.dnsSearch
dnsSettings.matchDomains = [""] // All DNS queries must first go through the tunnel's DNS dnsSettings.matchDomains = [""] // All DNS queries must first go through the tunnel's DNS
networkSettings.dnsSettings = dnsSettings networkSettings.dnsSettings = dnsSettings