You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
10 KiB
309 lines
10 KiB
// |
|
// 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<sockaddr>([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<sockaddr>([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. |
|
} |
|
} |
|
|
|
|