iOS private app network extension or self-contained networking layer (or library interposition, etc.)

First, some background. We are working on the iOS port of this as part of our ZeroTier SDK for mobile development project:


https://www.zerotier.com/product-netcon.shtml


More background here:


https://www.zerotier.com/blog/?p=612


(scroll down to "Standards Based Peer to Peer Networking")


The idea is that this could be built as a framework and linked against the app and would provide the app with the ability to communicate over virtual networks seamlessly without requiring other code to be rewritten or recompiled. We've figured out how to package this appropriately into a single self-contained framework, get it to load, and get it to run both in the simulator and on a real device. It works when we make direct BSD socket API calls in our app, e.g. socket() and bind() and connect() and friends, and is able to connect and communicate over virtual networks as expected.


Now what we're trying to do is to figure out how to get this to work when sockets are used via CFSocket, CFStream, etc. and with native iOS HTTP/HTTPS clients and other normal iOS network I/O code.


The prroblem we're running into is that library interposition does not seem to work this way on iOS (or OSX/Darwin). Our library's interception of socket(), bind(), etc. does not carry over into other libraries the way it does on Linux. I assume this is a difference in how Apple's dynamic linker/loader works, or maybe something related to security policies.


So I'm posting to ask:


(1) Is there any way to achieve the desired library interposition/interception behavior on iOS (and OSX) without "heroic" things like code injection that would not be allowed in a normal app? We're linking our library and calls made from our app's main code itself are overridden, but this isn't currently carrying over to other libraries. Is it somehow related to library load or link order and is there any way to change this?


(2) If not, is there any other way to achieve the desired end goal? We are in parallel working on an iOS version of our ZeroTier One VPN app (see Android version here for comparison https://play.google.com/store/apps/details?id=com.zerotier.one), and this is going well. This is straightforward in that we're just using Network Extensions, etc., and writing a VPN app. Is there any way for a normal iOS app to have anything like a "private network extension" that is entirely self-contained within the app and visible only to itself? That's what we are more or less trying to achieve, so if it can't be achieved with library interposition then perhaps it can be achieved in some other way.


If there's no way to achieve (1) and (2), the last resort option we're considering is launching a background thread that runs a private SOCKS5 endpoint and then having the app use that. Theoretically that should work but it's ugly and probably slower than doing it the right way.


What we want in the end is to be able to package this as something entirely private and self-contained within an app that can allow the app to talk over the virtual network seamlessly without special elevated permissions or external secondary apps to provide connectivity.


Edit:


We found references to an "official" way of interposing functions:


http://toves.freeshell.org/interpose/


http://www.opensource.apple.com/source/dyld/dyld-97.1/include/mach-o/dyld-interposing.h


The question is whether this works when a library is actually linked with the application, since you can't set DYLD_INSERT_LIBRARIES in an iOS app. If we figure it out we'll edit and update for anyone else's reference, but in the meantime feedback is welcome.


Edit #2: this looks promising, will report: https://github.com/facebook/fishhook

Replies

Now what we're trying to do is to figure out how to get this to work when sockets are used via CFSocket, CFStream, etc. and with native iOS HTTP/HTTPS clients and other normal iOS network I/O code.

I hate to be the bearer of bad news but there’s no way to achieve this goal in any supported fashion. The only approach that is on a reasonable technical footing, dyld interposition, is not documented for third party use and isn’t feasible on iOS anyway. The other techniques you described rely on OS internals that are not considered API (it’s OK to use them for debugging but you don’t want to use them in production code). Even your SOCKS proxy approach is worrying because, while SOCKS proxying is a supported feature on OS X and the code that implements it is common to both platforms, it’s not actively used on iOS. It’s easy to imagine scenarios where SOCKS might break on iOS, either due to some environmental difference in the platforms or because Apple simply removes that code from iOS for efficiency reasons.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Edit: leaving these but look at the last reply, since I think I found the "right" way to do this.


We've already found that dyld interposition is feasible on iOS, at least recent versions, but as you say it may not be acceptable for the app store since the APIs are not documented and may not be stable or future-proof. After thinking on it a bit I have doubts about whether it could ever work with the proposed bitcode app shipping format (LLVM bitcode?) that is being introduced (unless some kind of official support were added).


The SOCKS solution looks workable but as you say it may vanish at some point in the future. We will pursue it for now since it uses published APIs and will work but will also continue researching this and will build with the knowledge that the SOCKS solution might need to be swapped out later.


None of these solutions are ideal. The ideal would be some official way of providing a "socket provider" or something like that, or permitting application-private instances of a Network Extension that are only visible to the app itself and that die with the app. The latter is possible at the OS level and in some ways is the most elegant and idiomatic (in terms of stated intent, etc.) but there are obviously no APIs for it. Are there any plans like this in the future?

Let me provide a concrete use case that we are actively working with a customer to achieve.


The customer has a high-bandwidth IP camera app. They want apps for iOS (and Android) that permit the feed from the camera to be viewed. This use case would likely involve the customer viewing the feed for fairly long periods of time.


If the camera is on the same local LAN broadcast segment, it is possible for app to find it and communicate directly. Otherwise, there is currently no reliable option for the customer but to pay to have all traffic relayed off cloud resources. Amazon charges $0.09/GB for traffic outflow. This means that if the camera streams 50kb/sec and the user on average watches it for three hours a day, this costs the vendor about $1.45/month in bandwidth charges for a $100 one-time-sale IP camera product.


There are many other cases with similar economics: tele-presence, video chat, games that transfer a lot of state, etc. There are also security applications and applications that prefer low latency. A 2000 mile data path between two devices in the same city is dumb.


Thought this was worth posting since Apple seems to be taking the approach of watching what people want to do on iOS and adding official ways to do it when it seems compelling. While I doubt Apple would do this just for us, there are as I said other applications for this general class of thing. If iOS had some approved way of extending or reshaping the network in a manner private to the app it would actually be far ahead of any other OS. What we're trying to achieve here is possible almost everywhere, but no OS in common use provides a clean way to do it. The closest is Linux but even there it's a tad painful.

I think the best way to do this in an idiomatic, native, correct way would be to write a network app extension:


https://developer.apple.com/app-extensions/


There is currently no explicit category for this. Perhaps there should be a "networking" extension class. Looks like "action" or "share" are the closest analogs today.


It could then use app proxy provider from the Network Extensions API to do precisely what I'm talking about above, and would have the added advantage of being installed only once on a device even if multiple apps used it:


https://developer.apple.com/library/ios/documentation/NetworkExtension/Reference/Network_Extension_Framework_Reference/index.html#//apple_ref/doc/uid/TP40016234


Going to read up more but what I've read so far sounds like this might be the iOS-correct way to do this. Might be iOS 9 only though, but we could use SOCKS or interposition on iOS 8 for backward compatibility. (Client doesn't care about iOS before 8, and soon probably not before 9, so not a long term problem.)


Edit: yes, looks very promising. This is basically exactly what we want. Network Containers would be an App Extension, not a Framework:


https://developer.apple.com/library/ios/documentation/NetworkExtension/Reference/NEAppProxyFlowClassRef/index.html#//apple_ref/swift/cl/c:objc(cs)NEAppProxyFlow


Current extension categories don't seem like any of them would be right for this, so our strategy I think will be to use the "SOCKS hack" to achieve app proxy flows within the app (some apps do this, like Tor bundle). But a networking app extension sounds like the long term correct way to implement this type of thing in iOS. I'd propose creating such a category, especially if you deprecate and eliminate SOCKS, since it would allow SOCKS and other forms of network extensions to be achieved within an app in a general and very powerful way.