// // ViewController.swift // TunneledWebView // import UIKit import PsiphonTunnel class ViewController: UIViewController { var webView: UIWebView! // The instance of PsiphonTunnel we'll use for connecting. var psiphonTunnel: PsiphonTunnel? // This are the ports that we can proxy through. var socksProxyPort = -1 var httpProxyPort = -1 override func loadView() { // Make our whole view the webview. webView = UIWebView() view = webView self.psiphonTunnel = PsiphonTunnel.newPsiphonTunnel(self) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. // Start up the tunnel and begin connecting. // This could be started elsewhere or earlier. NSLog("Starting tunnel") let embeddedServerEntries = "" guard let success = self.psiphonTunnel?.start(embeddedServerEntries), success else { NSLog("psiphonTunnel.start returned false") return } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } // MARK: TunneledAppDelegate implementation // See the protocol definition for details about the methods. // Note that we're excluding all the optional methods that we aren't using, // however your needs may be different. extension ViewController: TunneledAppDelegate { func getPsiphonConfig() -> String? { // In this example, we're going to retrieve our Psiphon config from a file in the app bundle. // Alternatively, it could be a string literal in the code, or whatever makes sense. guard let psiphonConfigUrl = Bundle.main.url(forResource: "psiphon-config", withExtension: "json") else { NSLog("Error getting Psiphon config resource file URL!") return nil } do { return try String.init(contentsOf: psiphonConfigUrl) } catch { NSLog("Error getting Psiphon config resource file URL!") return nil } } func onDiagnosticMessage(_ message: String) { NSLog("onDiagnosticMessage: %@", message) } func onConnected() { NSLog("onConnected") // After we're connected, make a tunneled request and populate the webview. DispatchQueue.main.async { assert(self.httpProxyPort > 0) // We'll check out IP to make sure we're tunneled. let urlPath: String = "https://freegeoip.net/csv/" let url = URL(string: urlPath)! let request = URLRequest(url: url) let config = URLSessionConfiguration.ephemeral config.requestCachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData config.connectionProxyDictionary = [AnyHashable: Any]() // Enable and set the SOCKS proxy values. config.connectionProxyDictionary?[kCFStreamPropertySOCKSProxy as String] = 1 config.connectionProxyDictionary?[kCFStreamPropertySOCKSProxyHost as String] = "127.0.0.1" config.connectionProxyDictionary?[kCFStreamPropertySOCKSProxyPort as String] = self.socksProxyPort // Alternatively, the HTTP proxy can be used. Below are the settings for that. // The HTTPS key constants are mismatched and Xcode gives deprecation warnings, but they seem to be necessary to proxy HTTPS requests. This is probably a bug on Apple's side; see: https://forums.developer.apple.com/thread/19356#131446 // config.connectionProxyDictionary?[kCFNetworkProxiesHTTPEnable as String] = 1 // config.connectionProxyDictionary?[kCFNetworkProxiesHTTPProxy as String] = "127.0.0.1" // config.connectionProxyDictionary?[kCFNetworkProxiesHTTPPort as String] = self.httpProxyPort // config.connectionProxyDictionary?[kCFStreamPropertyHTTPSProxyHost as String] = "127.0.0.1" // config.connectionProxyDictionary?[kCFStreamPropertyHTTPSProxyPort as String] = self.httpProxyPort let session = URLSession.init(configuration: config, delegate: nil, delegateQueue: OperationQueue.current) // Create the URLSession task that will make the request via the tunnel proxy. let task = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in if error != nil { NSLog("Client-side error in request to \(urlPath): \(error)") return } let httpResponse = response as? HTTPURLResponse if httpResponse?.statusCode != 200 { NSLog("Server-side error in request to \(urlPath): \(httpResponse)") return } let encodingName = response?.textEncodingName != nil ? response?.textEncodingName : "utf-8" let encoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName as CFString!)) var stringData = String(data: data!, encoding: String.Encoding(rawValue: UInt(encoding))) stringData = stringData?.replacingOccurrences(of: ",", with: "\n") // Load the IP info result into the web view. self.webView.loadHTMLString("
\(stringData!)
", baseURL: url) // Make sure the session is cleaned up. session.invalidateAndCancel() // We're done with the Psiphon tunnel, so stop it. // In a real app, we would keep this alive for as long as we need it. self.psiphonTunnel?.stop() } // Start the request task. task.resume() } } func onListeningSocksProxyPort(_ port: Int) { NSLog("onListeningSocksProxyPort: %d", port) // Record the port being used so that we can proxy through it later. DispatchQueue.main.async { self.socksProxyPort = port } } func onListeningHttpProxyPort(_ port: Int) { NSLog("onListeningHttpProxyPort: %d", port) // Record the port being used so that we can proxy through it later. DispatchQueue.main.async { self.httpProxyPort = port } } }