Skip to content

Commit

Permalink
Merge pull request #109 from ivpn/feature/dns-over-https-tls-inside-vpn
Browse files Browse the repository at this point in the history
DNS over HTTPS/TLS inside VPN tunnel
  • Loading branch information
jurajhilje authored Mar 22, 2021
2 parents 28a6e95 + 58cfff9 commit 9fee3c7
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 116 deletions.
8 changes: 8 additions & 0 deletions IVPNClient.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@
82E81AE72449C44F00D81FB7 /* PaymentComponentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82E81AE62449C44F00D81FB7 /* PaymentComponentView.swift */; };
82E96A4B224BA1B3004FC1D6 /* ExtensionKeyManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82E96A4A224BA1B3004FC1D6 /* ExtensionKeyManagerTests.swift */; };
82EA857A21DCF0C300EB0EC9 /* StorageManager+OnDemandRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82EA857921DCF0C300EB0EC9 /* StorageManager+OnDemandRuleTests.swift */; };
82EEB6C625F9398600915837 /* DNSProtocolType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82EEB6C525F9398600915837 /* DNSProtocolType.swift */; };
82EEB6CD25F9422900915837 /* DNSProtocolType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82EEB6C525F9398600915837 /* DNSProtocolType.swift */; };
82EEB6DB25F961BC00915837 /* URL+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CE598F25ED3C7A0078099D /* URL+Ext.swift */; };
82F189A2225CE8A90038ABA0 /* UIView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F189A1225CE8A90038ABA0 /* UIView+Ext.swift */; };
82F638C2217DA89000410318 /* AddressType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F638C1217DA89000410318 /* AddressType.swift */; };
82F638C5217DA89000410318 /* AddressType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82F638C1217DA89000410318 /* AddressType.swift */; };
Expand Down Expand Up @@ -597,6 +600,7 @@
82E81AE62449C44F00D81FB7 /* PaymentComponentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentComponentView.swift; sourceTree = "<group>"; };
82E96A4A224BA1B3004FC1D6 /* ExtensionKeyManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionKeyManagerTests.swift; sourceTree = "<group>"; };
82EA857921DCF0C300EB0EC9 /* StorageManager+OnDemandRuleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StorageManager+OnDemandRuleTests.swift"; sourceTree = "<group>"; };
82EEB6C525F9398600915837 /* DNSProtocolType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSProtocolType.swift; sourceTree = "<group>"; };
82F189A1225CE8A90038ABA0 /* UIView+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Ext.swift"; sourceTree = "<group>"; };
82F638C1217DA89000410318 /* AddressType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressType.swift; sourceTree = "<group>"; };
82F638C6217DC22300410318 /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1057,6 +1061,7 @@
8229209F2480FA3600476FC1 /* ServersSort.swift */,
826FBDA22461847D00B9E464 /* ServiceType.swift */,
826FBDA42461848A00B9E464 /* ServiceDuration.swift */,
82EEB6C525F9398600915837 /* DNSProtocolType.swift */,
);
path = Enums;
sourceTree = "<group>";
Expand Down Expand Up @@ -2011,6 +2016,7 @@
82E716882181E8E100D6B7C2 /* ProviderConfigurationKeys.swift in Sources */,
8223C5402232694E006C4F14 /* KeyChain.swift in Sources */,
82F638C5217DA89000410318 /* AddressType.swift in Sources */,
82EEB6DB25F961BC00915837 /* URL+Ext.swift in Sources */,
822919712182EB1C00978BBA /* String+Ext.swift in Sources */,
8223C53A22325709006C4F14 /* ApiManager.swift in Sources */,
820535292302BE8F007BDD58 /* APIAccessManager.swift in Sources */,
Expand All @@ -2031,6 +2037,7 @@
8223C55022EAEC7100CD283D /* Session.swift in Sources */,
8223C53E22326321006C4F14 /* x25519.c in Sources */,
826C56D422FD551900D2B76A /* ServiceStatus.swift in Sources */,
82EEB6CD25F9422900915837 /* DNSProtocolType.swift in Sources */,
820EA86C2322430700E16B2D /* Result.swift in Sources */,
823FFB092338E09A00F91A5D /* Capability.swift in Sources */,
82DC75BD22B7647500D3C73C /* APIClient.swift in Sources */,
Expand Down Expand Up @@ -2240,6 +2247,7 @@
8285D253246D28FA0088C00F /* AnimatedCircleLayer.swift in Sources */,
9CBFF0302102254800FE1757 /* Settings.swift in Sources */,
9C3031371DB4307D00C38B0C /* SettingsViewController.swift in Sources */,
82EEB6C625F9398600915837 /* DNSProtocolType.swift in Sources */,
821F1C7E21FF544200107311 /* VPNServerViewModel.swift in Sources */,
82F189A2225CE8A90038ABA0 /* UIView+Ext.swift in Sources */,
82DEF01E244714D900CCB5CD /* ScannerViewController.swift in Sources */,
Expand Down
79 changes: 79 additions & 0 deletions IVPNClient/Enums/DNSProtocolType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// DNSProtocolType.swift
// IVPN iOS app
// https://github.com/ivpn/ios-app
//
// Created by Juraj Hilje on 2021-03-10.
// Copyright (c) 2021 Privatus Limited.
//
// This file is part of the IVPN iOS app.
//
// The IVPN iOS app 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.
//
// The IVPN iOS app 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 the IVPN iOS app. If not, see <https://www.gnu.org/licenses/>.
//

