diff --git a/CHANGELOG.md b/CHANGELOG.md index 5012828..57d5488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Override DNS servers client side. [#56](https://github.com/keeshux/tunnelkit/pull/56) + ### Fixed - Compiling errors in demo target. diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift index 5e11de5..f253fc2 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider+Configuration.swift @@ -64,7 +64,8 @@ extension TunnelKitProvider { tlsWrap: nil, keepAliveInterval: nil, renegotiatesAfter: nil, - usesPIAPatches: nil + usesPIAPatches: nil, + dnsServers: nil ), shouldDebug: false, debugLogKey: nil, diff --git a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift index db65320..66c1960 100644 --- a/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift +++ b/TunnelKit/Sources/AppExtension/TunnelKitProvider.swift @@ -499,7 +499,7 @@ extension TunnelKitProvider: SessionProxyDelegate { ipv6Settings?.excludedRoutes = [] } - let dnsSettings = NEDNSSettings(servers: reply.dnsServers) + let dnsSettings = NEDNSSettings(servers: cfg.sessionConfiguration.dnsServers ?? reply.dnsServers) let newSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: remoteAddress) newSettings.ipv4Settings = ipv4Settings diff --git a/TunnelKit/Sources/Core/ConfigurationParser.swift b/TunnelKit/Sources/Core/ConfigurationParser.swift index 21c1773..d3eee1b 100644 --- a/TunnelKit/Sources/Core/ConfigurationParser.swift +++ b/TunnelKit/Sources/Core/ConfigurationParser.swift @@ -91,6 +91,8 @@ public class ConfigurationParser { static let blockEnd = NSRegularExpression("^<\\/[\\w\\-]+>") + static let dnsRegexp = NSRegularExpression("dhcp-option DNS6? [\\d\\.a-fA-F:]+") + // unsupported // static let fragment = NSRegularExpression("^fragment +\\d+") @@ -143,6 +145,7 @@ public class ConfigurationParser { var tlsStrategy: SessionProxy.TLSWrap.Strategy? var tlsKeyLines: [Substring]? var tlsWrap: SessionProxy.TLSWrap? + var dnsServers: [String]? var currentBlockName: String? var currentBlock: [String] = [] @@ -316,6 +319,16 @@ public class ConfigurationParser { } renegotiateAfterSeconds = TimeInterval(arg) } + Regex.dnsRegexp.enumerateArguments(in: line) { + isHandled = true + guard $0.count == 2 else { + return + } + if dnsServers == nil { + dnsServers = [] + } + dnsServers?.append($0[1]) + } Regex.fragment.enumerateArguments(in: line) { (_) in unsupportedError = ParsingError.unsupportedConfiguration(option: "fragment") } @@ -388,6 +401,7 @@ public class ConfigurationParser { sessionBuilder.clientKey = clientKey sessionBuilder.keepAliveInterval = keepAliveSeconds sessionBuilder.renegotiatesAfter = renegotiateAfterSeconds + sessionBuilder.dnsServers = dnsServers return ParsingResult( url: originalURL, diff --git a/TunnelKit/Sources/Core/SessionProxy+Configuration.swift b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift index ae3bb82..9bcd700 100644 --- a/TunnelKit/Sources/Core/SessionProxy+Configuration.swift +++ b/TunnelKit/Sources/Core/SessionProxy+Configuration.swift @@ -165,6 +165,9 @@ extension SessionProxy { /// Server is patched for the PIA VPN provider. public var usesPIAPatches: Bool? + /// Optionally override the server DNS entries. + public var dnsServers: [String]? + /// :nodoc: public init(ca: CryptoContainer) { cipher = .aes128cbc @@ -177,6 +180,7 @@ extension SessionProxy { keepAliveInterval = nil renegotiatesAfter = nil usesPIAPatches = false + dnsServers = nil } /** @@ -195,7 +199,8 @@ extension SessionProxy { tlsWrap: tlsWrap, keepAliveInterval: keepAliveInterval, renegotiatesAfter: renegotiatesAfter, - usesPIAPatches: usesPIAPatches + usesPIAPatches: usesPIAPatches, + dnsServers: dnsServers ) } } @@ -233,6 +238,9 @@ extension SessionProxy { /// - Seealso: `SessionProxy.ConfigurationBuilder.usesPIAPatches` public let usesPIAPatches: Bool? + /// - Seealso: `SessionProxy.ConfigurationBuilder.dnsServers` + public let dnsServers: [String]? + /** Returns a `SessionProxy.ConfigurationBuilder` to use this configuration as a starting point for a new one. @@ -249,6 +257,7 @@ extension SessionProxy { builder.keepAliveInterval = keepAliveInterval builder.renegotiatesAfter = renegotiatesAfter builder.usesPIAPatches = usesPIAPatches + builder.dnsServers = dnsServers return builder } @@ -265,7 +274,8 @@ extension SessionProxy { (lhs.compressionFraming == rhs.compressionFraming) && (lhs.keepAliveInterval == rhs.keepAliveInterval) && (lhs.renegotiatesAfter == rhs.renegotiatesAfter) && - (lhs.usesPIAPatches == rhs.usesPIAPatches) + (lhs.usesPIAPatches == rhs.usesPIAPatches) && + (lhs.dnsServers == rhs.dnsServers) } } } diff --git a/TunnelKitTests/ConfigurationParserTests.swift b/TunnelKitTests/ConfigurationParserTests.swift index fb2ee6a..16edcb6 100644 --- a/TunnelKitTests/ConfigurationParserTests.swift +++ b/TunnelKitTests/ConfigurationParserTests.swift @@ -27,6 +27,8 @@ import XCTest import TunnelKit class ConfigurationParserTests: XCTestCase { + let base: [String] = ["", "", "remote 1.2.3.4"] + override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. @@ -55,8 +57,6 @@ class ConfigurationParserTests: XCTestCase { } func testCompression() throws { - let base: [String] = ["", "", "remote 1.2.3.4"] - XCTAssertNotNil(try ConfigurationParser.parsed(fromLines: base + ["comp-lzo"]).warning) XCTAssertNoThrow(try ConfigurationParser.parsed(fromLines: base + ["comp-lzo no"])) XCTAssertThrowsError(try ConfigurationParser.parsed(fromLines: base + ["comp-lzo yes"])) @@ -65,6 +65,14 @@ class ConfigurationParserTests: XCTestCase { XCTAssertThrowsError(try ConfigurationParser.parsed(fromLines: base + ["compress lzo"])) } + func testDHCPOption() throws { + let lines = base + ["dhcp-option DNS 8.8.8.8", "dhcp-option DNS6 ffff::1"] + XCTAssertNoThrow(try ConfigurationParser.parsed(fromLines: lines)) + + let parsed = try! ConfigurationParser.parsed(fromLines: lines) + XCTAssertEqual(parsed.configuration.dnsServers, ["8.8.8.8", "ffff::1"]) + } + private func url(withName name: String) -> URL { return Bundle(for: ConfigurationParserTests.self).url(forResource: name, withExtension: "ovpn")! }