Merge branch 'manager-subspec'

This commit is contained in:
Davide De Rosa 2020-06-29 13:33:52 +02:00
commit 5ab86d22bc
24 changed files with 1038 additions and 517 deletions

View File

@ -43,6 +43,16 @@ custom_categories:
- NETunnelInterface
- NEUDPSocket
- NSNotification
- name: Manager
children:
- VPN
- VPNProvider
- MockVPNProvider
- StandardVPNProvider
- VPNConfiguration
- NetworkExtensionVPNConfiguration
- VPNStatus
- NSNotification
- name: Protocols/OpenVPN
children:
- OpenVPN

View File

@ -1,9 +1,9 @@
//
// AppDelegate.swift
// BasicTunnel-iOS
// Demo
//
// Created by Davide De Rosa on 2/11/17.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
@ -22,18 +22,6 @@
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (c) 2018-Present Private Internet Access
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
import UIKit
import NetworkExtension

View File

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14269.14" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14252.5"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -22,40 +20,35 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="bc6-yT-aty">
<rect key="frame" x="20" y="190" width="335" height="30"/>
<nil key="textColor"/>
<rect key="frame" x="20" y="182" width="335" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" secureTextEntry="YES"/>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="ONL-vF-iUY">
<rect key="frame" x="20" y="40" width="335" height="30"/>
<nil key="textColor"/>
<rect key="frame" x="20" y="20" width="335" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet"/>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="dQS-Ma-dYP">
<rect key="frame" x="20" y="140" width="335" height="30"/>
<nil key="textColor"/>
<rect key="frame" x="20" y="128" width="335" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet"/>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" clearButtonMode="always" translatesAutoresizingMaskIntoConstraints="NO" id="XwE-sE-aPN">
<rect key="frame" x="20" y="90" width="245" height="30"/>
<nil key="textColor"/>
<rect key="frame" x="20" y="74" width="245" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet"/>
</textField>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="7LH-tE-it9">
<rect key="frame" x="275" y="90" width="80" height="30"/>
<rect key="frame" x="275" y="74" width="80" height="34"/>
<constraints>
<constraint firstAttribute="width" constant="80" id="aWP-Ug-b9B"/>
</constraints>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Teo-8d-LYJ">
<rect key="frame" x="20" y="240" width="276" height="70"/>
<rect key="frame" x="20" y="236" width="276" height="70"/>
<fontDescription key="fontDescription" type="system" pointSize="48"/>
<state key="normal" title="Connect"/>
<connections>
@ -63,19 +56,19 @@
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="TCP" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sA4-W9-jxo">
<rect key="frame" x="312" y="240" width="37.5" height="24"/>
<rect key="frame" x="312" y="236" width="37.5" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="WZf-S5-SqC">
<rect key="frame" x="306" y="279" width="51" height="31"/>
<rect key="frame" x="306" y="275" width="51" height="31"/>
<connections>
<action selector="tcpClicked:" destination="BYZ-38-t0r" eventType="valueChanged" id="ZJI-Jw-pow"/>
</connections>
</switch>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6dU-fF-FSg">
<rect key="frame" x="20" y="310" width="335" height="41"/>
<rect key="frame" x="20" y="306" width="335" height="41"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="See log"/>
<connections>
@ -83,13 +76,13 @@
</connections>
</button>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="UNN-CR-rdr">
<rect key="frame" x="20" y="371" width="335" height="276"/>
<rect key="frame" x="20" y="367" width="335" height="280"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<fontDescription key="fontDescription" name="CourierNewPSMT" family="Courier New" pointSize="17"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="UNN-CR-rdr" firstAttribute="top" secondItem="6dU-fF-FSg" secondAttribute="bottom" constant="20" id="03h-H3-dSN"/>
<constraint firstItem="7LH-tE-it9" firstAttribute="centerY" secondItem="XwE-sE-aPN" secondAttribute="centerY" id="0wJ-c9-Gcy"/>

View File

