diff --git a/Passepartout/Sources/Model/ConnectionService.swift b/Passepartout/Sources/Model/ConnectionService.swift index 3d687e53..8e6ca9a9 100644 --- a/Passepartout/Sources/Model/ConnectionService.swift +++ b/Passepartout/Sources/Model/ConnectionService.swift @@ -265,22 +265,22 @@ public class ConnectionService: Codable { case .provider: let providerProfile = try decoder.decode(ProviderConnectionProfile.self, from: data) - // fix renamed presets, fall back to default + // XXX: fix renamed presets, fall back to default if providerProfile.preset == nil { providerProfile.presetId = providerProfile.infrastructure.defaults.preset } - // fix renamed pool, fall back to default + // XXX: fix renamed pool, fall back to default if providerProfile.pool == nil, let fallbackPool = providerProfile.infrastructure.defaultPool() { providerProfile.poolId = fallbackPool.id } - + profile = providerProfile case .host: let hostProfile = try decoder.decode(HostConnectionProfile.self, from: data) - // migrate old endpointProtocols + // XXX: migrate old endpointProtocols if hostProfile.parameters.sessionConfiguration.endpointProtocols == nil { var sessionBuilder = hostProfile.parameters.sessionConfiguration.builder() sessionBuilder.endpointProtocols = hostProfile.parameters.endpointProtocols @@ -288,6 +288,8 @@ public class ConnectionService: Codable { parametersBuilder.sessionConfiguration = sessionBuilder.build() hostProfile.parameters = parametersBuilder.build() } + + // XXX: re-read routing policies for profile = hostProfile } @@ -329,6 +331,37 @@ public class ConnectionService: Codable { return url.deletingPathExtension().lastPathComponent } + func reloadHostProfilesFromConfigurationFiles() -> Bool { + var anyReloaded = false + for entry in cache { + guard entry.value.context == .host else { + continue + } + guard let host = profile(withKey: entry.key) as? HostConnectionProfile else { + log.warning("Host context but not a HostConnectionProfile?") + continue + } + guard let url = configurationURL(for: entry.key) else { + continue + } + + // can fail due to passphrase (migration is non-interactive) + if let result = try? ConfigurationParser.parsed(fromURL: url) { + host.parameters = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: result.configuration).build() + } else { + + // fall back to the safer option + var builder = host.parameters.builder() + builder.sessionConfiguration.routingPolicies = [.IPv4, .IPv6] + host.parameters = builder.build() + } + cache[entry.key] = host + + anyReloaded = true + } + return anyReloaded + } + // MARK: Profiles public func hasProfiles() -> Bool { diff --git a/Passepartout/Sources/Model/TransientStore.swift b/Passepartout/Sources/Model/TransientStore.swift index 0f284a19..35aace91 100644 --- a/Passepartout/Sources/Model/TransientStore.swift +++ b/Passepartout/Sources/Model/TransientStore.swift @@ -34,6 +34,10 @@ public class TransientStore { static let didHandleSubreddit = "DidHandleSubreddit" static let masksPrivateData = "MasksPrivateData" + + // migrations + + static let didMigrateHostsRoutingPolicies = "DidMigrateHostsRoutingPolicies" } public static let shared = TransientStore() @@ -62,6 +66,15 @@ public class TransientStore { } } + public static var didMigrateHostsRoutingPolicies: Bool { + get { + return UserDefaults.standard.bool(forKey: Keys.didMigrateHostsRoutingPolicies) + } + set { + UserDefaults.standard.set(newValue, forKey: Keys.didMigrateHostsRoutingPolicies) + } + } + public static var baseVPNConfiguration: TunnelKitProvider.ConfigurationBuilder { let sessionBuilder = SessionProxy.ConfigurationBuilder() var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build()) @@ -84,7 +97,7 @@ public class TransientStore { // this must be graceful ConnectionService.migrateJSON(from: TransientStore.serviceURL, to: TransientStore.serviceURL) - + let cfg = TransientStore.baseVPNConfiguration.build() do { let data = try Data(contentsOf: TransientStore.serviceURL) @@ -95,6 +108,14 @@ public class TransientStore { service = try JSONDecoder().decode(ConnectionService.self, from: data) service.baseConfiguration = cfg service.loadProfiles() + + // do migrations + if !TransientStore.didMigrateHostsRoutingPolicies { + if service.reloadHostProfilesFromConfigurationFiles() { + service.saveProfiles() + } + TransientStore.didMigrateHostsRoutingPolicies = true + } } catch let e { log.error("Could not decode service: \(e)") service = ConnectionService( diff --git a/Passepartout/Sources/Services/InfrastructurePreset.swift b/Passepartout/Sources/Services/InfrastructurePreset.swift index d37d581d..87baad20 100644 --- a/Passepartout/Sources/Services/InfrastructurePreset.swift +++ b/Passepartout/Sources/Services/InfrastructurePreset.swift @@ -162,6 +162,9 @@ public struct InfrastructurePreset: Codable { sessionBuilder.randomizeEndpoint = try cfgContainer.decodeIfPresent(Bool.self, forKey: .randomizeEndpoint) ?? false sessionBuilder.usesPIAPatches = try cfgContainer.decodeIfPresent(Bool.self, forKey: .usesPIAPatches) ?? false + // XXX: redirect everything through the VPN for providers + sessionBuilder.routingPolicies = [.IPv4, .IPv6] + let builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build()) configuration = builder.build() }