From 3c92e18c0e787cd9f0fe07fa9d98561fad8163d8 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Fri, 22 Jan 2021 10:18:31 +0100 Subject: [PATCH 1/3] Add DNSProtocol --- .jazzy.yaml | 1 + TunnelKit.xcodeproj/project.pbxproj | 6 +++ TunnelKit/Sources/Core/DNSProtocol.swift | 42 +++++++++++++++++++ .../Protocols/OpenVPN/Configuration.swift | 26 +++++++++++- 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 TunnelKit/Sources/Core/DNSProtocol.swift diff --git a/.jazzy.yaml b/.jazzy.yaml index 4fc3800..54e006d 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -18,6 +18,7 @@ custom_categories: children: - BidirectionalState - ConfigurationError + - DNSProtocol - DNSResolver - DNSRecord - EndpointProtocol diff --git a/TunnelKit.xcodeproj/project.pbxproj b/TunnelKit.xcodeproj/project.pbxproj index 4f26f79..2cb2c5a 100644 --- a/TunnelKit.xcodeproj/project.pbxproj +++ b/TunnelKit.xcodeproj/project.pbxproj @@ -186,6 +186,8 @@ 0E749F622178911D00BB2701 /* pia-2048.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0E749F612178911C00BB2701 /* pia-2048.pem */; }; 0E7F3F6A246ABA0F006BE77F /* IPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F3F69246ABA0F006BE77F /* IPHeader.swift */; }; 0E7F3F6B246ABA0F006BE77F /* IPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F3F69246ABA0F006BE77F /* IPHeader.swift */; }; + 0E94E8EA25BACEBD0040BC30 /* DNSProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E94E8E925BACEBD0040BC30 /* DNSProtocol.swift */; }; + 0E94E8EB25BACEBD0040BC30 /* DNSProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E94E8E925BACEBD0040BC30 /* DNSProtocol.swift */; }; 0EA82A282190B220007960EB /* TunnelKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3251C51F95770D00C108D9 /* TunnelKit.framework */; }; 0EA82A3E2190B2BC007960EB /* pia-2048.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0E749F612178911C00BB2701 /* pia-2048.pem */; }; 0EAC57372494277A00D0FCE0 /* OpenVPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57312494277A00D0FCE0 /* OpenVPNProvider.swift */; }; @@ -567,6 +569,7 @@ 0E749F612178911C00BB2701 /* pia-2048.pem */ = {isa = PBXFileReference; lastKnownFileType = text; path = "pia-2048.pem"; sourceTree = ""; }; 0E7F3F69246ABA0F006BE77F /* IPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPHeader.swift; sourceTree = ""; }; 0E85A25B202CCA3D0059E9F9 /* Host.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Host.entitlements; sourceTree = ""; }; + 0E94E8E925BACEBD0040BC30 /* DNSProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSProtocol.swift; sourceTree = ""; }; 0EA82A232190B220007960EB /* TunnelKitTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TunnelKitTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 0EAC57312494277A00D0FCE0 /* OpenVPNProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenVPNProvider.swift; sourceTree = ""; }; 0EAC57322494277A00D0FCE0 /* MockVPNProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockVPNProvider.swift; sourceTree = ""; }; @@ -1074,6 +1077,7 @@ 0EFEB4432006D3C800F81029 /* Data+Manipulation.swift */, 0E411B992271F90700E0852C /* DNS.h */, 0E411B9A2271F90700E0852C /* DNS.m */, + 0E94E8E925BACEBD0040BC30 /* DNSProtocol.swift */, 0EE2F9E522918DA100F56F49 /* DNSResolver.swift */, 0E011F7C2196D97200BA59EE /* EndpointProtocol.swift */, 0EFEB4362006D3C800F81029 /* Errors.h */, @@ -2036,6 +2040,7 @@ 0E23B44122982AF800304C30 /* OpenVPNSession+PIA.swift in Sources */, 0E23B42D22982AF800304C30 /* ControlChannelSerializer.swift in Sources */, 0EE2F9F622918DA100F56F49 /* NEUDPSocket.swift in Sources */, + 0E94E8EA25BACEBD0040BC30 /* DNSProtocol.swift in Sources */, 0EE2F96E2291636B00F56F49 /* IPv4Settings.swift in Sources */, 0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */, 0EE2FA0622918DA100F56F49 /* Utils.swift in Sources */, @@ -2125,6 +2130,7 @@ 0E23B44222982AF800304C30 /* OpenVPNSession+PIA.swift in Sources */, 0E23B42E22982AF800304C30 /* ControlChannelSerializer.swift in Sources */, 0EE2F9F722918DA100F56F49 /* NEUDPSocket.swift in Sources */, + 0E94E8EB25BACEBD0040BC30 /* DNSProtocol.swift in Sources */, 0EE2F96F2291636B00F56F49 /* IPv4Settings.swift in Sources */, 0EFEB49C2006D7F300F81029 /* Data+Manipulation.swift in Sources */, 0EE2FA0722918DA100F56F49 /* Utils.swift in Sources */, diff --git a/TunnelKit/Sources/Core/DNSProtocol.swift b/TunnelKit/Sources/Core/DNSProtocol.swift new file mode 100644 index 0000000..dd5d6e7 --- /dev/null +++ b/TunnelKit/Sources/Core/DNSProtocol.swift @@ -0,0 +1,42 @@ +// +// DNSProtocol.swift +// TunnelKit +// +// Created by Davide De Rosa on 1/22/21. +// Copyright (c) 2021 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of TunnelKit. +// +// TunnelKit 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. +// +// TunnelKit 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 TunnelKit. If not, see . +// + +import Foundation + +/// The protocol used in DNS servers. +public enum DNSProtocol: String, Codable { + + /// The value to fall back to when unset. + public static let fallback: DNSProtocol = .plain + + /// Standard plaintext DNS (port 53). + case plain + + /// DNS over HTTPS. + case https + + /// DNS over TLS (port 853). + case tls +} diff --git a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift index afc6009..6448981 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift @@ -258,9 +258,18 @@ extension OpenVPN { /// The settings for IPv6. `OpenVPNSession` only evaluates this server-side. public var ipv6: IPv6Settings? - /// The DNS servers. + /// The DNS protocol, defaults to `.plain` (iOS 14+ / macOS 11+). + public var dnsProtocol: DNSProtocol? + + /// The DNS servers if `dnsProtocol = .plain` or nil. public var dnsServers: [String]? + /// The server URL if `dnsProtocol = .https`. + public var dnsHTTPSURL: URL? + + /// The server name if `dnsProtocol = .tls`. + public var dnsTLSServerName: String? + /// The search domain. @available(*, deprecated, message: "Use searchDomains instead") public var searchDomain: String? { @@ -327,7 +336,10 @@ extension OpenVPN { peerId: peerId, ipv4: ipv4, ipv6: ipv6, + dnsProtocol: dnsProtocol, dnsServers: dnsServers, + dnsHTTPSURL: dnsHTTPSURL, + dnsTLSServerName: dnsTLSServerName, searchDomains: searchDomains, httpProxy: httpProxy, httpsProxy: httpsProxy, @@ -438,9 +450,18 @@ extension OpenVPN { /// - Seealso: `ConfigurationBuilder.ipv6` public let ipv6: IPv6Settings? + /// - Seealso: `ConfigurationBuilder.dnsProtocol` + public let dnsProtocol: DNSProtocol? + /// - Seealso: `ConfigurationBuilder.dnsServers` public let dnsServers: [String]? + /// - Seealso: `ConfigurationBuilder.dnsHTTPSURL` + public let dnsHTTPSURL: URL? + + /// - Seealso: `ConfigurationBuilder.dnsTLSServerName` + public let dnsTLSServerName: String? + /// - Seealso: `ConfigurationBuilder.searchDomains` public let searchDomains: [String]? @@ -514,7 +535,10 @@ extension OpenVPN.Configuration { builder.peerId = peerId builder.ipv4 = ipv4 builder.ipv6 = ipv6 + builder.dnsProtocol = dnsProtocol builder.dnsServers = dnsServers + builder.dnsHTTPSURL = dnsHTTPSURL + builder.dnsTLSServerName = dnsTLSServerName builder.searchDomains = searchDomains builder.httpProxy = httpProxy builder.httpsProxy = httpsProxy From dd81ad7a994090481011bf661ac3dd2934e35334 Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Fri, 22 Jan 2021 10:29:03 +0100 Subject: [PATCH 2/3] Pick proper DNS settings according to protocol --- CHANGELOG.md | 1 + .../AppExtension/OpenVPNTunnelProvider.swift | 27 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1311e6..1d66788 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Handle `--data-ciphers` and `data-ciphers-fallback` from OpenVPN 2.5 +- Support DNS over HTTPS (DoH) and TLS (DoT). ## 3.2.0 (2021-01-07) diff --git a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift index 87f56bf..c19df65 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift @@ -692,10 +692,35 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate { dnsServers = fallbackDNSServers } - let dnsSettings = NEDNSSettings(servers: dnsServers) + var dnsSettings = NEDNSSettings(servers: dnsServers) + if #available(iOS 14, macOS 11, *) { + switch cfg.sessionConfiguration.dnsProtocol { + case .https: + guard let serverURL = cfg.sessionConfiguration.dnsHTTPSURL else { + break + } + let specific = NEDNSOverHTTPSSettings(servers: dnsServers) + specific.serverURL = serverURL + dnsSettings = specific + + case .tls: + guard let serverName = cfg.sessionConfiguration.dnsTLSServerName else { + break + } + let specific = NEDNSOverTLSSettings(servers: dnsServers) + specific.serverName = serverName + dnsSettings = specific + + default: + break + } + } + + // "hack" for split DNS (i.e. use VPN only for DNS) if !isGateway { dnsSettings.matchDomains = [""] } + if let searchDomains = cfg.sessionConfiguration.searchDomains ?? options.searchDomains { log.info("DNS: Using search domains \(searchDomains.maskedDescription)") dnsSettings.domainName = searchDomains.first From fd9d34b49c2fce869d03102c9491addd570e491e Mon Sep 17 00:00:00 2001 From: Davide De Rosa Date: Fri, 22 Jan 2021 17:58:33 +0100 Subject: [PATCH 3/3] Print description of new DNS settings --- .../Protocols/OpenVPN/Configuration.swift | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift index 6448981..9727ba6 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift @@ -613,10 +613,27 @@ extension OpenVPN.Configuration { } else { log.info("\tGateway: not configured") } - if let dnsServers = dnsServers, !dnsServers.isEmpty { - log.info("\tDNS: \(dnsServers.maskedDescription)") - } else { - log.info("\tDNS: not configured") + switch dnsProtocol { + case .https: + if let dnsHTTPSURL = dnsHTTPSURL { + log.info("\tDNS over HTTPS: \(dnsHTTPSURL.maskedDescription)") + } else { + log.info("\tDNS: not configured") + } + + case .tls: + if let dnsTLSServerName = dnsTLSServerName { + log.info("\tDNS over TLS: \(dnsTLSServerName.maskedDescription)") + } else { + log.info("\tDNS: not configured") + } + + default: + if let dnsServers = dnsServers, !dnsServers.isEmpty { + log.info("\tDNS: \(dnsServers.maskedDescription)") + } else { + log.info("\tDNS: not configured") + } } if let searchDomains = searchDomains, !searchDomains.isEmpty { log.info("\tSearch domains: \(searchDomains.maskedDescription)")