@ -1,9 +1,9 @@
//
// ViewController.swift
// BasicTunnel-iOS
// Demo
//
// Created by Davide De Rosa on 2/11/17.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
@ -22,95 +22,13 @@
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (c) 2018-Present Private Internet Access
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
import UIKit
import NetworkExtension
import TunnelKit
private let ca = OpenVPN.CryptoContainer(pem: """
-----BEGIN CERTIFICATE-----
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu
dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx
IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB
FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1
MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg
QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE
AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50
ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy
bmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXD
L1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzX
lH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWp
cdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/
8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB
/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RC
OfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tL
y8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZO
sqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpM
b3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4G
A1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUg
SW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2Vz
czEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5j
b22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAn
a5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xU
ryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK3
7pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyC
GohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz
1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUt
YDQ8z9v+DMO6iwyIDRiU
-----END CERTIFICATE-----
""")
private let appGroup = "group.com.algoritmico.ios.demo.BasicTunnel"
extension ViewController {
private static let appGroup = "group.com.algoritmico.ios.demo.BasicTunnel"
private static let tunnelIdentifier = "com.algoritmico.ios.demo.BasicTunnel.Extension"
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 credentials = OpenVPN.Credentials(textUsername.text!, textPassword.text!)
var sessionBuilder = OpenVPN.ConfigurationBuilder()
sessionBuilder.ca = ca
sessionBuilder.cipher = .aes128cbc
sessionBuilder.digest = .sha1
sessionBuilder.compressionFraming = .compLZO
sessionBuilder.renegotiatesAfter = nil
sessionBuilder.hostname = hostname
let socketType: SocketType = switchTCP.isOn ? .tcp : .udp
sessionBuilder.endpointProtocols = [EndpointProtocol(socketType, port)]
sessionBuilder.usesPIAPatches = true
var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
builder.mtu = 1350
builder.shouldDebug = true
builder.masksPrivateData = false
let configuration = builder.build()
return try! configuration.generatedTunnelProtocol(
withBundleIdentifier: ViewController.tunnelIdentifier,
appGroup: ViewController.appGroup,
credentials: credentials
)
}
}
private let tunnelIdentifier = "com.algoritmico.ios.demo.BasicTunnel.Extension"
class ViewController: UIViewController, URLSessionDataDelegate {
@IBOutlet var textUsername: UITextField!
@ -129,160 +47,78 @@ class ViewController: UIViewController, URLSessionDataDelegate {
@IBOutlet var textLog: UITextView!
//
var currentManager: NETunnelProviderManager?
var status = NEVPNStatus.invalid
private let vpn = StandardVPNProvider(bundleIdentifier: tunnelIdentifier)
override func viewDidLoad() {
super.viewDidLoad()
textServer.text = "germany"
textDomain.text = "privateinternetaccess.com"
textPort.text = "1198"
textServer.text = "es"
textDomain.text = "lazerpenguin.com"
textPort.text = "443"
switchTCP.isOn = false
textUsername.text = "myusername"
textPassword.text = "mypassword"
textUsername.text = ""
textPassword.text = ""
NotificationCenter.default.addObserver(self,
selector: #selector(VPNStatusDidChange(notification:)),
name: .NEVPNStatusDidChange,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(VPNStatusDidChange(notification:)),
name: VPN.didChangeStatus,
object: nil
)
reloadCurrentManager(nil)
vpn.prepare(completionHandler: 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()
switch vpn.status {
case .disconnected:
connect()
case .connected, .connecting, .disconnecting:
disconnect()
}
}
@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
let server = textServer.text!
let domain = textDomain.text!
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
let port = UInt16(textPort.text!)!
let socketType: SocketType = switchTCP.isOn ? .tcp : .udp
let credentials = OpenVPN.Credentials(textUsername.text!, textPassword.text!)
let cfg = Configuration.make(hostname: hostname, port: port, socketType: socketType)
let proto = try! cfg.generatedTunnelProtocol(
withBundleIdentifier: tunnelIdentifier,
appGroup: appGroup,
credentials: credentials
)
let neCfg = NetworkExtensionVPNConfiguration(protocolConfiguration: proto, onDemandRules: [])
vpn.reconnect(configuration: neCfg) { (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()
})
vpn.disconnect(completionHandler: nil)
}
@IBAction func displayLog() {
guard let vpn = currentManager?.connection as? NETunnelProviderSession else {
return
}
try? vpn.sendProviderMessage(OpenVPNTunnelProvider.Message.requestLog.data) { (data) in
guard let data = data, let log = String(data: data, encoding: .utf8) else {
return
}
vpn.requestDebugLog(fallback: { "" }) { (log) in
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.tunnelIdentifier) {
manager = m
break
}
}
}
if (manager == nil) {
manager = NETunnelProviderManager()
}
self.currentManager = manager
self.status = manager!.connection.status
self.updateButton()
completionHandler?(nil)
}
}
func updateButton() {
switch status {
switch vpn.status {
case .connected, .connecting:
buttonConnection.setTitle("Disconnect", for: .normal)
@ -291,19 +127,11 @@ class ViewController: UIViewController, URLSessionDataDelegate {
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
print("VPNStatusDidChange: \(vpn.status)")
updateButton()
}

View File

@ -1,9 +1,9 @@
//
// AppDelegate.swift
// BasicTunnel-macOS
// Demo
//
// Created by Davide De Rosa on 10/15/17.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
@ -22,18 +22,6 @@
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (c) 2018-Present Private Internet Access
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
import Cocoa
import SwiftyBeaver

View File

@ -25,7 +25,7 @@
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2018 Davide De Rosa. All rights reserved.</string>
<string>Copyright (c) 2020 Davide De Rosa. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>

View File

@ -1,9 +1,9 @@
//
// ViewController.swift
// BasicTunnel-macOS
// Demo
//
// Created by Davide De Rosa on 10/15/17.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
@ -22,96 +22,14 @@
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (c) 2018-Present Private Internet Access
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
import Cocoa
import NetworkExtension
import TunnelKit
private let ca = OpenVPN.CryptoContainer(pem: """
-----BEGIN CERTIFICATE-----
MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD
VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV
BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu
dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx
IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB
FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1
MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex
EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg
QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE
AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50
ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy
bmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXD
L1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzX
lH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWp
cdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/
8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB
/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RC
OfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tL
y8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZO
sqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpM
b3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4G
A1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUg
SW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2Vz
czEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5j
b22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAn
a5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xU
ryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK3
7pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyC
GohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz
1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUt
YDQ8z9v+DMO6iwyIDRiU
-----END CERTIFICATE-----
""")
private let appGroup = "DTDYD63ZX9.group.com.algoritmico.macos.demo.BasicTunnel"
extension ViewController {
private static let appGroup = "DTDYD63ZX9.group.com.algoritmico.macos.demo.BasicTunnel"
private static let tunnelIdentifier = "com.algoritmico.macos.demo.BasicTunnel.Extension"
private func makeProtocol() -> NETunnelProviderProtocol {
let server = textServer.stringValue
let domain = textDomain.stringValue
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
let port = UInt16(textPort.stringValue)!
let credentials = OpenVPN.Credentials(textUsername.stringValue, textPassword.stringValue)
var sessionBuilder = OpenVPN.ConfigurationBuilder()
sessionBuilder.ca = ca
sessionBuilder.cipher = .aes128cbc
sessionBuilder.digest = .sha1
sessionBuilder.compressionFraming = .compLZO
sessionBuilder.renegotiatesAfter = nil
sessionBuilder.hostname = hostname
// let socketType: SocketType = isTCP ? .tcp : .udp
let socketType: SocketType = .udp
sessionBuilder.endpointProtocols = [EndpointProtocol(socketType, port)]
sessionBuilder.usesPIAPatches = true
var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
builder.mtu = 1350
builder.shouldDebug = true
builder.masksPrivateData = false
let configuration = builder.build()
return try! configuration.generatedTunnelProtocol(
withBundleIdentifier: ViewController.tunnelIdentifier,
appGroup: ViewController.appGroup,
credentials: credentials
)
}
}
private let tunnelIdentifier = "com.algoritmico.macos.demo.BasicTunnel.Extension"
class ViewController: NSViewController {
@IBOutlet var textUsername: NSTextField!
@ -126,139 +44,67 @@ class ViewController: NSViewController {
@IBOutlet var buttonConnection: NSButton!
var currentManager: NETunnelProviderManager?
var status = NEVPNStatus.invalid
private let vpn = StandardVPNProvider(bundleIdentifier: tunnelIdentifier)
override func viewDidLoad() {
super.viewDidLoad()
textServer.stringValue = "germany"
textDomain.stringValue = "privateinternetaccess.com"
textPort.stringValue = "1198"
textUsername.stringValue = "myusername"
textPassword.stringValue = "mypassword"
textServer.stringValue = "es"
textDomain.stringValue = "lazerpenguin.com"
textPort.stringValue = "443"
textUsername.stringValue = ""
textPassword.stringValue = ""
NotificationCenter.default.addObserver(
self,
selector: #selector(VPNStatusDidChange(notification:)),
name: .NEVPNStatusDidChange,
name: VPN.didChangeStatus,
object: nil
)
reloadCurrentManager(nil)
//
vpn.prepare(completionHandler: 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()
switch vpn.status {
case .disconnected:
connect()
case .connected, .connecting, .disconnecting:
disconnect()
}
}
func connect() {
configureVPN({ (manager) in
return self.makeProtocol()
}, completionHandler: { (error) in
let server = textServer.stringValue
let domain = textDomain.stringValue
let hostname = ((domain == "") ? server : [server, domain].joined(separator: "."))
let port = UInt16(textPort.stringValue)!
let credentials = OpenVPN.Credentials(textUsername.stringValue, textPassword.stringValue)
let cfg = Configuration.make(hostname: hostname, port: port, socketType: .udp)
let proto = try! cfg.generatedTunnelProtocol(
withBundleIdentifier: tunnelIdentifier,
appGroup: appGroup,
credentials: credentials
)
let neCfg = NetworkExtensionVPNConfiguration(protocolConfiguration: proto, onDemandRules: [])
vpn.reconnect(configuration: neCfg) { (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()
})
vpn.disconnect(completionHandler: nil)
}
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.tunnelIdentifier) {
manager = m
break
}
}
}
if (manager == nil) {
manager = NETunnelProviderManager()
}
self.currentManager = manager
self.status = manager!.connection.status
self.updateButton()
completionHandler?(nil)
}
}
func updateButton() {
switch status {
switch vpn.status {
case .connected, .connecting:
buttonConnection.title = "Disconnect"
@ -267,22 +113,14 @@ class ViewController: NSViewController {
case .disconnecting:
buttonConnection.title = "Disconnecting"
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
print("VPNStatusDidChange: \(vpn.status)")
updateButton()
}
private func testFetchRef() {
// let keychain = Keychain(group: ViewController.APP_GROUP)
// let username = "foo"

View File

@ -1,9 +1,9 @@
//
// PacketTunnelProvider.swift
// BasicTunnelExtension-iOS
// Demo
//
// Created by Davide De Rosa on 9/15/17.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
@ -22,18 +22,6 @@
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (c) 2018-Present Private Internet Access
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
import TunnelKit

View File

@ -30,6 +30,6 @@
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2018 Davide De Rosa. All rights reserved.</string>
<string>Copyright (c) 2020 Davide De Rosa. All rights reserved.</string>
</dict>
</plist>

View File

@ -1,9 +1,9 @@
//
// PacketTunnelProvider.swift
// BasicTunnelExtension-macOS
// Demo
//
// Created by Davide De Rosa on 10/15/17.
// Copyright (c) 2018 Davide De Rosa. All rights reserved.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
@ -22,18 +22,6 @@
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
// This file incorporates work covered by the following copyright and
// permission notice:
//
// Copyright (c) 2018-Present Private Internet Access
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
import TunnelKit

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
0EAC574724943B5D00D0FCE0 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC574624943B5D00D0FCE0 /* Configuration.swift */; };
0EAC574824943B5D00D0FCE0 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC574624943B5D00D0FCE0 /* Configuration.swift */; };
0EB39FE91F7424F80023AFFC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB39FE81F7424F80023AFFC /* AppDelegate.swift */; };
0EB39FEB1F7424F80023AFFC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB39FEA1F7424F80023AFFC /* ViewController.swift */; };
0EB39FEE1F7424F80023AFFC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0EB39FEC1F7424F80023AFFC /* Main.storyboard */; };
@ -71,6 +73,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0EAC574624943B5D00D0FCE0 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
0EB39FE61F7424F80023AFFC /* BasicTunnel-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "BasicTunnel-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
0EB39FE81F7424F80023AFFC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
0EB39FEA1F7424F80023AFFC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@ -147,6 +150,14 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0EAC574524943B3500D0FCE0 /* Shared */ = {
isa = PBXGroup;
children = (
0EAC574624943B5D00D0FCE0 /* Configuration.swift */,
);
path = Shared;
sourceTree = "<group>";
};
0EB39FC41F7424580023AFFC = {
isa = PBXGroup;
children = (
@ -154,6 +165,7 @@
0EB39FFD1F7425140023AFFC /* BasicTunnelExtension-iOS */,
0EB6EEB81F92D417005F6221 /* BasicTunnel-macOS */,
0EB6EECC1F92D43D005F6221 /* BasicTunnelExtension-macOS */,
0EAC574524943B3500D0FCE0 /* Shared */,
0EB39FCE1F7424580023AFFC /* Products */,
B850E57E641AD1B37E79BAB5 /* Frameworks */,
94C1D7B5B11C6CCE74B21D93 /* Pods */,
@ -541,8 +553,6 @@
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-macOS-BasicTunnel-macOS/Pods-macOS-BasicTunnel-macOS-frameworks.sh",
"${PODS_ROOT}/OpenSSL-Apple/frameworks/MacOSX/openssl.framework",
@ -550,8 +560,6 @@
"${BUILT_PRODUCTS_DIR}/TunnelKit-macOS/TunnelKit.framework",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
);
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework",
@ -567,8 +575,6 @@
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-iOS-BasicTunnel-iOS/Pods-iOS-BasicTunnel-iOS-frameworks.sh",
"${PODS_ROOT}/OpenSSL-Apple/frameworks/iPhone/openssl.framework",
@ -576,8 +582,6 @@
"${BUILT_PRODUCTS_DIR}/TunnelKit-iOS/TunnelKit.framework",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
);
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyBeaver.framework",
@ -639,6 +643,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0EAC574724943B5D00D0FCE0 /* Configuration.swift in Sources */,
0EB39FEB1F7424F80023AFFC /* ViewController.swift in Sources */,
0EB39FE91F7424F80023AFFC /* AppDelegate.swift in Sources */,
);
@ -656,6 +661,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0EAC574824943B5D00D0FCE0 /* Configuration.swift in Sources */,
0EB6EEBC1F92D417005F6221 /* ViewController.swift in Sources */,
0EB6EEBA1F92D417005F6221 /* AppDelegate.swift in Sources */,
);

