Merge branch 'http-proxy-settings'
This commit is contained in:
commit
12b26df10d
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Basic support for proxy settings (no PAC). [#74](https://github.com/keeshux/tunnelkit/issues/74)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Make `hostname` optional and pick `resolvedAddresses` if nil.
|
- Make `hostname` optional and pick `resolvedAddresses` if nil.
|
||||||
|
|
|
@ -163,6 +163,18 @@ extension TunnelKitProvider {
|
||||||
sessionConfigurationBuilder.usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool ?? ConfigurationBuilder.defaults.sessionConfiguration.usesPIAPatches
|
sessionConfigurationBuilder.usesPIAPatches = providerConfiguration[S.usesPIAPatches] as? Bool ?? ConfigurationBuilder.defaults.sessionConfiguration.usesPIAPatches
|
||||||
sessionConfigurationBuilder.dnsServers = providerConfiguration[S.dnsServers] as? [String]
|
sessionConfigurationBuilder.dnsServers = providerConfiguration[S.dnsServers] as? [String]
|
||||||
sessionConfigurationBuilder.searchDomain = providerConfiguration[S.searchDomain] as? String
|
sessionConfigurationBuilder.searchDomain = providerConfiguration[S.searchDomain] as? String
|
||||||
|
if let proxyString = providerConfiguration[S.httpProxy] as? String {
|
||||||
|
guard let proxy = Proxy(rawValue: proxyString) else {
|
||||||
|
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.httpProxy)] has a badly formed element")
|
||||||
|
}
|
||||||
|
sessionConfigurationBuilder.httpProxy = proxy
|
||||||
|
}
|
||||||
|
if let proxyString = providerConfiguration[S.httpsProxy] as? String {
|
||||||
|
guard let proxy = Proxy(rawValue: proxyString) else {
|
||||||
|
throw ProviderConfigurationError.parameter(name: "protocolConfiguration.providerConfiguration[\(S.httpsProxy)] has a badly formed element")
|
||||||
|
}
|
||||||
|
sessionConfigurationBuilder.httpsProxy = proxy
|
||||||
|
}
|
||||||
sessionConfiguration = sessionConfigurationBuilder.build()
|
sessionConfiguration = sessionConfigurationBuilder.build()
|
||||||
|
|
||||||
shouldDebug = providerConfiguration[S.debug] as? Bool ?? ConfigurationBuilder.defaults.shouldDebug
|
shouldDebug = providerConfiguration[S.debug] as? Bool ?? ConfigurationBuilder.defaults.shouldDebug
|
||||||
|
@ -240,6 +252,10 @@ extension TunnelKitProvider {
|
||||||
|
|
||||||
static let searchDomain = "SearchDomain"
|
static let searchDomain = "SearchDomain"
|
||||||
|
|
||||||
|
static let httpProxy = "HTTPProxy"
|
||||||
|
|
||||||
|
static let httpsProxy = "HTTPSProxy"
|
||||||
|
|
||||||
// MARK: Debugging
|
// MARK: Debugging
|
||||||
|
|
||||||
static let debug = "Debug"
|
static let debug = "Debug"
|
||||||
|
@ -443,6 +459,12 @@ extension TunnelKitProvider {
|
||||||
if let searchDomain = sessionConfiguration.searchDomain {
|
if let searchDomain = sessionConfiguration.searchDomain {
|
||||||
dict[S.searchDomain] = searchDomain
|
dict[S.searchDomain] = searchDomain
|
||||||
}
|
}
|
||||||
|
if let httpProxy = sessionConfiguration.httpProxy {
|
||||||
|
dict[S.httpProxy] = httpProxy.rawValue
|
||||||
|
}
|
||||||
|
if let httpsProxy = sessionConfiguration.httpsProxy {
|
||||||
|
dict[S.httpsProxy] = httpsProxy.rawValue
|
||||||
|
}
|
||||||
//
|
//
|
||||||
if let resolvedAddresses = resolvedAddresses {
|
if let resolvedAddresses = resolvedAddresses {
|
||||||
dict[S.resolvedAddresses] = resolvedAddresses
|
dict[S.resolvedAddresses] = resolvedAddresses
|
||||||
|
@ -537,6 +559,12 @@ extension TunnelKitProvider {
|
||||||
if let searchDomain = sessionConfiguration.searchDomain {
|
if let searchDomain = sessionConfiguration.searchDomain {
|
||||||
log.info("\tSearch domain: \(searchDomain.maskedDescription)")
|
log.info("\tSearch domain: \(searchDomain.maskedDescription)")
|
||||||
}
|
}
|
||||||
|
if let httpProxy = sessionConfiguration.httpProxy {
|
||||||
|
log.info("\tHTTP proxy: \(httpProxy.maskedDescription)")
|
||||||
|
}
|
||||||
|
if let httpsProxy = sessionConfiguration.httpsProxy {
|
||||||
|
log.info("\tHTTPS proxy: \(httpsProxy.maskedDescription)")
|
||||||
|
}
|
||||||
log.info("\tMTU: \(mtu)")
|
log.info("\tMTU: \(mtu)")
|
||||||
log.info("\tDebug: \(shouldDebug)")
|
log.info("\tDebug: \(shouldDebug)")
|
||||||
log.info("\tMasks private data: \(masksPrivateData ?? true)")
|
log.info("\tMasks private data: \(masksPrivateData ?? true)")
|
||||||
|
|
|
@ -554,11 +554,23 @@ extension TunnelKitProvider: SessionProxyDelegate {
|
||||||
if let searchDomain = searchDomain {
|
if let searchDomain = searchDomain {
|
||||||
dnsSettings.searchDomains = [searchDomain]
|
dnsSettings.searchDomains = [searchDomain]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var proxySettings: NEProxySettings?
|
||||||
|
if let httpsProxy = cfg.sessionConfiguration.httpsProxy ?? reply.options.httpsProxy {
|
||||||
|
proxySettings = NEProxySettings()
|
||||||
|
proxySettings?.httpsServer = httpsProxy.neProxy()
|
||||||
|
proxySettings?.httpsEnabled = true
|
||||||
|
} else if let httpProxy = cfg.sessionConfiguration.httpProxy ?? reply.options.httpProxy {
|
||||||
|
proxySettings = NEProxySettings()
|
||||||
|
proxySettings?.httpServer = httpProxy.neProxy()
|
||||||
|
proxySettings?.httpEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
let newSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: remoteAddress)
|
let newSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: remoteAddress)
|
||||||
newSettings.ipv4Settings = ipv4Settings
|
newSettings.ipv4Settings = ipv4Settings
|
||||||
newSettings.ipv6Settings = ipv6Settings
|
newSettings.ipv6Settings = ipv6Settings
|
||||||
newSettings.dnsSettings = dnsSettings
|
newSettings.dnsSettings = dnsSettings
|
||||||
|
newSettings.proxySettings = proxySettings
|
||||||
|
|
||||||
setTunnelNetworkSettings(newSettings, completionHandler: completionHandler)
|
setTunnelNetworkSettings(newSettings, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
@ -671,3 +683,9 @@ extension TunnelKitProvider {
|
||||||
return error as? ProviderError ?? .linkError
|
return error as? ProviderError ?? .linkError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension Proxy {
|
||||||
|
func neProxy() -> NEProxyServer {
|
||||||
|
return NEProxyServer(address: address, port: Int(port))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -92,12 +92,14 @@ public class ConfigurationParser {
|
||||||
|
|
||||||
static let domain = NSRegularExpression("^dhcp-option +DOMAIN +[^ ]+")
|
static let domain = NSRegularExpression("^dhcp-option +DOMAIN +[^ ]+")
|
||||||
|
|
||||||
|
static let proxy = NSRegularExpression("^dhcp-option +PROXY_(HTTPS?|BYPASS) +[^ ]+ +\\d+")
|
||||||
|
|
||||||
// MARK: Unsupported
|
// MARK: Unsupported
|
||||||
|
|
||||||
// static let fragment = NSRegularExpression("^fragment +\\d+")
|
// static let fragment = NSRegularExpression("^fragment +\\d+")
|
||||||
static let fragment = NSRegularExpression("^fragment")
|
static let fragment = NSRegularExpression("^fragment")
|
||||||
|
|
||||||
static let proxy = NSRegularExpression("^\\w+-proxy")
|
static let connectionProxy = NSRegularExpression("^\\w+-proxy")
|
||||||
|
|
||||||
static let externalFiles = NSRegularExpression("^(ca|cert|key|tls-auth|tls-crypt) ")
|
static let externalFiles = NSRegularExpression("^(ca|cert|key|tls-auth|tls-crypt) ")
|
||||||
|
|
||||||
|
@ -193,7 +195,9 @@ public class ConfigurationParser {
|
||||||
var optRoutes6: [(String, UInt8, String?)] = [] // destination, prefix, gateway
|
var optRoutes6: [(String, UInt8, String?)] = [] // destination, prefix, gateway
|
||||||
var optDNSServers: [String] = []
|
var optDNSServers: [String] = []
|
||||||
var optSearchDomain: String?
|
var optSearchDomain: String?
|
||||||
|
var optHTTPProxy: Proxy?
|
||||||
|
var optHTTPSProxy: Proxy?
|
||||||
|
|
||||||
log.verbose("Configuration file:")
|
log.verbose("Configuration file:")
|
||||||
for line in lines {
|
for line in lines {
|
||||||
log.verbose(line)
|
log.verbose(line)
|
||||||
|
@ -215,7 +219,7 @@ public class ConfigurationParser {
|
||||||
Regex.fragment.enumerateComponents(in: line) { (_) in
|
Regex.fragment.enumerateComponents(in: line) { (_) in
|
||||||
unsupportedError = ConfigurationError.unsupportedConfiguration(option: "fragment")
|
unsupportedError = ConfigurationError.unsupportedConfiguration(option: "fragment")
|
||||||
}
|
}
|
||||||
Regex.proxy.enumerateComponents(in: line) { (_) in
|
Regex.connectionProxy.enumerateComponents(in: line) { (_) in
|
||||||
unsupportedError = ConfigurationError.unsupportedConfiguration(option: "proxy: \"\(line)\"")
|
unsupportedError = ConfigurationError.unsupportedConfiguration(option: "proxy: \"\(line)\"")
|
||||||
}
|
}
|
||||||
Regex.externalFiles.enumerateComponents(in: line) { (_) in
|
Regex.externalFiles.enumerateComponents(in: line) { (_) in
|
||||||
|
@ -469,6 +473,21 @@ public class ConfigurationParser {
|
||||||
}
|
}
|
||||||
optSearchDomain = $0[1]
|
optSearchDomain = $0[1]
|
||||||
}
|
}
|
||||||
|
Regex.proxy.enumerateArguments(in: line) {
|
||||||
|
guard $0.count == 3, let port = UInt16($0[2]) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch $0[0] {
|
||||||
|
case "PROXY_HTTPS":
|
||||||
|
optHTTPSProxy = Proxy($0[1], port)
|
||||||
|
|
||||||
|
case "PROXY_HTTP":
|
||||||
|
optHTTPProxy = Proxy($0[1], port)
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -631,6 +650,8 @@ public class ConfigurationParser {
|
||||||
|
|
||||||
sessionBuilder.dnsServers = optDNSServers
|
sessionBuilder.dnsServers = optDNSServers
|
||||||
sessionBuilder.searchDomain = optSearchDomain
|
sessionBuilder.searchDomain = optSearchDomain
|
||||||
|
sessionBuilder.httpProxy = optHTTPProxy
|
||||||
|
sessionBuilder.httpsProxy = optHTTPSProxy
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
|
@ -215,6 +215,12 @@ extension SessionProxy {
|
||||||
/// The search domain.
|
/// The search domain.
|
||||||
public var searchDomain: String?
|
public var searchDomain: String?
|
||||||
|
|
||||||
|
/// The HTTP proxy.
|
||||||
|
public var httpProxy: Proxy?
|
||||||
|
|
||||||
|
/// The HTTPS proxy.
|
||||||
|
public var httpsProxy: Proxy?
|
||||||
|
|
||||||
/// :nodoc:
|
/// :nodoc:
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
|
@ -246,7 +252,9 @@ extension SessionProxy {
|
||||||
ipv4: ipv4,
|
ipv4: ipv4,
|
||||||
ipv6: ipv6,
|
ipv6: ipv6,
|
||||||
dnsServers: dnsServers,
|
dnsServers: dnsServers,
|
||||||
searchDomain: searchDomain
|
searchDomain: searchDomain,
|
||||||
|
httpProxy: httpProxy,
|
||||||
|
httpsProxy: httpsProxy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,6 +342,12 @@ extension SessionProxy {
|
||||||
/// - Seealso: `SessionProxy.ConfigurationBuilder.searchDomain`
|
/// - Seealso: `SessionProxy.ConfigurationBuilder.searchDomain`
|
||||||
public let searchDomain: String?
|
public let searchDomain: String?
|
||||||
|
|
||||||
|
/// - Seealso: `SessionProxy.ConfigurationBuilder.httpProxy`
|
||||||
|
public var httpProxy: Proxy?
|
||||||
|
|
||||||
|
/// - Seealso: `SessionProxy.ConfigurationBuilder.httpsProxy`
|
||||||
|
public var httpsProxy: Proxy?
|
||||||
|
|
||||||
// MARK: Shortcuts
|
// MARK: Shortcuts
|
||||||
|
|
||||||
/// :nodoc:
|
/// :nodoc:
|
||||||
|
@ -384,6 +398,8 @@ extension SessionProxy.Configuration {
|
||||||
builder.ipv6 = ipv6
|
builder.ipv6 = ipv6
|
||||||
builder.dnsServers = dnsServers
|
builder.dnsServers = dnsServers
|
||||||
builder.searchDomain = searchDomain
|
builder.searchDomain = searchDomain
|
||||||
|
builder.httpProxy = httpProxy
|
||||||
|
builder.httpsProxy = httpsProxy
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -486,6 +502,45 @@ public struct IPv6Settings: Codable, CustomStringConvertible {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encapsulate a proxy setting.
|
||||||
|
public struct Proxy: Codable, RawRepresentable, CustomStringConvertible {
|
||||||
|
|
||||||
|
/// The proxy address.
|
||||||
|
public let address: String
|
||||||
|
|
||||||
|
/// The proxy port.
|
||||||
|
public let port: UInt16
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
public init(_ address: String, _ port: UInt16) {
|
||||||
|
self.address = address
|
||||||
|
self.port = port
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: RawRepresentable
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
public var rawValue: String {
|
||||||
|
return "\(address):\(port)"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
public init?(rawValue: String) {
|
||||||
|
let comps = rawValue.components(separatedBy: ":")
|
||||||
|
guard comps.count == 2, let port = UInt16(comps[1]) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.init(comps[0], port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: CustomStringConvertible
|
||||||
|
|
||||||
|
/// :nodoc:
|
||||||
|
public var description: String {
|
||||||
|
return rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// :nodoc:
|
/// :nodoc:
|
||||||
extension EndpointProtocol: Codable {
|
extension EndpointProtocol: Codable {
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
|
|
|
@ -53,12 +53,22 @@ class ConfigurationParserTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDHCPOption() throws {
|
func testDHCPOption() throws {
|
||||||
let lines = base + ["dhcp-option DNS 8.8.8.8", "dhcp-option DNS6 ffff::1", "dhcp-option DOMAIN example.com"]
|
let lines = base + [
|
||||||
|
"dhcp-option DNS 8.8.8.8",
|
||||||
|
"dhcp-option DNS6 ffff::1",
|
||||||
|
"dhcp-option DOMAIN example.com",
|
||||||
|
"dhcp-option PROXY_HTTP 1.2.3.4 8081",
|
||||||
|
"dhcp-option PROXY_HTTPS 7.8.9.10 8082"
|
||||||
|
]
|
||||||
XCTAssertNoThrow(try ConfigurationParser.parsed(fromLines: lines))
|
XCTAssertNoThrow(try ConfigurationParser.parsed(fromLines: lines))
|
||||||
|
|
||||||
let parsed = try! ConfigurationParser.parsed(fromLines: lines).configuration
|
let parsed = try! ConfigurationParser.parsed(fromLines: lines).configuration
|
||||||
XCTAssertEqual(parsed.dnsServers, ["8.8.8.8", "ffff::1"])
|
XCTAssertEqual(parsed.dnsServers, ["8.8.8.8", "ffff::1"])
|
||||||
XCTAssertEqual(parsed.searchDomain, "example.com")
|
XCTAssertEqual(parsed.searchDomain, "example.com")
|
||||||
|
XCTAssertEqual(parsed.httpProxy?.address, "1.2.3.4")
|
||||||
|
XCTAssertEqual(parsed.httpProxy?.port, 8081)
|
||||||
|
XCTAssertEqual(parsed.httpsProxy?.address, "7.8.9.10")
|
||||||
|
XCTAssertEqual(parsed.httpsProxy?.port, 8082)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testConnectionBlock() throws {
|
func testConnectionBlock() throws {
|
||||||
|
|
Loading…
Reference in New Issue