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")!
}