From c6cb5a646aa5f9b29d0a0fdee9dd3a0449cf8a0c Mon Sep 17 00:00:00 2001 From: ThinkChaos Date: Mon, 21 Oct 2019 21:47:45 +0200 Subject: [PATCH 1/2] Add Proxy Auto-Configuration (PAC) support --- .../OpenVPNTunnelProvider+Configuration.swift | 8 ++++++++ .../AppExtension/OpenVPNTunnelProvider.swift | 19 +++++++++++++++---- .../Protocols/OpenVPN/Configuration.swift | 12 ++++++++++-- .../OpenVPN/ConfigurationParser.swift | 17 +++++++++++++++-- .../OpenVPN/ConfigurationParserTests.swift | 2 ++ 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift index 0f2e382..e111af9 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift @@ -181,6 +181,8 @@ extension OpenVPNTunnelProvider { static let httpsProxy = "HTTPSProxy" + static let proxyAutoConfURL = "proxyAutoConfURL" + static let proxyBypassDomains = "ProxyBypassDomains" static let routingPolicies = "RoutingPolicies" @@ -595,6 +597,9 @@ private extension OpenVPN.Configuration { if let httpsProxy = httpsProxy { dict[S.httpsProxy] = httpsProxy.rawValue } + if let proxyAutoConfURL = proxyAutoConfURL { + dict[S.proxyAutoConfURL] = proxyAutoConfURL.absoluteString + } if let proxyBypassDomains = proxyBypassDomains { dict[S.proxyBypassDomains] = proxyBypassDomains } @@ -668,6 +673,9 @@ private extension OpenVPN.Configuration { if let httpsProxy = httpsProxy { log.info("\tHTTPS proxy: \(httpsProxy.maskedDescription)") } + if let proxyAutoConfURL = proxyAutoConfURL { + log.info("\tPAC: \(proxyAutoConfURL)") + } if let proxyBypassDomains = proxyBypassDomains { log.info("\tProxy bypass domains: \(proxyBypassDomains.maskedDescription)") } diff --git a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift index 958632c..722adf1 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift @@ -514,6 +514,9 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate { if let proxy = options.httpsProxy { log.info("\t\tHTTPS: \(proxy.maskedDescription)") } + if let pacURL = options.proxyAutoConfURL { + log.info("\t\tPAC: \(pacURL)") + } if let bypass = options.proxyBypassDomains { log.info("\t\tBypass domains: \(bypass.maskedDescription)") } @@ -665,18 +668,26 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate { var proxySettings: NEProxySettings? if let httpsProxy = cfg.sessionConfiguration.httpsProxy ?? options.httpsProxy { proxySettings = NEProxySettings() - proxySettings?.httpsServer = httpsProxy.neProxy() - proxySettings?.httpsEnabled = true + proxySettings!.httpsServer = httpsProxy.neProxy() + proxySettings!.httpsEnabled = true log.info("Routing: Setting HTTPS proxy \(httpsProxy.address.maskedDescription):\(httpsProxy.port)") } if let httpProxy = cfg.sessionConfiguration.httpProxy ?? options.httpProxy { if proxySettings == nil { proxySettings = NEProxySettings() } - proxySettings?.httpServer = httpProxy.neProxy() - proxySettings?.httpEnabled = true + proxySettings!.httpServer = httpProxy.neProxy() + proxySettings!.httpEnabled = true log.info("Routing: Setting HTTP proxy \(httpProxy.address.maskedDescription):\(httpProxy.port)") } + if let pacURL = cfg.sessionConfiguration.proxyAutoConfURL ?? options.proxyAutoConfURL { + if proxySettings == nil { + proxySettings = NEProxySettings() + } + proxySettings!.proxyAutoConfigurationURL = pacURL + proxySettings!.autoProxyConfigurationEnabled = true + log.info("Routing: Setting PAC \(pacURL)") + } // only set if there is a proxy (proxySettings set to non-nil above) if let bypass = cfg.sessionConfiguration.proxyBypassDomains ?? options.proxyBypassDomains { diff --git a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift index 7970527..0155c2d 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift @@ -244,9 +244,12 @@ extension OpenVPN { /// The search domain. public var searchDomain: String? + /// The Proxy Auto-Configuration (PAC) url. + public var proxyAutoConfURL: URL? + /// The HTTP proxy. public var httpProxy: Proxy? - + /// The HTTPS proxy. public var httpsProxy: Proxy? @@ -291,6 +294,7 @@ extension OpenVPN { searchDomain: searchDomain, httpProxy: httpProxy, httpsProxy: httpsProxy, + proxyAutoConfURL: proxyAutoConfURL, proxyBypassDomains: proxyBypassDomains, routingPolicies: routingPolicies ) @@ -385,10 +389,13 @@ extension OpenVPN { /// - Seealso: `ConfigurationBuilder.httpProxy` public let httpProxy: Proxy? - + /// - Seealso: `ConfigurationBuilder.httpsProxy` public let httpsProxy: Proxy? + /// - Seealso: `ConfigurationBuilder.proxyAutoConfURL` + public let proxyAutoConfURL: URL? + /// - Seealso: `ConfigurationBuilder.proxyBypassDomains` public let proxyBypassDomains: [String]? @@ -449,6 +456,7 @@ extension OpenVPN.Configuration { builder.searchDomain = searchDomain builder.httpProxy = httpProxy builder.httpsProxy = httpsProxy + builder.proxyAutoConfURL = proxyAutoConfURL builder.proxyBypassDomains = proxyBypassDomains builder.routingPolicies = routingPolicies return builder diff --git a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift index 03225a1..70a2b62 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift @@ -94,7 +94,7 @@ extension OpenVPN { static let domain = NSRegularExpression("^dhcp-option +DOMAIN +[^ ]+") - static let proxy = NSRegularExpression("^dhcp-option +PROXY_(HTTPS?) +[^ ]+ +\\d+") + static let proxy = NSRegularExpression("^dhcp-option +PROXY_(HTTPS? +[^ ]+ +\\d+|AUTO_CONFIG_URL +[^ ]+)") static let proxyBypass = NSRegularExpression("^dhcp-option +PROXY_BYPASS +.+") @@ -225,6 +225,7 @@ extension OpenVPN { var optSearchDomain: String? var optHTTPProxy: Proxy? var optHTTPSProxy: Proxy? + var optProxyAutoConfURL: URL? var optProxyBypass: [String]? var optRedirectGateway: Set? @@ -517,6 +518,17 @@ extension OpenVPN { optSearchDomain = $0[1] } Regex.proxy.enumerateArguments(in: line) { + if $0.count == 2 { + let maybeURL = URL(string: $0[1]) + if maybeURL != nil { + optProxyAutoConfURL = maybeURL! + } + else { + unsupportedError = ConfigurationError.malformed(option: "dhcp-option PROXY_AUTO_CONFIG_URL has malformed URL") + } + return + } + guard $0.count == 3, let port = UInt16($0[2]) else { return } @@ -526,7 +538,7 @@ extension OpenVPN { case "PROXY_HTTP": optHTTPProxy = Proxy($0[1], port) - + default: break } @@ -714,6 +726,7 @@ extension OpenVPN { sessionBuilder.searchDomain = optSearchDomain sessionBuilder.httpProxy = optHTTPProxy sessionBuilder.httpsProxy = optHTTPSProxy + sessionBuilder.proxyAutoConfURL = optProxyAutoConfURL sessionBuilder.proxyBypassDomains = optProxyBypass if let flags = optRedirectGateway { diff --git a/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift b/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift index 34fd0d0..2664a91 100644 --- a/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift +++ b/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift @@ -56,6 +56,7 @@ class ConfigurationParserTests: XCTestCase { "dhcp-option DOMAIN example.com", "dhcp-option PROXY_HTTP 1.2.3.4 8081", "dhcp-option PROXY_HTTPS 7.8.9.10 8082", + "dhcp-option PROXY_AUTO_CONFIG_URL https://pac/", "dhcp-option PROXY_BYPASS foo.com bar.org net.chat" ] XCTAssertNoThrow(try OpenVPN.ConfigurationParser.parsed(fromLines: lines)) @@ -67,6 +68,7 @@ class ConfigurationParserTests: XCTestCase { XCTAssertEqual(parsed.httpProxy?.port, 8081) XCTAssertEqual(parsed.httpsProxy?.address, "7.8.9.10") XCTAssertEqual(parsed.httpsProxy?.port, 8082) + XCTAssertEqual(parsed.proxyAutoConfURL?.absoluteString, "https://pac/") XCTAssertEqual(parsed.proxyBypassDomains, ["foo.com", "bar.org", "net.chat"]) } From 26d7b9fe0f7863e66fc422468fe833741411d7a8 Mon Sep 17 00:00:00 2001 From: ThinkChaos Date: Tue, 22 Oct 2019 21:03:25 +0200 Subject: [PATCH 2/2] Address review comments --- .../OpenVPNTunnelProvider+Configuration.swift | 10 +++++----- .../AppExtension/OpenVPNTunnelProvider.swift | 16 ++++++++-------- .../Protocols/OpenVPN/Configuration.swift | 10 +++++----- .../Protocols/OpenVPN/ConfigurationParser.swift | 12 +++++------- .../OpenVPN/ConfigurationParserTests.swift | 2 +- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift index e111af9..0d433ba 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider+Configuration.swift @@ -181,7 +181,7 @@ extension OpenVPNTunnelProvider { static let httpsProxy = "HTTPSProxy" - static let proxyAutoConfURL = "proxyAutoConfURL" + static let proxyAutoConfigurationURL = "ProxyAutoConfigurationURL" static let proxyBypassDomains = "ProxyBypassDomains" @@ -597,8 +597,8 @@ private extension OpenVPN.Configuration { if let httpsProxy = httpsProxy { dict[S.httpsProxy] = httpsProxy.rawValue } - if let proxyAutoConfURL = proxyAutoConfURL { - dict[S.proxyAutoConfURL] = proxyAutoConfURL.absoluteString + if let proxyAutoConfigurationURL = proxyAutoConfigurationURL { + dict[S.proxyAutoConfigurationURL] = proxyAutoConfigurationURL.absoluteString } if let proxyBypassDomains = proxyBypassDomains { dict[S.proxyBypassDomains] = proxyBypassDomains @@ -673,8 +673,8 @@ private extension OpenVPN.Configuration { if let httpsProxy = httpsProxy { log.info("\tHTTPS proxy: \(httpsProxy.maskedDescription)") } - if let proxyAutoConfURL = proxyAutoConfURL { - log.info("\tPAC: \(proxyAutoConfURL)") + if let proxyAutoConfigurationURL = proxyAutoConfigurationURL { + log.info("\tPAC: \(proxyAutoConfigurationURL)") } if let proxyBypassDomains = proxyBypassDomains { log.info("\tProxy bypass domains: \(proxyBypassDomains.maskedDescription)") diff --git a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift index 722adf1..a9a7829 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/AppExtension/OpenVPNTunnelProvider.swift @@ -514,7 +514,7 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate { if let proxy = options.httpsProxy { log.info("\t\tHTTPS: \(proxy.maskedDescription)") } - if let pacURL = options.proxyAutoConfURL { + if let pacURL = options.proxyAutoConfigurationURL { log.info("\t\tPAC: \(pacURL)") } if let bypass = options.proxyBypassDomains { @@ -668,24 +668,24 @@ extension OpenVPNTunnelProvider: OpenVPNSessionDelegate { var proxySettings: NEProxySettings? if let httpsProxy = cfg.sessionConfiguration.httpsProxy ?? options.httpsProxy { proxySettings = NEProxySettings() - proxySettings!.httpsServer = httpsProxy.neProxy() - proxySettings!.httpsEnabled = true + proxySettings?.httpsServer = httpsProxy.neProxy() + proxySettings?.httpsEnabled = true log.info("Routing: Setting HTTPS proxy \(httpsProxy.address.maskedDescription):\(httpsProxy.port)") } if let httpProxy = cfg.sessionConfiguration.httpProxy ?? options.httpProxy { if proxySettings == nil { proxySettings = NEProxySettings() } - proxySettings!.httpServer = httpProxy.neProxy() - proxySettings!.httpEnabled = true + proxySettings?.httpServer = httpProxy.neProxy() + proxySettings?.httpEnabled = true log.info("Routing: Setting HTTP proxy \(httpProxy.address.maskedDescription):\(httpProxy.port)") } - if let pacURL = cfg.sessionConfiguration.proxyAutoConfURL ?? options.proxyAutoConfURL { + if let pacURL = cfg.sessionConfiguration.proxyAutoConfigurationURL ?? options.proxyAutoConfigurationURL { if proxySettings == nil { proxySettings = NEProxySettings() } - proxySettings!.proxyAutoConfigurationURL = pacURL - proxySettings!.autoProxyConfigurationEnabled = true + proxySettings?.proxyAutoConfigurationURL = pacURL + proxySettings?.autoProxyConfigurationEnabled = true log.info("Routing: Setting PAC \(pacURL)") } diff --git a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift index 0155c2d..725102d 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/Configuration.swift @@ -245,7 +245,7 @@ extension OpenVPN { public var searchDomain: String? /// The Proxy Auto-Configuration (PAC) url. - public var proxyAutoConfURL: URL? + public var proxyAutoConfigurationURL: URL? /// The HTTP proxy. public var httpProxy: Proxy? @@ -294,7 +294,7 @@ extension OpenVPN { searchDomain: searchDomain, httpProxy: httpProxy, httpsProxy: httpsProxy, - proxyAutoConfURL: proxyAutoConfURL, + proxyAutoConfigurationURL: proxyAutoConfigurationURL, proxyBypassDomains: proxyBypassDomains, routingPolicies: routingPolicies ) @@ -393,8 +393,8 @@ extension OpenVPN { /// - Seealso: `ConfigurationBuilder.httpsProxy` public let httpsProxy: Proxy? - /// - Seealso: `ConfigurationBuilder.proxyAutoConfURL` - public let proxyAutoConfURL: URL? + /// - Seealso: `ConfigurationBuilder.proxyAutoConfigurationURL` + public let proxyAutoConfigurationURL: URL? /// - Seealso: `ConfigurationBuilder.proxyBypassDomains` public let proxyBypassDomains: [String]? @@ -456,7 +456,7 @@ extension OpenVPN.Configuration { builder.searchDomain = searchDomain builder.httpProxy = httpProxy builder.httpsProxy = httpsProxy - builder.proxyAutoConfURL = proxyAutoConfURL + builder.proxyAutoConfigurationURL = proxyAutoConfigurationURL builder.proxyBypassDomains = proxyBypassDomains builder.routingPolicies = routingPolicies return builder diff --git a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift index 70a2b62..1baea13 100644 --- a/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift +++ b/TunnelKit/Sources/Protocols/OpenVPN/ConfigurationParser.swift @@ -225,7 +225,7 @@ extension OpenVPN { var optSearchDomain: String? var optHTTPProxy: Proxy? var optHTTPSProxy: Proxy? - var optProxyAutoConfURL: URL? + var optProxyAutoConfigurationURL: URL? var optProxyBypass: [String]? var optRedirectGateway: Set? @@ -519,13 +519,11 @@ extension OpenVPN { } Regex.proxy.enumerateArguments(in: line) { if $0.count == 2 { - let maybeURL = URL(string: $0[1]) - if maybeURL != nil { - optProxyAutoConfURL = maybeURL! - } - else { + guard let url = URL(string: $0[1]) else { unsupportedError = ConfigurationError.malformed(option: "dhcp-option PROXY_AUTO_CONFIG_URL has malformed URL") + return } + optProxyAutoConfigurationURL = url return } @@ -726,7 +724,7 @@ extension OpenVPN { sessionBuilder.searchDomain = optSearchDomain sessionBuilder.httpProxy = optHTTPProxy sessionBuilder.httpsProxy = optHTTPSProxy - sessionBuilder.proxyAutoConfURL = optProxyAutoConfURL + sessionBuilder.proxyAutoConfigurationURL = optProxyAutoConfigurationURL sessionBuilder.proxyBypassDomains = optProxyBypass if let flags = optRedirectGateway { diff --git a/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift b/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift index 2664a91..0ae7845 100644 --- a/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift +++ b/TunnelKitTests/OpenVPN/ConfigurationParserTests.swift @@ -68,7 +68,7 @@ class ConfigurationParserTests: XCTestCase { XCTAssertEqual(parsed.httpProxy?.port, 8081) XCTAssertEqual(parsed.httpsProxy?.address, "7.8.9.10") XCTAssertEqual(parsed.httpsProxy?.port, 8082) - XCTAssertEqual(parsed.proxyAutoConfURL?.absoluteString, "https://pac/") + XCTAssertEqual(parsed.proxyAutoConfigurationURL?.absoluteString, "https://pac/") XCTAssertEqual(parsed.proxyBypassDomains, ["foo.com", "bar.org", "net.chat"]) }