diff --git a/CHANGELOG.md b/CHANGELOG.md index 353a6ca..fdd9e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - OpenVPN: Deal with remote options properly. [#297](https://github.com/passepartoutvpn/tunnelkit/pull/297) - OpenVPN: Routes from configuration file are ignored. [#278](https://github.com/passepartoutvpn/tunnelkit/issues/278) +- OpenVPN: Split DNS domain and search domains. [#313](https://github.com/passepartoutvpn/tunnelkit/pulls/313) - IPv6 endpoints are parsed improperly. [#293](https://github.com/passepartoutvpn/tunnelkit/issues/293) - Fix abandoned MockVPN. [#285](https://github.com/passepartoutvpn/tunnelkit/pull/285) diff --git a/Sources/TunnelKitOpenVPNAppExtension/NetworkSettingsBuilder.swift b/Sources/TunnelKitOpenVPNAppExtension/NetworkSettingsBuilder.swift index 629eaac..73c2019 100644 --- a/Sources/TunnelKitOpenVPNAppExtension/NetworkSettingsBuilder.swift +++ b/Sources/TunnelKitOpenVPNAppExtension/NetworkSettingsBuilder.swift @@ -128,6 +128,14 @@ extension NetworkSettingsBuilder { return servers } + private var dnsDomain: String? { + var domain = localOptions.dnsDomain + if pullDNS, let remoteDomain = remoteOptions.dnsDomain { + domain = remoteDomain + } + return domain + } + private var allDNSSearchDomains: [String] { var searchDomains = localOptions.searchDomains ?? [] if pullDNS, let remoteSearchDomains = remoteOptions.searchDomains { @@ -275,10 +283,14 @@ extension NetworkSettingsBuilder { dnsSettings?.matchDomains = [""] } + if let domain = dnsDomain { + log.info("DNS: Using domain: \(domain)") + dnsSettings?.domainName = domain + } + let searchDomains = allDNSSearchDomains if !searchDomains.isEmpty { - log.info("DNS: Using search domains \(searchDomains)") - dnsSettings?.domainName = searchDomains.first + log.info("DNS: Using search domains: \(searchDomains)") dnsSettings?.searchDomains = searchDomains if !isGateway { dnsSettings?.matchDomains = dnsSettings?.searchDomains diff --git a/Sources/TunnelKitOpenVPNCore/Configuration.swift b/Sources/TunnelKitOpenVPNCore/Configuration.swift index eed52db..2bd1463 100644 --- a/Sources/TunnelKitOpenVPNCore/Configuration.swift +++ b/Sources/TunnelKitOpenVPNCore/Configuration.swift @@ -275,6 +275,9 @@ extension OpenVPN { /// The server name if `dnsProtocol = .tls`. public var dnsTLSServerName: String? + /// The main domain name. + public var dnsDomain: String? + /// The search domain. @available(*, deprecated, message: "Use searchDomains instead") public var searchDomain: String? { @@ -287,7 +290,7 @@ extension OpenVPN { } } - /// The search domains. The first one is interpreted as the main domain name. + /// The search domains. public var searchDomains: [String]? /// The Proxy Auto-Configuration (PAC) url. @@ -370,6 +373,7 @@ extension OpenVPN { dnsServers: dnsServers, dnsHTTPSURL: dnsHTTPSURL, dnsTLSServerName: dnsTLSServerName, + dnsDomain: dnsDomain, searchDomains: searchDomains, isProxyEnabled: isProxyEnabled, httpProxy: httpProxy, @@ -496,6 +500,9 @@ extension OpenVPN { /// - Seealso: `ConfigurationBuilder.dnsTLSServerName` public let dnsTLSServerName: String? + /// - Seealso: `ConfigurationBuilder.dnsDomain` + public let dnsDomain: String? + /// - Seealso: `ConfigurationBuilder.searchDomains` public let searchDomains: [String]? @@ -619,6 +626,7 @@ extension OpenVPN.Configuration { builder.dnsServers = dnsServers builder.dnsHTTPSURL = dnsHTTPSURL builder.dnsTLSServerName = dnsTLSServerName + builder.dnsDomain = dnsDomain builder.searchDomains = searchDomains builder.isProxyEnabled = isProxyEnabled builder.httpProxy = httpProxy @@ -755,6 +763,9 @@ extension OpenVPN.Configuration { log.info("\tDNS: not configured") } } + if let dnsDomain = dnsDomain, !dnsDomain.isEmpty { + log.info("\tDNS domain: \(dnsDomain.maskedDescription)") + } if let searchDomains = searchDomains, !searchDomains.isEmpty { log.info("\tSearch domains: \(searchDomains.maskedDescription)") } diff --git a/Sources/TunnelKitOpenVPNCore/ConfigurationParser.swift b/Sources/TunnelKitOpenVPNCore/ConfigurationParser.swift index 7d4164a..d5237ff 100644 --- a/Sources/TunnelKitOpenVPNCore/ConfigurationParser.swift +++ b/Sources/TunnelKitOpenVPNCore/ConfigurationParser.swift @@ -111,6 +111,8 @@ extension OpenVPN { static let domain = NSRegularExpression("^dhcp-option +DOMAIN +[^ ]+") + static let domainSearch = NSRegularExpression("^dhcp-option +DOMAIN-SEARCH +[^ ]+") + static let proxy = NSRegularExpression("^dhcp-option +PROXY_(HTTPS? +[^ ]+ +\\d+|AUTO_CONFIG_URL +[^ ]+)") static let proxyBypass = NSRegularExpression("^dhcp-option +PROXY_BYPASS +.+") @@ -288,6 +290,7 @@ extension OpenVPN { var optRoutes4: [(String, String, String?)]? // address, netmask, gateway var optRoutes6: [(String, UInt8, String?)]? // destination, prefix, gateway var optDNSServers: [String]? + var optDomain: String? var optSearchDomains: [String]? var optHTTPProxy: Proxy? var optHTTPSProxy: Proxy? @@ -654,6 +657,12 @@ extension OpenVPN { optDNSServers?.append($0[1]) } Regex.domain.enumerateSpacedArguments(in: line) { + guard $0.count == 2 else { + return + } + optDomain = $0[1] + } + Regex.domainSearch.enumerateSpacedArguments(in: line) { guard $0.count == 2 else { return } @@ -931,6 +940,7 @@ extension OpenVPN { } sessionBuilder.dnsServers = optDNSServers + sessionBuilder.dnsDomain = optDomain sessionBuilder.searchDomains = optSearchDomains sessionBuilder.httpProxy = optHTTPProxy sessionBuilder.httpsProxy = optHTTPSProxy diff --git a/Tests/TunnelKitOpenVPNTests/ConfigurationParserTests.swift b/Tests/TunnelKitOpenVPNTests/ConfigurationParserTests.swift index a3bcddd..bc99ba4 100644 --- a/Tests/TunnelKitOpenVPNTests/ConfigurationParserTests.swift +++ b/Tests/TunnelKitOpenVPNTests/ConfigurationParserTests.swift @@ -64,10 +64,12 @@ class ConfigurationParserTests: XCTestCase { let lines = [ "dhcp-option DNS 8.8.8.8", "dhcp-option DNS6 ffff::1", - "dhcp-option DOMAIN fake-main.net", - "dhcp-option DOMAIN main.net", - "dhcp-option DOMAIN one.com", - "dhcp-option DOMAIN two.com", + "dhcp-option DOMAIN first-domain.net", + "dhcp-option DOMAIN second-domain.org", + "dhcp-option DOMAIN-SEARCH fake-main.net", + "dhcp-option DOMAIN-SEARCH main.net", + "dhcp-option DOMAIN-SEARCH one.com", + "dhcp-option DOMAIN-SEARCH two.com", "dhcp-option PROXY_HTTP 1.2.3.4 8081", "dhcp-option PROXY_HTTPS 7.8.9.10 8082", "dhcp-option PROXY_AUTO_CONFIG_URL https://pac/", @@ -77,6 +79,7 @@ class ConfigurationParserTests: XCTestCase { let parsed = try! OpenVPN.ConfigurationParser.parsed(fromLines: lines).configuration XCTAssertEqual(parsed.dnsServers, ["8.8.8.8", "ffff::1"]) + XCTAssertEqual(parsed.dnsDomain, "second-domain.org") XCTAssertEqual(parsed.searchDomains, ["fake-main.net", "main.net", "one.com", "two.com"]) XCTAssertEqual(parsed.httpProxy?.address, "1.2.3.4") XCTAssertEqual(parsed.httpProxy?.port, 8081)