VPN: Refactor use of DNS resolution while activating the tunnel

Signed-off-by: Roopesh Chander <roop@roopc.net>
This commit is contained in:
Roopesh Chander
2018-10-30 16:34:46 +05:30
parent ce20a17e51
commit fd241fac40
+56 -40
View File
@@ -258,58 +258,74 @@ class TunnelContainer: NSObject {
fileprivate func startActivation(completionHandler: @escaping (Error?) -> Void) { fileprivate func startActivation(completionHandler: @escaping (Error?) -> Void) {
assert(status == .inactive) assert(status == .inactive)
assert(self.dnsResolver == nil)
guard let tunnelConfiguration = tunnelConfiguration() else { fatalError() } guard let tunnelConfiguration = tunnelConfiguration() else { fatalError() }
let endpoints = tunnelConfiguration.peers.map { $0.endpoint } let endpoints = tunnelConfiguration.peers.map { $0.endpoint }
let dnsResolver = DNSResolver(endpoints: endpoints)
assert(self.dnsResolver == nil) // Ensure there's a tunner server address we can give to iOS
if let endpoints = dnsResolver.resolveWithoutNetworkRequests() { guard (endpoints.contains(where: { $0 != nil })) else {
guard (endpoints.contains(where: { $0 != nil })) else { DispatchQueue.main.async { [weak self] in
self?.status = .inactive
completionHandler(TunnelsManagerError.noEndpoint) completionHandler(TunnelsManagerError.noEndpoint)
status = .inactive
return
}
self.tunnelProvider.loadFromPreferences { [weak self] (error) in
guard let s = self else { return }
s.startObservingTunnelStatus()
let session = (s.tunnelProvider.connection as! NETunnelProviderSession)
do {
let tunnelOptions = PacketTunnelOptionsGenerator.generateOptions(
from: tunnelConfiguration, withResolvedEndpoints: endpoints)
try session.startTunnel(options: tunnelOptions)
} catch (let error) {
os_log("Failed to activate tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error)")
completionHandler(error)
s.status = .inactive
return
}
completionHandler(nil)
} }
return
}
// Resolve DNS and start the tunnel
let dnsResolver = DNSResolver(endpoints: endpoints)
let resolvedEndpoints = dnsResolver.resolveWithoutNetworkRequests()
if let resolvedEndpoints = resolvedEndpoints {
// If we don't have to make a DNS network request, we never
// change the status to .resolvingEndpointDomains
startActivation(tunnelConfiguration: tunnelConfiguration,
resolvedEndpoints: resolvedEndpoints,
completionHandler: completionHandler)
} else { } else {
self.dnsResolver = dnsResolver
status = .resolvingEndpointDomains status = .resolvingEndpointDomains
dnsResolver.resolve { [weak self] endpoints in self.dnsResolver = dnsResolver
dnsResolver.resolve { [weak self] resolvedEndpoints in
guard let s = self else { return } guard let s = self else { return }
assert(s.status == .resolvingEndpointDomains) assert(s.status == .resolvingEndpointDomains)
s.dnsResolver = nil s.dnsResolver = nil
guard let endpoints = endpoints else { guard let resolvedEndpoints = resolvedEndpoints else {
completionHandler(TunnelsManagerError.dnsResolutionFailed)
s.status = .inactive s.status = .inactive
completionHandler(TunnelsManagerError.dnsResolutionFailed)
return return
} }
s.tunnelProvider.loadFromPreferences { [weak s] (error) in s.startActivation(tunnelConfiguration: tunnelConfiguration,
guard let s = s else { return } resolvedEndpoints: resolvedEndpoints,
s.startObservingTunnelStatus() completionHandler: completionHandler)
let session = (s.tunnelProvider.connection as! NETunnelProviderSession) }
do { }
let tunnelOptions = PacketTunnelOptionsGenerator.generateOptions( }
from: tunnelConfiguration, withResolvedEndpoints: endpoints)
try session.startTunnel(options: tunnelOptions) fileprivate func startActivation(tunnelConfiguration: TunnelConfiguration,
} catch (let error) { resolvedEndpoints: [Endpoint?],
os_log("Failed to activate tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error)") completionHandler: @escaping (Error?) -> Void) {
s.status = .inactive // resolvedEndpoints should contain only IP addresses, not any named endpoints
return assert(resolvedEndpoints.allSatisfy { (resolvedEndpoint) in
} guard let resolvedEndpoint = resolvedEndpoint else { return true }
} switch (resolvedEndpoint.host) {
case .ipv4(_): return true
case .ipv6(_): return true
case .name(_, _): return false
}
})
// Start the tunnel
self.tunnelProvider.loadFromPreferences { [weak self] (error) in
guard let s = self else { return }
s.startObservingTunnelStatus()
let session = (s.tunnelProvider.connection as! NETunnelProviderSession)
do {
let tunnelOptions = PacketTunnelOptionsGenerator.generateOptions(
from: tunnelConfiguration, withResolvedEndpoints: resolvedEndpoints)
try session.startTunnel(options: tunnelOptions)
completionHandler(nil)
} catch (let error) {
os_log("Failed to activate tunnel: %{public}@", log: OSLog.default, type: .debug, "\(error)")
s.status = .inactive
completionHandler(error)
} }
} }
} }