Browse Source

Updated README with caveats

mirokuratczyk 8 years ago
parent
commit
280eeaa722

+ 45 - 2
MobileLibrary/iOS/SampleApps/TunneledWebView/README.md

@@ -1,7 +1,6 @@
 # iOS Library Sample App: TunneledWebView
 
 ## Tunneling UIWebView
-*Note: NSURLProtocol is only partially supported by UIWebView (https://bugs.webkit.org/show_bug.cgi?id=138169) and in iOS 9 (and perhaps other versions of iOS) audio and video are fetched out of process in mediaserverd and therefore not intercepted by NSURLProtocol.*
 
 *Note: this approach does not work with WKWebView (see [http://www.openradar.me/17190141](http://www.openradar.me/17190141)).*
 
@@ -11,10 +10,54 @@ The listening Psiphon proxy ports can be obtained via TunneledAppDelegate delega
 This is accomplished by registering `NSURLProtocol` subclass `JAHPAuthenticatingHTTPProtocol` with `NSURLProtocol`.
 `JAHPAuthenticatingHTTPProtocol` is then configured to use the local Psiphon proxies.
 This is done by setting the [connectionProxyDictionary](https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1411499-connectionproxydictionary?language=objc) of [NSURLSessionConfiguration](https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration).
-See `+ (JAHPQNSURLSessionDemux *)sharedDemux` in `JAHPAuthenticatingHTTPProtocol.m`.
+See [`+ (JAHPQNSURLSessionDemux *)sharedDemux`](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/c9c4834fba5e7a8b675c3ae493ac17b5975ab0fb/MobileLibrary/iOS/SampleApps/TunneledWebView/External/JiveAuthenticatingHTTPProtocol/JAHPAuthenticatingHTTPProtocol.m#L157) in `JAHPAuthenticatingHTTPProtocol.m`.
 
 We use a slightly modified version of JiveAuthenticatingProtocol (https://github.com/jivesoftware/JiveAuthenticatingHTTPProtocol), which in turn is largely based on [Apple's CustomHTTPProtocol example](https://developer.apple.com/library/content/samplecode/CustomHTTPProtocol/Introduction/Intro.html). 
 
+## *\*\* Caveats \*\*\*
+
+### Challenges
+
+***NSURLProtocol is only partially supported by UIWebView (https://bugs.webkit.org/show_bug.cgi?id=138169) and in 
+some versions of iOS audio and video are fetched out of process in mediaserverd and therefore are
+not intercepted by NSURLProtocol.***
+
+*In our limited testing iOS 9/10 leak and iOS 11 does not leak.*
+
+### Workarounds
+
+***It is worth noting that this fix is inexact and may not always work. If one has control over the HTML being rendered and resources being fetched with XHR it is preferable to alter 
+the media source URLs directly beforehand instead of relying on the javascript injection trick.***
+
+***This is a description of a workaround used in the [Psiphon Browser iOS app](https://github.com/Psiphon-Inc/endless) and not of what is implemented in TunneledWebView.
+TunneledWebView *does NOT* attempt to tunnel all audio/video content in UIWebView. This is only a hack which allows tunneling
+audio and video in UIWebView on versions of iOS which fetch audio/video out of process.***
+
+#### Background
+In [PsiphonBrowser](https://github.com/Psiphon-Inc/endless) we have implemented a workaround for audio and video being 
+fetched out of process.
+
+[PsiphonTunnel's](https://github.com/Psiphon-Labs/psiphon-tunnel-core/tree/master/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel)
+HTTP Proxy also offers a ["URL proxy (reverse proxy)"](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/631099d086c7c554a590b0cb76766be6dce94ef9/psiphon/httpProxy.go#L45-L70) 
+mode that relays requests for HTTP or HTTPS or URLs specified in the proxy request path. 
+ 
+This reverse proxy can be used by constructing a URL such as `http://127.0.0.1:<proxy-port>/tunneled-rewrite/<origin media URL>?m3u8=true`.
+
+When the retrieved resource is detected to be a [M3U8](https://en.wikipedia.org/wiki/M3U#M3U8) playlist a rewriting rule is applied to ensure all the URL entries
+are rewritten to use the same reverse proxy. Otherwise it will be returned unmodified.
+
+#### Fix
+
+* Media element URLs are rewritten to use the URL proxy (reverse proxy).
+* This is done by [injecting javascript](https://github.com/Psiphon-Inc/endless/blob/b0c33b4bbd917467a849ad8c51a225c2d4dab260/Endless/Resources/injected.js#L379-L408) 
+into the HTML [as it is being loaded](https://github.com/Psiphon-Inc/endless/blob/b0c33b4bbd917467a849ad8c51a225c2d4dab260/External/JiveAuthenticatingHTTPProtocol/JAHPAuthenticatingHTTPProtocol.m#L1274-L1280) 
+which [rewrites media URLs to use the URL proxy (reverse proxy)](https://github.com/Psiphon-Inc/endless/blob/b0c33b4bbd917467a849ad8c51a225c2d4dab260/Endless/Resources/injected.js#L319-L377).
+* If a [CSP](https://en.wikipedia.org/wiki/Content_Security_Policy) 
+is found in the header of the response, we need to modify it to allow our injected javascript to run.
+  * This is done by [modifying the
+CSP](https://github.com/Psiphon-Inc/endless/blob/b0c33b4bbd917467a849ad8c51a225c2d4dab260/External/JiveAuthenticatingHTTPProtocol/JAHPAuthenticatingHTTPProtocol.m#L1184-L1228) 
+to include a nonce generated for our injected javascript, which is [included in the script tag](https://github.com/Psiphon-Inc/endless/blob/b0c33b4bbd917467a849ad8c51a225c2d4dab260/External/JiveAuthenticatingHTTPProtocol/JAHPAuthenticatingHTTPProtocol.m#L1276).
+
 ## Configuring, Building, Running
 
 The sample app requires some extra files and configuration before building.

+ 4 - 0
MobileLibrary/iOS/SampleApps/TunneledWebView/TunneledWebView.xcodeproj/project.pbxproj

@@ -11,6 +11,7 @@
 		4E0CA9691FDE554B00B48BCA /* JAHPCacheStoragePolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E0CA9631FDE554B00B48BCA /* JAHPCacheStoragePolicy.m */; };
 		4E0CA96A1FDE554B00B48BCA /* JAHPCanonicalRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E0CA9651FDE554B00B48BCA /* JAHPCanonicalRequest.m */; };
 		4E0CA96B1FDE554B00B48BCA /* JAHPQNSURLSessionDemux.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E0CA9671FDE554B00B48BCA /* JAHPQNSURLSessionDemux.m */; };
+		4EE9CDD91FE0830600BCE310 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 4EE9CDD81FE0830600BCE310 /* README.md */; };
 		662658EE1DCB8CF300872F6C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662658ED1DCB8CF300872F6C /* AppDelegate.swift */; };
 		662658F01DCB8CF300872F6C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662658EF1DCB8CF300872F6C /* ViewController.swift */; };
 		662658F31DCB8CF300872F6C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 662658F11DCB8CF300872F6C /* Main.storyboard */; };
@@ -64,6 +65,7 @@
 		4E0CA9661FDE554B00B48BCA /* JAHPQNSURLSessionDemux.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JAHPQNSURLSessionDemux.h; sourceTree = "<group>"; };
 		4E0CA9671FDE554B00B48BCA /* JAHPQNSURLSessionDemux.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JAHPQNSURLSessionDemux.m; sourceTree = "<group>"; };
 		4E5A8DF51FDA7541009F8702 /* TunneledWebView-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TunneledWebView-Bridging-Header.h"; sourceTree = "<group>"; };
+		4EE9CDD81FE0830600BCE310 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
 		662658EA1DCB8CF300872F6C /* TunneledWebView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TunneledWebView.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		662658ED1DCB8CF300872F6C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		662658EF1DCB8CF300872F6C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@@ -147,6 +149,7 @@
 		662658EC1DCB8CF300872F6C /* TunneledWebView */ = {
 			isa = PBXGroup;
 			children = (
+				4EE9CDD81FE0830600BCE310 /* README.md */,
 				4E5A8DF51FDA7541009F8702 /* TunneledWebView-Bridging-Header.h */,
 				662658ED1DCB8CF300872F6C /* AppDelegate.swift */,
 				662658EF1DCB8CF300872F6C /* ViewController.swift */,
@@ -292,6 +295,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				4EE9CDD91FE0830600BCE310 /* README.md in Resources */,
 				662658F81DCB8CF300872F6C /* LaunchScreen.storyboard in Resources */,
 				662658F51DCB8CF300872F6C /* Assets.xcassets in Resources */,
 				6682D90E1EB1334000329958 /* psiphon-embedded-server-entries.txt in Resources */,

+ 18 - 0
MobileLibrary/iOS/SampleApps/TunneledWebView/TunneledWebView/AppDelegate.swift

@@ -41,6 +41,24 @@ import PsiphonTunnel
         // JAHPAuthenticatingHTTPProtocol with NSURLProtocol.
         // See comments for `setDelegate` and `start` in
         // JAHPAuthenticatingHTTPProtocol.h
+        /*******************************************************/
+        /*****                                             *****/
+        /*****               !!! WARNING !!!               *****/
+        /*****                                             *****/
+        /*******************************************************/
+        /*****                                             *****/
+        /*****  This methood of proxying UIWebView is not  *****/
+        /*****  officially supported and requires extra    *****/
+        /*****  steps to proxy audio / video content.      *****/
+        /*****  Otherwise audio / video fetching may be    *****/
+        /*****  untunneled!                                *****/
+        /*****                                             *****/
+        /*****  It is strongly advised that you read the   *****/
+        /*****  "Caveats" section of README.md before      *****/
+        /*****  using PsiphonTunnel to proxy UIWebView     *****/
+        /*****  traffic.                                   *****/
+        /*****                                             *****/
+        /*******************************************************/
         JAHPAuthenticatingHTTPProtocol.setDelegate(self)
         JAHPAuthenticatingHTTPProtocol.start()