Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS intercept all network traffic from my app?

I'd like to add a proxy to all networks calls coming from my app. Something like

func intercept(request: URLRequest) {
  if isOk(request) {
    return // the request continues as normally
  } else if isIntercepted(request) {
    let res = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "HTTP/2", headerFields: ["Content-Type": "application/json"])
    res.value = "{\"ok\":true}" // not that exactly work, but you get the idea
    request.end(res)
  } else {
    let res = HTTPURLResponse(url: url, statusCode: 401, httpVersion: "HTTP/2")
    res.value = "forbidden"
    request.end(res)
  }
}

I'd like it to be applied to all calls coming from my app. Ie from my code and from all libraries and frameworks I'm using. Is it possible?

I've found questions about reading traffic of other apps (not possible) and setting delegates on calls started from my code. I'd like to go further with something that 1) would automatically apply to all traffic and 2) include traffic from 3rd parties

like image 791
Guig Avatar asked Feb 08 '17 23:02

Guig


1 Answers

That's a job for the URLProtocol class.

From Apple's docs:

An NSURLProtocol object handles the loading of protocol-specific URL data. The NSURLProtocol class itself is an abstract class that provides the infrastructure for processing URLs with a specific URL scheme. You create subclasses for any custom protocols or URL schemes that your app supports.

You need to implement your own subclass of URLProtocol and register it with the app for it to be used. Thereafter all connections initialized from the App will make use of the protocol, and you will be able to handle/block whichever request you want.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
    guard URLProtocol.registerClass(MyURLProtocol.self) else
    {
        abort()
    }

    return true
}

Also, if you are using URLSession (and you should!), then you also have to register your class through the session configurations:

func getURLSessionConfiguration() -> URLSessionConfiguration
{
    let configuration = URLSessionConfiguration.default

    configuration.protocolClasses = [
        MyURLProtocol.self
    ]

    return configuration
}

let session = URLSession(configuration: getURLSessionConfiguration())

Then you can manage whatever you want to block from the startLoading() method of your URLProtocol subclass:

override func startLoading()
{
    if !self.isOK()
    {
        let error = NSError(domain: "GuardURLProtocol", code: 10, userInfo: [NSLocalizedDescriptionKey: "Connection denied by guard"])
        self.client?.urlProtocol(self, didFailWithError: error)
    }
    else if let task = self.task
    {
        task.resume()
    }
}

There are more methods you have to implement, and you should read the documentation from Apple.

But as a gizmo (and exercise for myself), I have written a generic blocker protocol, and you should check it out to see how it works. In the bottom of the GuardURLProtocol.swift file there is an example subclass (BlockFPTURLSession) where all FTP requests are blocked by schema.

If you use the class I linked above, and try to open an FTP connection, you will see the following errors:

2017-02-16 23:09:45.846 URLProtocol[83111:7456862] Error: Error Domain=GuardURLProtocol Code=10 "Connection denied by guard" UserInfo={NSLocalizedDescription=Connection denied by guard}

Have fun!

like image 75
Bruno Philipe Avatar answered Sep 21 '22 06:09

Bruno Philipe