View File

@ -6,6 +6,7 @@ abstract_target 'iOS' do
target 'BasicTunnelExtension-iOS' do
pod 'TunnelKit', :path => '..'
pod 'TunnelKit/Manager', :path => '..'
end
target 'BasicTunnel-iOS' do
end
@ -16,6 +17,7 @@ abstract_target 'macOS' do
target 'BasicTunnelExtension-macOS' do
pod 'TunnelKit', :path => '..'
pod 'TunnelKit/Manager', :path => '..'
end
target 'BasicTunnel-macOS' do
end

View File

@ -1,20 +1,23 @@
PODS:
- OpenSSL-Apple (1.1.0j.2)
- SwiftyBeaver (1.7.0)
- TunnelKit (2.0.0):
- TunnelKit/Protocols/OpenVPN (= 2.0.0)
- TunnelKit/AppExtension (2.0.0):
- OpenSSL-Apple (1.1.1g.6)
- SwiftyBeaver (1.9.1)
- TunnelKit (2.3.0):
- TunnelKit/Protocols/OpenVPN (= 2.3.0)
- TunnelKit/AppExtension (2.3.0):
- SwiftyBeaver
- TunnelKit/Core
- TunnelKit/Core (2.0.0):
- OpenSSL-Apple (~> 1.1.0j.2)
- TunnelKit/Core (2.3.0):
- SwiftyBeaver
- TunnelKit/Protocols/OpenVPN (2.0.0):
- TunnelKit/Manager (2.3.0):
- SwiftyBeaver
- TunnelKit/Protocols/OpenVPN (2.3.0):
- OpenSSL-Apple (~> 1.1.1g.6)
- TunnelKit/AppExtension
- TunnelKit/Core
DEPENDENCIES:
- TunnelKit (from `..`)
- TunnelKit/Manager (from `..`)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
@ -26,10 +29,10 @@ EXTERNAL SOURCES:
:path: ".."
SPEC CHECKSUMS:
OpenSSL-Apple: e88e1eb314acb4a05e2348069790c4aa49f6d319
SwiftyBeaver: 4cc0080d2e23f980652e28978db11a5c9da39165
TunnelKit: 821c15bb87aafae69eb8c63e4cc46d883fff8797
OpenSSL-Apple: c9c1b9c5b2b1fc4e1758fc5f0836b58ae7fd8183
SwiftyBeaver: a1f5691458561414bcfab51874b2b7445451602b
TunnelKit: a4fa4ecc6fc2b9fa74c38609c0e8fc4441d9672e
PODFILE CHECKSUM: f66dfaaa92a8d04ab2743f3caeab0ac9f9f25859
PODFILE CHECKSUM: 67d7cb3c3db58264711361062345c3b7831e510e
COCOAPODS: 1.6.1
COCOAPODS: 1.9.1

