// // ViewController.swift // Netcon-iOS // // Created by Joseph Henry on 2/14/16. // Copyright © 2016 ZeroTier. All rights reserved. // import UIKit class ViewController: UIViewController { @IBOutlet weak var myWebView: UIWebView! @IBOutlet weak var btnTcpServerTest: UIButton! @IBOutlet weak var btnTcpClientTest: UIButton! @IBOutlet weak var btnUdpServerTest: UIButton! @IBOutlet weak var btnUdpClientTest: UIButton! @IBOutlet weak var btnExecuteTest: UIButton! @IBOutlet weak var txtPort: UITextField! @IBOutlet weak var txtAddr: UITextField! @IBOutlet weak var urlTextField: UITextField! var serverPort:UInt16 = 8888 var serverAddr:String = "10.5.5.2" // Test Network Join @IBOutlet weak var txtNWID: UITextField! @IBOutlet weak var btnJoinNetwork: UIButton! @IBAction func joinNetworkClicked(sender: AnyObject) { zt_join_network(txtNWID.text!); zt_join_network("e5cd7a9e1c2e194f"); } // Shim { Hook, Proxy, Changeling, Direct Call } @IBOutlet weak var ShimControl: UISegmentedControl! var selectedShim:UInt16 = 0 @IBAction func ShimControlSelected(sender: AnyObject) { switch sender.selectedSegmentIndex { case 0: print("Selected Hook\n"); selectedShim = 0 case 1: print("Selected Proxy\n"); selectedShim = 1 case 2: print("Selected Changeling\n"); selectedShim = 2 case 3: print("Selected Direct\n"); selectedShim = 3 default: break; } } // Mode { Client / Server } @IBOutlet weak var ModeControl: UISegmentedControl! var selectedMode:UInt16 = 0 @IBAction func ModeControlSelected(sender: AnyObject) { switch sender.selectedSegmentIndex { case 0: print("Selected client\n"); selectedMode = 0 case 1: print("Selected server\n"); selectedMode = 1 default: break; } } // Protocol { TCP / UDP } @IBOutlet weak var ProtocolControl: UISegmentedControl! var selectedProtocol:Int32 = SOCK_STREAM @IBAction func ProtocolControlSelected(sender: AnyObject) { switch sender.selectedSegmentIndex { case 0: print("Selected TCP (SOCK_STREAM)\n"); selectedProtocol = SOCK_STREAM case 1: print("Selected UDP (SOCK_DGRAM)\n"); selectedProtocol = SOCK_DGRAM default: break; } } @IBAction func ExecuteTest(sender: AnyObject) { print("Running Test...\n") switch selectedShim { case 0: print("test_client_hook_bsd_socket_api\n"); test_client_hook_bsd_socket_api() case 1: print("test_intercepted_proxy_streams\n"); test_client_proxy_nsstream() case 2: print("test_client_changeling\n"); test_client_changeling() case 3: print("test_client_direct_call_zt_socket\n"); test_client_direct_call_zt_socket() default: break; } } @IBOutlet weak var btnSockTest: UIButton! @IBAction func SocksTestAction(sender: AnyObject) { // Remove } @IBOutlet weak var WebRequest: UIButton! @IBAction func WebRequestAction(sender: AnyObject) { // TODO: Re-test let url_str = "http://" + txtAddr.text! + "/" let url = NSURL (string: url_str); //urlTextField.text = url_str; let requestObj = NSURLRequest(URL: url!); myWebView.loadRequest(requestObj); } // Mode: Client Test // Shim: SOCKS5 Proxy // Method: NSStream func test_client_proxy_nsstream() { // For HTTP request var buffer = [UInt8](count: 100, repeatedValue: 0) let str = "GET / HTTP/1.0\r\n\r\n" //let str = "Welcome to the machine" print("strlen = %d\n", str.characters.count) let encodedDataArray = [UInt8](str.utf8) var inputStream:NSInputStream? var outputStream:NSOutputStream? // As usual, get our streams to our desired "local" address NSStream.getStreamsToHostWithName(serverAddr, port: Int(serverPort), inputStream: &inputStream, outputStream: &outputStream) // SOCKS Proxy config dictionary let myDict:NSDictionary = [NSStreamSOCKSProxyHostKey : "0.0.0.0", NSStreamSOCKSProxyPortKey : 1337, NSStreamSOCKSProxyVersionKey : NSStreamSOCKSProxyVersion5] // Give configuration to NSStreams inputStream!.setProperty(myDict, forKey: NSStreamSOCKSProxyConfigurationKey) outputStream!.setProperty(myDict, forKey: NSStreamSOCKSProxyConfigurationKey) /* If you're interested in what happens next: NSStream objects will generate native sockets internally which then connect to the SOCKS proxy on 'localhost'. Once this connection is established the Proxy server will handle a connection request to the "local address" of your choice. The subsequent socket(), and connect() calls will be intercepted and sent to the Netcon service via an RPC mechanism mediated by unix domain sockets. These RPC calls are dissected and sent to the lwIP stack and finally to the ZeroTierOne service */ inputStream!.open() outputStream!.open() outputStream?.write(encodedDataArray, maxLength: encodedDataArray.count) //sleep(5) //inputStream?.read(&buffer, maxLength: 100) //print("buffer = \(buffer)\n") } // Mode: Client Test // Shim: Hook // Method: BSD-like socket API func test_client_hook_bsd_socket_api() { // TCP if(selectedProtocol == SOCK_STREAM) { let sd = socket(AF_INET, SOCK_STREAM, 0) var addr = sockaddr_in(sin_len: UInt8(sizeof(sockaddr_in)), sin_family: UInt8(AF_INET), sin_port: serverPort.bigEndian, sin_addr: in_addr(s_addr: 0), sin_zero: (0,0,0,0,0,0,0,0)) inet_pton(AF_INET, serverAddr, &(addr.sin_addr)); let connect_fd = connect(sd, UnsafePointer([addr]), UInt32(addr.sin_len)) print("connect_fd = \(connect_fd),\(errno)") if connect_fd < 0 { let err = errno print("Error connecting IPv4 socket \(err)") return } } // UDP if(selectedProtocol == SOCK_DGRAM) { } } // Mode: Client Test // Shim: N/A // Method: Direct Call to ZT API func test_client_direct_call_zt_socket() { // TCP if(selectedProtocol == SOCK_STREAM) { // Note: We merely added the 'zt_' prefix to the standard native bsd socket calls // This gets you direct access to ZeroTier Sockets let sd = zts_socket(AF_INET, SOCK_STREAM, 0) var addr = sockaddr_in(sin_len: UInt8(sizeof(sockaddr_in)), sin_family: UInt8(AF_INET), sin_port: serverPort.bigEndian, sin_addr: in_addr(s_addr: 0), sin_zero: (0,0,0,0,0,0,0,0)) inet_pton(AF_INET, serverAddr, &(addr.sin_addr)); let connect_fd = zts_connect(sd, UnsafePointer([addr]), UInt32(addr.sin_len)) print("connect_fd = \(connect_fd),\(errno)") if connect_fd < 0 { let err = errno print("Error connecting IPv4 socket \(err)") return } } // UDP if(selectedProtocol == SOCK_DGRAM) { } } // Mode: Client Test // Shim: Changeling // Method: BSD-like socket API func test_client_changeling() { // Technically this scenario is using the same bsd socket API as the // 'test_client_hook_bsd_socket_api' test, we're just handling the native // sockets in a different way, so we'll just call the same test function test_client_hook_bsd_socket_api() } // -------- BEGIN ZEROTIER SERVICE AND PROXY THREAD DEFINITIONS var service_thread : NSThread! func ztnc_start_service() { // FIXME: We use this to get a path for the ZeroTierOne service to use, this should be done differently for production let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) //disable_intercept() // We don't want the ZeroTier service to use intercepted calls print("start_service()\n") start_service(path[0]) } // ------- END override func viewDidLoad() { txtNWID.text = "e5cd7a9e1c3511dd" // Style self.view.backgroundColor = UIColor.blackColor() btnExecuteTest.setTitleColor(UIColor.greenColor(), forState: UIControlState.Normal) btnExecuteTest.layer.cornerRadius = 6 btnExecuteTest.layer.backgroundColor = UIColor.grayColor().CGColor btnExecuteTest.layer.borderColor = UIColor.grayColor().CGColor super.viewDidLoad() // ------- BEGIN INITIALIZATION OF ZEROTIER SERVICE AND PROXY // ZeroTier Service thread dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { self.service_thread = NSThread(target:self, selector:"ztnc_start_service", object:nil) self.service_thread.start() }); } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }