2018-08-23 08:19:25 +00:00
|
|
|
//
|
|
|
|
// ViewController.swift
|
|
|
|
// BasicTunnel-iOS
|
|
|
|
//
|
|
|
|
// Created by Davide De Rosa on 2/11/17.
|
2018-08-23 09:46:02 +00:00
|
|
|
// Copyright © 2018 Davide De Rosa. All rights reserved.
|
2018-08-23 08:19:25 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import NetworkExtension
|
2018-08-23 09:46:02 +00:00
|
|
|
import TunnelKit
|
2018-08-23 08:19:25 +00:00
|
|
|
|
2018-08-23 09:46:02 +00:00
|
|
|
extension ViewController {
|
|
|
|
private static let appGroup = "group.com.algoritmico.ios.demo.BasicTunnel"
|
2018-08-23 08:19:25 +00:00
|
|
|
|
2018-08-23 09:46:02 +00:00
|
|
|
private static let bundleIdentifier = "com.algoritmico.ios.demo.BasicTunnel.BasicTunnelExtension"
|
2018-08-23 08:19:25 +00:00
|
|
|
|
2018-08-23 09:46:02 +00:00
|
|
|
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(appGroup: ViewController.appGroup)
|
|
|
|
let socketType: TunnelKitProvider.SocketType = switchTCP.isOn ? .tcp : .udp
|
|
|
|
builder.endpointProtocols = [TunnelKitProvider.EndpointProtocol(socketType, port)]
|
|
|
|
builder.cipher = .aes128cbc
|
|
|
|
builder.digest = .sha1
|
|
|
|
builder.mtu = 1350
|
2018-08-30 09:05:34 +00:00
|
|
|
builder.compressionFraming = .compLZO
|
2018-08-23 09:46:02 +00:00
|
|
|
builder.renegotiatesAfterSeconds = nil
|
|
|
|
builder.shouldDebug = true
|
|
|
|
builder.debugLogKey = "Log"
|
|
|
|
|
|
|
|
let configuration = builder.build()
|
|
|
|
return try! configuration.generatedTunnelProtocol(
|
|
|
|
withBundleIdentifier: ViewController.bundleIdentifier,
|
|
|
|
endpoint: endpoint
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ViewController: UIViewController, URLSessionDataDelegate {
|
2018-08-23 08:19:25 +00:00
|
|
|
@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 {
|
2018-08-23 21:59:59 +00:00
|
|
|
textPort.text = "502"
|
2018-08-23 08:19:25 +00:00
|
|
|
} else {
|
2018-08-23 21:59:59 +00:00
|
|
|
textPort.text = "1198"
|
2018-08-23 08:19:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func connect() {
|
|
|
|
configureVPN({ (manager) in
|
2018-08-23 09:46:02 +00:00
|
|
|
return self.makeProtocol()
|
2018-08-23 08:19:25 +00:00
|
|
|
}, 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
|
|
|
|
}
|
2018-08-23 09:46:02 +00:00
|
|
|
try? vpn.sendProviderMessage(TunnelKitProvider.Message.requestLog.data) { (data) in
|
2018-08-23 08:19:25 +00:00
|
|
|
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 {
|
2018-08-23 09:46:02 +00:00
|
|
|
if (p.providerBundleIdentifier == ViewController.bundleIdentifier) {
|
2018-08-23 08:19:25 +00:00
|
|
|
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)")
|
|
|
|
}
|
|
|
|
}
|