import Foundation

enum DNSProtocolType: String {

case doh
case dot
case plain

static func getServerURL(address: String) -> String? {
var serverURL = address

if !address.hasPrefix("https://") {
serverURL = "https://\(serverURL)"
}

if !address.hasSuffix("/dns-query") {
serverURL = "\(serverURL)/dns-query"
}

return serverURL
}

static func getServerName(address: String) -> String {
var serverName = address

if !address.hasPrefix("https://") {
serverName = "https://\(serverName)"
}

if let serverURL = URL.init(string: serverName) {
if let host = serverURL.host {
do {
let ipAddress = try CIDRAddress(stringRepresentation: host)
return ipAddress?.ipAddress ?? address
} catch {}
}

return serverURL.getTopLevelDomain()
}

return address
}

static func preferred() -> DNSProtocolType {
guard !UserDefaults.shared.isAntiTracker else {
return .plain
}

return DNSProtocolType.init(rawValue: UserDefaults.shared.customDNSProtocol) ?? .plain
}

static func save(preferred: DNSProtocolType) {
UserDefaults.shared.setValue(preferred.rawValue, forKey: UserDefaults.Key.customDNSProtocol)
}

}
21 changes: 14 additions & 7 deletions IVPNClient/Managers/DNSManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,33 @@ class DNSManager {
}
}

func saveResolvedDNS(server: String) {
static func saveResolvedDNS(server: String, key: String) {
DNSResolver.resolve(host: server) { list in
var addresses: [String]? = nil
var addresses: [String] = []

for ip in list {
if let host = ip.host {
addresses?.append(host)
addresses.append(host)
}
}

UserDefaults.standard.set(addresses, forKey: UserDefaults.Key.resolvedDNS)
NotificationCenter.default.post(name: Notification.Name.UpdateResolvedDNS, object: nil)
switch key {
case UserDefaults.Key.resolvedDNSOutsideVPN:
UserDefaults.standard.set(addresses, forKey: UserDefaults.Key.resolvedDNSOutsideVPN)
NotificationCenter.default.post(name: Notification.Name.UpdateResolvedDNS, object: nil)
case UserDefaults.Key.resolvedDNSInsideVPN:
UserDefaults.shared.set(addresses, forKey: UserDefaults.Key.resolvedDNSInsideVPN)
default:
break
}
}
}

// MARK: - Private methods -

private func getDnsSettings(model: SecureDNS) -> NEDNSSettings {
let servers = UserDefaults.standard.value(forKey: UserDefaults.Key.resolvedDNS) as? [String] ?? []
let type = SecureDNSType.init(rawValue: model.type)
let servers = UserDefaults.standard.value(forKey: UserDefaults.Key.resolvedDNSOutsideVPN) as? [String] ?? []
let type = DNSProtocolType.init(rawValue: model.type)

if type == .dot {
let dnsSettings = NEDNSOverTLSSettings(servers: servers)
Expand Down
50 changes: 4 additions & 46 deletions IVPNClient/Models/SecureDNS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ struct SecureDNS: Codable {
var address: String? {
didSet {
if let address = address {
serverURL = getServerURL(address: address)
serverName = getServerName(address: address)
serverURL = DNSProtocolType.getServerURL(address: address)
serverName = DNSProtocolType.getServerName(address: address)
if #available(iOS 14.0, *) {
DNSManager.shared.saveResolvedDNS(server: address)
DNSManager.saveResolvedDNS(server: address, key: UserDefaults.Key.resolvedDNSOutsideVPN)
}
} else {
serverURL = nil
Expand Down Expand Up @@ -105,52 +105,10 @@ struct SecureDNS: Codable {

func validation() -> (Bool, String?) {
guard let address = address, !address.isEmpty else {
return (false, "Please enter DNS server")
return (false, "Please enter DNS server info")
}

return (true, nil)
}

// MARK: - Private methods -

private func getServerURL(address: String) -> String? {
var serverURL = address

if !address.hasPrefix("https://") {
serverURL = "https://\(serverURL)"
}

if !address.hasSuffix("/dns-query") {
serverURL = "\(serverURL)/dns-query"
}

return serverURL
}

private func getServerName(address: String) -> String {
var serverName = address

if !address.hasPrefix("https://") {
serverName = "https://\(serverName)"
}

if let serverURL = URL.init(string: serverName) {
if let host = serverURL.host {
do {
let ipAddress = try CIDRAddress(stringRepresentation: host)
return ipAddress?.ipAddress ?? address
} catch {}
}

return serverURL.getTopLevelDomain()
}

return address
}

}

enum SecureDNSType: String {
case doh
case dot
}
Loading

0 comments on commit 9fee3c7

Please sign in to comment.