Create "Manager" subspec

- Move VPN helpers from Passepartout
- Initialize VPN.shared explicitly
- Expose internal *VPNConfiguration constructors
This commit is contained in:
Davide De Rosa 2020-06-12 23:08:25 +02:00
parent f424d4a064
commit 5807924202
8 changed files with 619 additions and 0 deletions

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

@ -0,0 +1,85 @@
//
// 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
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: .VPNDidPrepare, 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: .VPNDidChangeStatus, object: self)
completionHandler?(nil)
}
public func disconnect(completionHandler: ((Error?) -> Void)?) {
isEnabled = false
status = .disconnected
NotificationCenter.default.post(name: .VPNDidChangeStatus, object: self)
completionHandler?(nil)
}
public func reconnect(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?) {
isEnabled = true
status = .connected
NotificationCenter.default.post(name: .VPNDidChangeStatus, object: self)
completionHandler?(nil)
}
public func uninstall(completionHandler: (() -> Void)?) {
isEnabled = false
status = .disconnected
NotificationCenter.default.post(name: .VPNDidChangeStatus, 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,316 @@
//
// 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
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: .VPNDidPrepare, 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: .VPNDidChangeStatus, object: self)
}
@objc private func vpnDidReinstall(_ notification: Notification) {
NotificationCenter.default.post(name: .VPNDidReinstall, object: self)
}
}

View File

@ -0,0 +1,30 @@
//
// 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
public class VPN {
public static var shared: VPNProvider = MockVPNProvider()
}

View File

@ -0,0 +1,41 @@
//
// 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
public protocol VPNConfiguration {
}
public struct NetworkExtensionVPNConfiguration: VPNConfiguration {
public let protocolConfiguration: NETunnelProviderProtocol
public let onDemandRules: [NEOnDemandRule]
public init(protocolConfiguration: NETunnelProviderProtocol, onDemandRules: [NEOnDemandRule]) {
self.protocolConfiguration = protocolConfiguration
self.onDemandRules = onDemandRules
}
}

View File

@ -0,0 +1,60 @@
//
// 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
public protocol VPNProvider: class {
var isPrepared: Bool { get }
var isEnabled: Bool { get }
var status: VPNStatus { get }
func prepare(completionHandler: (() -> Void)?)
func install(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?)
func connect(completionHandler: ((Error?) -> Void)?)
func disconnect(completionHandler: ((Error?) -> Void)?)
func reconnect(configuration: VPNConfiguration, completionHandler: ((Error?) -> Void)?)
func uninstall(completionHandler: (() -> Void)?)
func requestDebugLog(fallback: (() -> String)?, completionHandler: @escaping (String) -> Void)
func requestBytesCount(completionHandler: @escaping ((UInt, UInt)?) -> Void)
func requestServerConfiguration(completionHandler: @escaping (Any?) -> Void)
}
public extension Notification.Name {
static let VPNDidPrepare = Notification.Name("VPNDidPrepare")
static let VPNDidChangeStatus = Notification.Name("VPNDidChangeStatus")
static let VPNDidReinstall = Notification.Name("VPNDidReinstall")
}

View File

@ -0,0 +1,36 @@
//
// 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
public enum VPNStatus {
case connected
case connecting
case disconnected
case disconnecting
}