View File

@ -0,0 +1,188 @@
//
// Configuration.swift
// Demo
//
// Created by Davide De Rosa on 6/13/20.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import TunnelKit
struct Configuration {
static let ca = OpenVPN.CryptoContainer(pem: """
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
""")
static let clientCertificate = OpenVPN.CryptoContainer(pem: """
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
""")
static let clientKey = OpenVPN.CryptoContainer(pem: """
-----BEGIN PRIVATE KEY-----
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
1LXZrtjAB4U+/F7um6YyAavpHYq9hg==
-----END PRIVATE KEY-----
""")
static func make(hostname: String, port: UInt16, socketType: SocketType) -> OpenVPNTunnelProvider.Configuration {
var sessionBuilder = OpenVPN.ConfigurationBuilder()
sessionBuilder.ca = ca
sessionBuilder.cipher = .aes128cbc
sessionBuilder.digest = .sha1
sessionBuilder.compressionFraming = .compLZO
sessionBuilder.renegotiatesAfter = nil
sessionBuilder.hostname = hostname
sessionBuilder.endpointProtocols = [EndpointProtocol(socketType, port)]
sessionBuilder.clientCertificate = clientCertificate
sessionBuilder.clientKey = clientKey
var builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
builder.mtu = 1350
builder.shouldDebug = true
builder.masksPrivateData = false
return builder.build()
}
}

View File

@ -153,6 +153,15 @@ There are no physical network implementations (e.g. UDP or TCP) in this module.
Provides a layer on top of the NetworkExtension framework. Most importantly, bridges native [NWUDPSession][ne-udp] and [NWTCPConnection][ne-tcp] to an abstract `GenericSocket` interface, thus making a multi-protocol VPN dramatically easier to manage.
### Manager
This subspec includes convenient classes to control the VPN tunnel from your app without the NetworkExtension headaches. Have a look at `VPNProvider` implementations:
- `MockVPNProvider` (default, useful to test on simulator)
- `StandardVPNProvider`
Set `VPN.shared` to either of them at app launch time.
### Protocols/OpenVPN
Here you will find the low-level entities on top of which an OpenVPN connection is established. Code is mixed Swift and Obj-C, most of it is not exposed to consumers. The module depends on OpenSSL.

View File

@ -33,6 +33,13 @@ Pod::Spec.new do |s|
p.dependency "TunnelKit/Core"
end
s.subspec "Manager" do |p|
p.source_files = "TunnelKit/Sources/Manager/**/*.swift"
p.frameworks = "NetworkExtension"
p.dependency "SwiftyBeaver"
end
s.subspec "Protocols" do |t|
t.subspec "OpenVPN" do |p|
p.source_files = "TunnelKit/Sources/Protocols/OpenVPN/**/*.{h,m,swift}"

View File

