// // ProviderConnectionProfile.swift // Passepartout // // Created by Davide De Rosa on 9/2/18. // Copyright (c) 2019 Davide De Rosa. All rights reserved. // // https://github.com/passepartoutvpn // // This file is part of Passepartout. // // Passepartout 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. // // Passepartout 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 Passepartout. If not, see . // import Foundation import TunnelKit public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable { public let name: Infrastructure.Name public var infrastructure: Infrastructure { return InfrastructureFactory.shared.get(name) } public var poolId: String { didSet { validateEndpoint() } } public var pool: Pool? { return infrastructure.pool(for: poolId) ?? infrastructure.pool(for: infrastructure.defaults.pool) } public var presetId: String { didSet { validateEndpoint() } } public var preset: InfrastructurePreset? { return infrastructure.preset(for: presetId) } public var manualAddress: String? public var manualProtocol: EndpointProtocol? public var usesProviderEndpoint: Bool { return (manualAddress != nil) || (manualProtocol != nil) } public init(name: Infrastructure.Name) { self.name = name poolId = "" presetId = "" username = nil poolId = infrastructure.defaults.pool presetId = infrastructure.defaults.preset } public func pools() -> [Pool] { return infrastructure.pools } private func validateEndpoint() { guard let pool = pool, let preset = preset else { manualAddress = nil manualProtocol = nil return } if let address = manualAddress, !pool.hasAddress(address) { manualAddress = nil } if let proto = manualProtocol, !preset.hasProtocol(proto) { manualProtocol = nil } } // MARK: ConnectionProfile public let context: Context = .provider public var id: String { return name.rawValue } public var username: String? public var requiresCredentials: Bool { return true } public func generate(from configuration: TunnelKitProvider.Configuration, preferences: Preferences) throws -> TunnelKitProvider.Configuration { guard let pool = pool else { preconditionFailure("Nil pool?") } guard let preset = preset else { preconditionFailure("Nil preset?") } // assert(!pool.numericAddresses.isEmpty) // XXX: copy paste, error prone var builder = preset.configuration.builder() builder.mtu = configuration.mtu builder.shouldDebug = configuration.shouldDebug builder.debugLogFormat = configuration.debugLogFormat builder.masksPrivateData = configuration.masksPrivateData do { try preset.injectExternalConfiguration(&builder, with: name, pool: pool) } catch { fatalError("Could not find external preset resources") } if let address = manualAddress { builder.prefersResolvedAddresses = true builder.resolvedAddresses = [address] } else if builder.sessionConfiguration.hostname == nil { builder.prefersResolvedAddresses = true builder.resolvedAddresses = pool.addresses() } else { builder.prefersResolvedAddresses = !preferences.resolvesHostname builder.resolvedAddresses = pool.addresses() } if let proto = manualProtocol { builder.sessionConfiguration.endpointProtocols = [proto] } else { // restrict "Any" protocol to UDP, unless there are no UDP endpoints let allEndpoints = builder.sessionConfiguration.endpointProtocols var endpoints = allEndpoints?.filter { $0.socketType == .udp } if endpoints?.isEmpty ?? true { endpoints = allEndpoints } builder.sessionConfiguration.endpointProtocols = endpoints // builder.sessionConfiguration.endpointProtocols = [ // EndpointProtocol(.udp, 8080), // EndpointProtocol(.tcp, 443) // ] } return builder.build() } public func with(newId: String) -> ConnectionProfile { fatalError("Cannot rename a ProviderConnectionProfile") } } public extension ProviderConnectionProfile { static func ==(lhs: ProviderConnectionProfile, rhs: ProviderConnectionProfile) -> Bool { return lhs.id == rhs.id } } public extension ProviderConnectionProfile { var mainAddress: String? { assert(pool != nil, "Getting provider main address but no pool set") return pool?.hostname } var addresses: [String] { return pool?.addresses() ?? [] } var protocols: [EndpointProtocol] { return preset?.configuration.sessionConfiguration.endpointProtocols ?? [] } var canCustomizeEndpoint: Bool { return true } var customAddress: String? { return manualAddress } var customProtocol: EndpointProtocol? { return manualProtocol } }