I am writing a network extension (activated from a MacOS app) which should proxy HTTP connections for certain domains through a custom made proxy server. This is the code I am playing with:
class AppProxyProvider: NETransparentProxyProvider {
static let log = OSLog(subsystem: "com.example.myextension", category: "provider")
override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {
let settings = NETransparentProxyNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
let rule = NENetworkRule(destinationHost: NWHostEndpoint(hostname:"example.com", port:"443"), protocol: .TCP)
settings.includedNetworkRules = [rule]
self.setTunnelNetworkSettings(settings) { error in
completionHandler(error)
}
}
override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
completionHandler()
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
if let handler = completionHandler {
handler(messageData)
}
}
override func sleep(completionHandler: @escaping () -> Void) {
completionHandler()
}
override func wake() {
}
override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
os_log(.debug, log: Self.log, "HANDLE NEW FLOW")
return true
}
}
I am able to successfully invoke the startProxy
method which in turn registers the custom NETransparentProxyNetworkSettings
and invokes the completionHandler without errors. I can see the network extension successfully enabled in the system.
But as soon as I try to make an HTTP request to the specified domain (example.com:443), the request hangs on the client and the handleNewFlow
method of my network extension is never called so that I can proxy the traffic.
So my question is what is the correct way to setup a NETransparentProxyProvider
(or a NEAppProxyProvider
)?
My goal is to intercept connections to specific addresses and proxy them through a local TCP server (by wrapping them in HTTP CONNECT requests)
I also tried subclassing directly from NEAppProxyProvider
and registering NETunnelNetworkSettings
, but in this case the completion handler of setTunnelNetworkSettings
is invoked with an error:
The operation couldn’t be completed. (NEAgentErrorDomain error 1.)
Here's my attempt with NEAppProxyProvider
:
class AppProxyProvider: NEAppProxyProvider {
static let log = OSLog(subsystem: "com.example.myextension", category: "provider")
override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {
let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
self.setTunnelNetworkSettings(settings) { error in
completionHandler(error)
}
}
override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
completionHandler()
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
if let handler = completionHandler {
handler(messageData)
}
}
override func sleep(completionHandler: @escaping () -> Void) {
completionHandler()
}
override func wake() {
}
override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
os_log(.debug, log: Self.log, "HANDLE NEW FLOW")
return true
}
}