tunnelkit/Demo/BasicTunnel-iOS/ViewController.swift

270 lines
8.2 KiB
Swift

//
// ViewController.swift
// BasicTunnel-iOS
//
// Created by Davide De Rosa on 2/11/17.
// Copyright © 2018 Davide De Rosa. All rights reserved.
//
import UIKit
import NetworkExtension
import TunnelKit
extension ViewController {
private static let appGroup = "group.com.algoritmico.ios.demo.BasicTunnel"
private static let bundleIdentifier = "com.algoritmico.ios.demo.BasicTunnel.BasicTunnelExtension"
private func makeProtocol() -> NETunnelProviderProtocol {
let server = textServer.text!
let domain = textDomain.text!
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
let port = UInt16(textPort.text!)!
let username = textUsername.text!
let password = textPassword.text!
let endpoint = TunnelKitProvider.AuthenticatedEndpoint(
hostname: hostname,
username: username,
password: password
)
var builder = TunnelKitProvider.ConfigurationBuilder()
let socketType: TunnelKitProvider.SocketType = switchTCP.isOn ? .tcp : .udp
builder.endpointProtocols = [TunnelKitProvider.EndpointProtocol(socketType, port)]
builder.cipher = .aes128cbc
builder.digest = .sha1
builder.mtu = 1350
builder.compressionFraming = .compLZO
builder.renegotiatesAfterSeconds = nil
builder.shouldDebug = true
builder.debugLogKey = "Log"
let configuration = builder.build()
return try! configuration.generatedTunnelProtocol(
withBundleIdentifier: ViewController.bundleIdentifier,
appGroup: ViewController.appGroup,
endpoint: endpoint
)
}
}
class ViewController: UIViewController, URLSessionDataDelegate {
@IBOutlet var textUsername: UITextField!
@IBOutlet var textPassword: UITextField!
@IBOutlet var textServer: UITextField!
@IBOutlet var textDomain: UITextField!
@IBOutlet var textPort: UITextField!
@IBOutlet var switchTCP: UISwitch!
@IBOutlet var buttonConnection: UIButton!
@IBOutlet var textLog: UITextView!
//
var currentManager: NETunnelProviderManager?
var status = NEVPNStatus.invalid
override func viewDidLoad() {
super.viewDidLoad()
textServer.text = "germany"
textDomain.text = "privateinternetaccess.com"
textPort.text = "1198"
switchTCP.isOn = false
textUsername.text = "myusername"
textPassword.text = "mypassword"
NotificationCenter.default.addObserver(self,
selector: #selector(VPNStatusDidChange(notification:)),
name: .NEVPNStatusDidChange,
object: nil)
reloadCurrentManager(nil)
//
testFetchRef()
}
@IBAction func connectionClicked(_ sender: Any) {
let block = {
switch (self.status) {
case .invalid, .disconnected:
self.connect()
case .connected, .connecting:
self.disconnect()
default:
break
}
}
if (status == .invalid) {
reloadCurrentManager({ (error) in
block()
})
}
else {
block()
}
}
@IBAction func tcpClicked(_ sender: Any) {
if switchTCP.isOn {
textPort.text = "502"
} else {
textPort.text = "1198"
}
}
func connect() {
configureVPN({ (manager) in
return self.makeProtocol()
}, completionHandler: { (error) in
if let error = error {
print("configure error: \(error)")
return
}
let session = self.currentManager?.connection as! NETunnelProviderSession
do {
try session.startTunnel()
} catch let e {
print("error starting tunnel: \(e)")
}
})
}
func disconnect() {
configureVPN({ (manager) in
return nil
}, completionHandler: { (error) in
self.currentManager?.connection.stopVPNTunnel()
})
}
@IBAction func displayLog() {
guard let vpn = currentManager?.connection as? NETunnelProviderSession else {
return
}
try? vpn.sendProviderMessage(TunnelKitProvider.Message.requestLog.data) { (data) in
guard let log = String(data: data!, encoding: .utf8) else {
return
}
self.textLog.text = log
}
}
func configureVPN(_ configure: @escaping (NETunnelProviderManager) -> NETunnelProviderProtocol?, completionHandler: @escaping (Error?) -> Void) {
reloadCurrentManager { (error) in
if let error = error {
print("error reloading preferences: \(error)")
completionHandler(error)
return
}
let manager = self.currentManager!
if let protocolConfiguration = configure(manager) {
manager.protocolConfiguration = protocolConfiguration
}
manager.isEnabled = true
manager.saveToPreferences { (error) in
if let error = error {
print("error saving preferences: \(error)")
completionHandler(error)
return
}
print("saved preferences")
self.reloadCurrentManager(completionHandler)
}
}
}
func reloadCurrentManager(_ completionHandler: ((Error?) -> Void)?) {
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
if let error = error {
completionHandler?(error)
return
}
var manager: NETunnelProviderManager?
for m in managers! {
if let p = m.protocolConfiguration as? NETunnelProviderProtocol {
if (p.providerBundleIdentifier == ViewController.bundleIdentifier) {
manager = m
break
}
}
}
if (manager == nil) {
manager = NETunnelProviderManager()
}
self.currentManager = manager
self.status = manager!.connection.status
self.updateButton()
completionHandler?(nil)
}
}
func updateButton() {
switch status {
case .connected, .connecting:
buttonConnection.setTitle("Disconnect", for: .normal)
case .disconnected:
buttonConnection.setTitle("Connect", for: .normal)
case .disconnecting:
buttonConnection.setTitle("Disconnecting", for: .normal)
default:
break
}
}
@objc private func VPNStatusDidChange(notification: NSNotification) {
guard let status = currentManager?.connection.status else {
print("VPNStatusDidChange")
return
}
print("VPNStatusDidChange: \(status.rawValue)")
self.status = status
updateButton()
}
private func testFetchRef() {
// let keychain = Keychain(group: ViewController.APP_GROUP)
// let username = "foo"
// let password = "bar"
//
// guard let _ = try? keychain.set(password: password, for: username) else {
// print("Couldn't set password")
// return
// }
// guard let passwordReference = try? keychain.passwordReference(for: username) else {
// print("Couldn't get password reference")
// return
// }
// guard let fetchedPassword = try? Keychain.password(for: username, reference: passwordReference) else {
// print("Couldn't fetch password")
// return
// }
//
// print("\(username) -> \(password)")
// print("\(username) -> \(fetchedPassword)")
}
}