@ -168,6 +168,18 @@
0E7F3F6B246ABA0F006BE77F /* IPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F3F69246ABA0F006BE77F /* IPHeader.swift */; };
0EA82A282190B220007960EB /* TunnelKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3251C51F95770D00C108D9 /* TunnelKit.framework */; };
0EA82A3E2190B2BC007960EB /* pia-2048.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0E749F612178911C00BB2701 /* pia-2048.pem */; };
0EAC57372494277A00D0FCE0 /* StandardVPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57312494277A00D0FCE0 /* StandardVPNProvider.swift */; };
0EAC57382494277A00D0FCE0 /* StandardVPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57312494277A00D0FCE0 /* StandardVPNProvider.swift */; };
0EAC57392494277A00D0FCE0 /* MockVPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57322494277A00D0FCE0 /* MockVPNProvider.swift */; };
0EAC573A2494277A00D0FCE0 /* MockVPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57322494277A00D0FCE0 /* MockVPNProvider.swift */; };
0EAC573B2494277A00D0FCE0 /* VPNStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57332494277A00D0FCE0 /* VPNStatus.swift */; };
0EAC573C2494277A00D0FCE0 /* VPNStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57332494277A00D0FCE0 /* VPNStatus.swift */; };
0EAC573D2494277A00D0FCE0 /* VPNConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57342494277A00D0FCE0 /* VPNConfiguration.swift */; };
0EAC573E2494277A00D0FCE0 /* VPNConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57342494277A00D0FCE0 /* VPNConfiguration.swift */; };
0EAC573F2494277A00D0FCE0 /* VPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57352494277A00D0FCE0 /* VPNProvider.swift */; };
0EAC57402494277A00D0FCE0 /* VPNProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57352494277A00D0FCE0 /* VPNProvider.swift */; };
0EAC57412494277A00D0FCE0 /* VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57362494277A00D0FCE0 /* VPN.swift */; };
0EAC57422494277A00D0FCE0 /* VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAC57362494277A00D0FCE0 /* VPN.swift */; };
0ECAF84A246697DA00D8266A /* TunnelKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E17D7F91F730D9F009EE129 /* TunnelKit.framework */; };
0ECAF84B246697DA00D8266A /* TunnelKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0E17D7F91F730D9F009EE129 /* TunnelKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0ECC60D82254981A0020BEAC /* ConfigurationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECC60D72254981A0020BEAC /* ConfigurationError.swift */; };
@ -424,6 +436,12 @@
0E85A25B202CCA3D0059E9F9 /* TunnelKitHost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TunnelKitHost.entitlements; sourceTree = "<group>"; };
0EA82A232190B220007960EB /* TunnelKitTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TunnelKitTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
0EA82A272190B220007960EB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0EAC57312494277A00D0FCE0 /* StandardVPNProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StandardVPNProvider.swift; sourceTree = "<group>"; };
0EAC57322494277A00D0FCE0 /* MockVPNProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockVPNProvider.swift; sourceTree = "<group>"; };
0EAC57332494277A00D0FCE0 /* VPNStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNStatus.swift; sourceTree = "<group>"; };
0EAC57342494277A00D0FCE0 /* VPNConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNConfiguration.swift; sourceTree = "<group>"; };
0EAC57352494277A00D0FCE0 /* VPNProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNProvider.swift; sourceTree = "<group>"; };
0EAC57362494277A00D0FCE0 /* VPN.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPN.swift; sourceTree = "<group>"; };
0EB03E0E2290CF52006D03A0 /* module.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
0ECC60D72254981A0020BEAC /* ConfigurationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationError.swift; sourceTree = "<group>"; };
0ECEB1132252C8E900E9E551 /* tunnelbear.enc.8.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.enc.8.ovpn; sourceTree = "<group>"; };
@ -618,6 +636,7 @@
0EE2F9DD22918DA100F56F49 /* AppExtension */,
0EFEB4292006D3C800F81029 /* Core */,
0E23B41922982AF800304C30 /* Extra */,
0EAC57302494277A00D0FCE0 /* Manager */,
0E23B3E022982AF800304C30 /* Protocols */,
);
path = Sources;
@ -800,6 +819,19 @@
path = "TunnelKitTests-iOS";
sourceTree = "<group>";
};
0EAC57302494277A00D0FCE0 /* Manager */ = {
isa = PBXGroup;
children = (
0EAC57322494277A00D0FCE0 /* MockVPNProvider.swift */,
0EAC57312494277A00D0FCE0 /* StandardVPNProvider.swift */,
0EAC57362494277A00D0FCE0 /* VPN.swift */,
0EAC57342494277A00D0FCE0 /* VPNConfiguration.swift */,
0EAC57352494277A00D0FCE0 /* VPNProvider.swift */,
0EAC57332494277A00D0FCE0 /* VPNStatus.swift */,
);
path = Manager;
sourceTree = "<group>";
};
0EE2F9DD22918DA100F56F49 /* AppExtension */ = {
isa = PBXGroup;
children = (
@ -1410,12 +1442,14 @@
files = (
0EE2F974229163C900F56F49 /* Proxy.swift in Sources */,
0EFEB4732006D3C800F81029 /* LinkInterface.swift in Sources */,
0EAC573B2494277A00D0FCE0 /* VPNStatus.swift in Sources */,
0E23B48922982AF800304C30 /* PacketStream.m in Sources */,
0E23B3DE229749C600304C30 /* LinkProducer.swift in Sources */,
0E23B42722982AF800304C30 /* CryptoAEAD.m in Sources */,
0EE2F9F222918DA100F56F49 /* NETunnelInterface.swift in Sources */,
0E23B46722982AF800304C30 /* Authenticator.swift in Sources */,
0E23B43922982AF800304C30 /* OpenVPNError.swift in Sources */,
0EAC573F2494277A00D0FCE0 /* VPNProvider.swift in Sources */,
0E23B44F22982AF800304C30 /* OpenVPNTunnelProvider+Interaction.swift in Sources */,
0E23B46D22982AF800304C30 /* StaticKey.swift in Sources */,
0E23B49F22982AF800304C30 /* minilzo.c in Sources */,
@ -1432,6 +1466,7 @@
0E12B29E21449ADB00B4BAE9 /* NSRegularExpression+Shortcuts.swift in Sources */,
0EE2F9F822918DA100F56F49 /* NWUDPSessionState+Description.swift in Sources */,
0EE2F9AC2291853D00F56F49 /* Session.swift in Sources */,
0EAC57412494277A00D0FCE0 /* VPN.swift in Sources */,
0E23B48122982AF800304C30 /* ControlPacket.m in Sources */,
0EFEB4622006D3C800F81029 /* SecureRandom.swift in Sources */,
0E011F7D2196D97200BA59EE /* EndpointProtocol.swift in Sources */,
@ -1479,9 +1514,12 @@
0E23B45722982AF800304C30 /* NETCPLink.swift in Sources */,
0E23B45322982AF800304C30 /* ConnectionStrategy.swift in Sources */,
0E011F7A2196D93600BA59EE /* SocketType.swift in Sources */,
0EAC57392494277A00D0FCE0 /* MockVPNProvider.swift in Sources */,
0E23B45922982AF800304C30 /* OpenVPNTunnelProvider.swift in Sources */,
0EFEB45A2006D3C800F81029 /* TunnelInterface.swift in Sources */,
0EAC57372494277A00D0FCE0 /* StandardVPNProvider.swift in Sources */,
0E23B47322982AF800304C30 /* MSS.m in Sources */,
0EAC573D2494277A00D0FCE0 /* VPNConfiguration.swift in Sources */,
0E23B45D22982AF800304C30 /* ProtocolMacros.swift in Sources */,
0EE2FA0022918DA100F56F49 /* MemoryDestination.swift in Sources */,
);
@ -1493,12 +1531,14 @@
files = (
0EE2F975229163C900F56F49 /* Proxy.swift in Sources */,
0EFEB4A12006D7F300F81029 /* LinkInterface.swift in Sources */,
0EAC573C2494277A00D0FCE0 /* VPNStatus.swift in Sources */,
0E23B48A22982AF800304C30 /* PacketStream.m in Sources */,
0E23B3DF229749C600304C30 /* LinkProducer.swift in Sources */,
0E23B42822982AF800304C30 /* CryptoAEAD.m in Sources */,
0EE2F9F322918DA100F56F49 /* NETunnelInterface.swift in Sources */,
0E23B46822982AF800304C30 /* Authenticator.swift in Sources */,
0E23B43A22982AF800304C30 /* OpenVPNError.swift in Sources */,
0EAC57402494277A00D0FCE0 /* VPNProvider.swift in Sources */,
0E23B45022982AF800304C30 /* OpenVPNTunnelProvider+Interaction.swift in Sources */,
0E23B46E22982AF800304C30 /* StaticKey.swift in Sources */,
0E23B4A022982AF800304C30 /* minilzo.c in Sources */,
@ -1515,6 +1555,7 @@
0E12B29F21449ADB00B4BAE9 /* NSRegularExpression+Shortcuts.swift in Sources */,
0EE2F9F922918DA100F56F49 /* NWUDPSessionState+Description.swift in Sources */,
0EE2F9AD2291853D00F56F49 /* Session.swift in Sources */,
0EAC57422494277A00D0FCE0 /* VPN.swift in Sources */,
0E23B48222982AF800304C30 /* ControlPacket.m in Sources */,
0E011F7E2196D97200BA59EE /* EndpointProtocol.swift in Sources */,
0EE2F9F122918DA100F56F49 /* NETCPSocket.swift in Sources */,
@ -1562,9 +1603,12 @@
0E23B45822982AF800304C30 /* NETCPLink.swift in Sources */,
0E23B45422982AF800304C30 /* ConnectionStrategy.swift in Sources */,
0EFEB49D2006D7F300F81029 /* IOInterface.swift in Sources */,
0EAC573A2494277A00D0FCE0 /* MockVPNProvider.swift in Sources */,
0E23B45A22982AF800304C30 /* OpenVPNTunnelProvider.swift in Sources */,
0E011F7B2196D93600BA59EE /* SocketType.swift in Sources */,
0EAC57382494277A00D0FCE0 /* StandardVPNProvider.swift in Sources */,
0E23B47422982AF800304C30 /* MSS.m in Sources */,
0EAC573E2494277A00D0FCE0 /* VPNConfiguration.swift in Sources */,
0E23B45E22982AF800304C30 /* ProtocolMacros.swift in Sources */,
0EE2FA0122918DA100F56F49 /* MemoryDestination.swift in Sources */,
);

View File

@ -44,7 +44,7 @@ private let log = SwiftyBeaver.self
public class InterfaceObserver: NSObject {
/// A change in Wi-Fi state occurred.
public static let didDetectWifiChange = NSNotification.Name("InterfaceObserverDidDetectWifiChange")
public static let didDetectWifiChange = Notification.Name("InterfaceObserverDidDetectWifiChange")
private var queue: DispatchQueue?

View File

@ -0,0 +1,86 @@
//
// MockVPNProvider.swift
// TunnelKit
//
// Created by Davide De Rosa on 6/15/18.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
/// :nodoc:
public class MockVPNProvider: VPNProvider {
public let isPrepared: Bool = true
public private(set) var isEnabled: Bool = false
public private(set) var status: VPNStatus = .disconnected
public func prepare(completionHandler: (() -> Void)?) {
NotificationCenter.default.post(name: VPN.didPrepare, object: nil)
completionHandler?()
}
public func install(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
isEnabled = true
completionHandler?(nil)
}
public func connect(completionHandler: ((Error?) -> Void)?) {
isEnabled = true
status = .connected
NotificationCenter.default.post(name: VPN.didChangeStatus, object: self)
completionHandler?(nil)
}
public func disconnect(completionHandler: ((Error?) -> Void)?) {
isEnabled = false
status = .disconnected
NotificationCenter.default.post(name: VPN.didChangeStatus, object: self)
completionHandler?(nil)
}
public func reconnect(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
isEnabled = true
status = .connected
NotificationCenter.default.post(name: VPN.didChangeStatus, object: self)
completionHandler?(nil)
}
public func uninstall(completionHandler: (() -> Void)?) {
isEnabled = false
status = .disconnected
NotificationCenter.default.post(name: VPN.didChangeStatus, object: self)
completionHandler?()
}
public func requestDebugLog(fallback: (() -> String)?, completionHandler: @escaping (String) -> Void) {
let log = [String](repeating: "lorem ipsum", count: 1000).joined(separator: " ")
completionHandler(log)
}
public func requestBytesCount(completionHandler: @escaping ((UInt, UInt)?) -> Void) {
completionHandler((0, 0))
}
public func requestServerConfiguration(completionHandler: @escaping (Any?) -> Void) {
completionHandler(nil)
}
}

View File

@ -0,0 +1,317 @@
//
// StandardVPNProvider.swift
// TunnelKit
//
// Created by Davide De Rosa on 6/15/18.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import NetworkExtension
/// :nodoc:
public class StandardVPNProvider: VPNProvider {
private let bundleIdentifier: String
private var manager: NETunnelProviderManager?
private var lastNotifiedStatus: VPNStatus?
public init(bundleIdentifier: String) {
self.bundleIdentifier = bundleIdentifier
let nc = NotificationCenter.default
nc.addObserver(self, selector: #selector(vpnDidUpdate(_:)), name: .NEVPNStatusDidChange, object: nil)
nc.addObserver(self, selector: #selector(vpnDidReinstall(_:)), name: .NEVPNConfigurationChange, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: VPNProvider
public var isPrepared: Bool {
return manager != nil
}
public var isEnabled: Bool {
guard let manager = manager else {
return false
}
return manager.isEnabled && manager.isOnDemandEnabled
}
public var status: VPNStatus {
guard let neStatus = manager?.connection.status else {
return .disconnected
}
switch neStatus {
case .connected:
return .connected
case .connecting, .reasserting:
return .connecting
case .disconnecting:
return .disconnecting
case .disconnected, .invalid:
return .disconnected
@unknown default:
return .disconnected
}
}
public func prepare(completionHandler: (() -> Void)?) {
find(with: bundleIdentifier) {
self.manager = $0
NotificationCenter.default.post(name: VPN.didPrepare, object: nil)
completionHandler?()
}
}
public func install(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
guard let configuration = configuration as? NetworkExtensionVPNConfiguration else {
fatalError("Not a NetworkExtensionVPNConfiguration")
}
find(with: bundleIdentifier) {
guard let manager = $0 else {
completionHandler?(nil)
return
}
self.manager = manager
manager.protocolConfiguration = configuration.protocolConfiguration
manager.onDemandRules = configuration.onDemandRules
manager.isOnDemandEnabled = true
manager.isEnabled = true
manager.saveToPreferences { (error) in
guard error == nil else {
manager.isOnDemandEnabled = false
manager.isEnabled = false
completionHandler?(error)
return
}
manager.loadFromPreferences { (error) in
completionHandler?(error)
}
}
}
}
public func connect(completionHandler: ((Error?) -> Void)?) {
do {
try manager?.connection.startVPNTunnel()
completionHandler?(nil)
} catch let e {
completionHandler?(e)
}
}
public func disconnect(completionHandler: ((Error?) -> Void)?) {
guard let manager = manager else {
completionHandler?(nil)
return
}
manager.connection.stopVPNTunnel()
manager.isOnDemandEnabled = false
manager.isEnabled = false
manager.saveToPreferences(completionHandler: completionHandler)
}
public func reconnect(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
guard let configuration = configuration as? NetworkExtensionVPNConfiguration else {
fatalError("Not a NetworkExtensionVPNConfiguration")
}
install(configuration: configuration) { (error) in
guard error == nil else {
completionHandler?(nil)
return
}
let connectBlock = {
self.connect(completionHandler: completionHandler)
}
if self.status != .disconnected {
self.manager?.connection.stopVPNTunnel()
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: connectBlock)
} else {
connectBlock()
}
}
}
public func uninstall(completionHandler: (() -> Void)?) {
find(with: bundleIdentifier) { (manager) in
guard let manager = manager else {
completionHandler?()
return
}
manager.connection.stopVPNTunnel()
manager.removeFromPreferences { (error) in
self.manager = nil
completionHandler?()
}
}
}
public func requestDebugLog(fallback: (() -> String)?, completionHandler: @escaping (String) -> Void) {
guard status != .disconnected else {
completionHandler(fallback?() ?? "")
return
}
findAndRequestDebugLog { (recent) in
DispatchQueue.main.async {
guard let recent = recent else {
completionHandler(fallback?() ?? "")
return
}
completionHandler(recent)
}
}
}
public func requestBytesCount(completionHandler: @escaping ((UInt, UInt)?) -> Void) {
find(with: bundleIdentifier) {
self.manager = $0
guard let session = self.manager?.connection as? NETunnelProviderSession else {
DispatchQueue.main.async {
completionHandler(nil)
}
return
}
do {
try session.sendProviderMessage(OpenVPNTunnelProvider.Message.dataCount.data) { (data) in
guard let data = data, data.count == 16 else {
DispatchQueue.main.async {
completionHandler(nil)
}
return
}
let bytesIn: UInt = data.subdata(in: 0..<8).withUnsafeBytes { $0.load(as: UInt.self) }
let bytesOut: UInt = data.subdata(in: 8..<16).withUnsafeBytes { $0.load(as: UInt.self) }
DispatchQueue.main.async {
completionHandler((bytesIn, bytesOut))
}
}
} catch {
DispatchQueue.main.async {
completionHandler(nil)
}
}
}
}
public func requestServerConfiguration(completionHandler: @escaping (Any?) -> Void) {
find(with: bundleIdentifier) {
self.manager = $0
guard let session = self.manager?.connection as? NETunnelProviderSession else {
DispatchQueue.main.async {
completionHandler(nil)
}
return
}
do {
try session.sendProviderMessage(OpenVPNTunnelProvider.Message.serverConfiguration.data) { (data) in
guard let data = data, let cfg = try? JSONDecoder().decode(OpenVPN.Configuration.self, from: data) else {
DispatchQueue.main.async {
completionHandler(nil)
}
return
}
DispatchQueue.main.async {
completionHandler(cfg)
}
}
} catch {
DispatchQueue.main.async {
completionHandler(nil)
}
}
}
}
// MARK: Helpers
private func find(with bundleIdentifier: String, completionHandler: @escaping (NETunnelProviderManager?) -> Void) {
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
guard error == nil else {
completionHandler(nil)
return
}
let manager = managers?.first {
guard let ptm = $0.protocolConfiguration as? NETunnelProviderProtocol else {
return false
}
return (ptm.providerBundleIdentifier == bundleIdentifier)
}
completionHandler(manager ?? NETunnelProviderManager())
}
}
private func findAndRequestDebugLog(completionHandler: @escaping (String?) -> Void) {
find(with: bundleIdentifier) {
self.manager = $0
guard let session = self.manager?.connection as? NETunnelProviderSession else {
completionHandler(nil)
return
}
StandardVPNProvider.requestDebugLog(session: session, completionHandler: completionHandler)
}
}
private static func requestDebugLog(session: NETunnelProviderSession, completionHandler: @escaping (String?) -> Void) {
do {
try session.sendProviderMessage(OpenVPNTunnelProvider.Message.requestLog.data) { (data) in
guard let data = data, !data.isEmpty else {
completionHandler(nil)
return
}
let newestLog = String(data: data, encoding: .utf8)
completionHandler(newestLog)
}
} catch {
completionHandler(nil)
}
}
// MARK: Notifications
@objc private func vpnDidUpdate(_ notification: Notification) {
// guard let connection = notification.object as? NETunnelProviderSession else {
// return
// }
// log.debug("VPN status did change: \(connection.status.rawValue)")
let status = self.status
if let last = lastNotifiedStatus {
guard status != last else {
return
}
}
lastNotifiedStatus = status
NotificationCenter.default.post(name: VPN.didChangeStatus, object: self)
}
@objc private func vpnDidReinstall(_ notification: Notification) {
NotificationCenter.default.post(name: VPN.didReinstall, object: self)
}
}

View File

@ -0,0 +1,42 @@
//
// VPN.swift
// TunnelKit
//
// Created by Davide De Rosa on 6/12/18.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
/// Wrapper for shared access to VPN-related objects.
public class VPN {
/// The VPN became ready to use.
public static let didPrepare = Notification.Name("VPNDidPrepare")
/// The VPN did change status.
public static let didChangeStatus = Notification.Name("VPNDidChangeStatus")
/// The VPN profile did (re)install.
public static let didReinstall = Notification.Name("VPNDidReinstall")
/// A singleton `VPNProvider` instance (default is a `MockVPNProvider`). Make sure to set this on app launch.
public static var shared: VPNProvider = MockVPNProvider()
}

View File

@ -0,0 +1,47 @@
//
// VPNConfiguration.swift
// TunnelKit
//
// Created by Davide De Rosa on 9/18/18.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import NetworkExtension
/// Generic marker for objects able to configure a `VPNProvider`.
public protocol VPNConfiguration {
}
/// A `VPNConfiguration` built on top of NetworkExtension entities.
public struct NetworkExtensionVPNConfiguration: VPNConfiguration {
/// The `NETunnelProviderProtocol` object embedding tunnel configuration.
public let protocolConfiguration: NETunnelProviderProtocol
/// The on-demand rules to establish.
public let onDemandRules: [NEOnDemandRule]
/// :nodoc:
public init(protocolConfiguration: NETunnelProviderProtocol, onDemandRules: [NEOnDemandRule]) {
self.protocolConfiguration = protocolConfiguration
self.onDemandRules = onDemandRules
}
}

View File

@ -0,0 +1,107 @@
//
// VPNProvider.swift
// TunnelKit
//
// Created by Davide De Rosa on 9/6/18.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
/// Helps controlling a VPN without messing with underlying implementations.
public protocol VPNProvider: class {
/// `true` if the VPN is ready for use.
var isPrepared: Bool { get }
/// `true` if the associated VPN profile is enabled.
var isEnabled: Bool { get }
/// The status of the VPN.
var status: VPNStatus { get }
/**
Prepares the VPN for use.
- Postcondition: The VPN is ready to use and `isPrepared` becomes `true`.
- Parameter completionHandler: The completion handler.
- Seealso: `isPrepared`
*/
func prepare(completionHandler: (() -> Void)?)
/**
Installs the VPN profile.
- Parameter configuration: The `VPNConfiguration` to install.
- Parameter completionHandler: The completion handler with an optional error.
*/
func install(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?)
/**
Connects to the VPN.
- Parameter completionHandler: The completion handler with an optional error.
*/
func connect(completionHandler: ((Error?) -> Void)?)
/**
Disconnects from the VPN.
- Parameter completionHandler: The completion handler with an optional error.
*/
func disconnect(completionHandler: ((Error?) -> Void)?)
/**
Reconnects to the VPN.
- Parameter configuration: The `VPNConfiguration` to install.
- Parameter completionHandler: The completion handler with an optional error.
*/
func reconnect(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?)
/**
Uninstalls the VPN profile.
- Parameter completionHandler: The completion handler.
*/
func uninstall(completionHandler: (() -> Void)?)
/**
Request a debug log from the VPN.
- Parameter fallback: The block resolving to a fallback `String` if no debug log is available.
- Parameter completionHandler: The completion handler with the debug log.
*/
func requestDebugLog(fallback: (() -> String)?, completionHandler: @escaping (String) -> Void)
/**
Requests the current received/sent bytes count from the VPN.
- Parameter completionHandler: The completion handler with an optional received/sent bytes count.
*/
func requestBytesCount(completionHandler: @escaping ((UInt, UInt)?) -> Void)
/**
Requests the server configuration from the VPN.
- Parameter completionHandler: The completion handler with an optional configuration object.
*/
func requestServerConfiguration(completionHandler: @escaping (Any?) -> Void)
}

View File

@ -0,0 +1,42 @@
//
// VPNStatus.swift
// TunnelKit
//
// Created by Davide De Rosa on 9/18/18.
// Copyright (c) 2020 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
/// Status of a `VPNProvider`.
public enum VPNStatus: String {
/// VPN is connected.
case connected
/// VPN is attempting a connection.
case connecting
/// VPN is disconnected.
case disconnected
/// VPN is completing a disconnection.
case disconnecting
}