Compare commits

..

No commits in common. "b0331ed1119b5037e885618b62277f8f77be8196" and "0fde99a34f4c425befabf0d6598ad2346b79f387" have entirely different histories.

2 changed files with 111 additions and 111 deletions

View File

@ -3,13 +3,6 @@ import MobileNebula
import os.log
import SwiftyJSON
enum VPNStartError: Error {
case noManagers
case couldNotFindManager
case noTunFileDescriptor
case noProviderConfig
}
class PacketTunnelProvider: NEPacketTunnelProvider {
private var networkMonitor: NWPathMonitor?
@ -20,46 +13,61 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private var didSleep = false
private var cachedRouteDescription: String?
override func startTunnel(options: [String : NSObject]? = nil) async throws {
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
// There is currently no way to get initialization errors back to the UI via completionHandler here
// `expectStart` is sent only via the UI which means we should wait for the real start command which has another completion handler the UI can intercept
if options?["expectStart"] != nil {
// startTunnel must complete before IPC will work
// The system completion handler must be called before IPC will work
completionHandler(nil)
return
}
// VPN is being booted out of band of the UI. Use the system completion handler as there will be nothing to route initialization errors to but we still need to report
// success/fail by the presence of an error or nil
try await start()
start(completionHandler: completionHandler)
}
private func start() async throws {
var manager: NETunnelProviderManager?
private func start(completionHandler: @escaping (Error?) -> Void) {
// Load vpn configs from system, and find the manager matching the one being started
NETunnelProviderManager.loadAllFromPreferences() { managers, error in
let targetProtoConfig = self.protocolConfiguration as? NETunnelProviderProtocol;
let targetID = targetProtoConfig?.providerConfiguration!["id"] as? String;
guard let managers = managers else {
self.log.error("No managers were loaded")
return
}
for manager in managers {
var config: Data
var key: String
manager = try await self.findManager()
guard let foundManager = manager else {
throw VPNStartError.couldNotFindManager
}
let mgr = manager.protocolConfiguration as? NETunnelProviderProtocol;
let id = mgr?.providerConfiguration!["id"] as? String;
if (id == targetID) {
do {
self.site = try Site(manager: foundManager)
self.site = try Site(manager: manager)
config = try self.site!.getConfig()
} catch {
//TODO: need a way to notify the app
self.log.error("Failed to render config from vpn object")
throw error
return completionHandler(error)
}
let _site = self.site!
key = try _site.getKey()
guard let fileDescriptor = self.tunnelFileDescriptor else {
throw VPNStartError.noTunFileDescriptor
do {
key = try _site.getKey()
} catch {
return completionHandler(error)
}
let tunFD = Int(fileDescriptor)
let fileDescriptor = self.tunnelFileDescriptor
if fileDescriptor == nil {
return completionHandler("Unable to locate the tun file descriptor")
}
let tunFD = Int(fileDescriptor!)
// This is set to 127.0.0.1 because it has to be something..
let tunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
@ -68,16 +76,16 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
var err: NSError?
let ipNet = MobileNebulaParseCIDR(_site.cert!.cert.details.ips[0], &err)
if (err != nil) {
throw err!
return completionHandler(err!)
}
tunnelNetworkSettings.ipv4Settings = NEIPv4Settings(addresses: [ipNet!.ip], subnetMasks: [ipNet!.maskCIDR])
var routes: [NEIPv4Route] = [NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR)]
// Add our unsafe routes
try _site.unsafeRoutes.forEach { unsafeRoute in
_site.unsafeRoutes.forEach { unsafeRoute in
let ipNet = MobileNebulaParseCIDR(unsafeRoute.route, &err)
if (err != nil) {
throw err!
return completionHandler(err!)
}
routes.append(NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR))
}
@ -85,18 +93,31 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
tunnelNetworkSettings.ipv4Settings!.includedRoutes = routes
tunnelNetworkSettings.mtu = _site.mtu as NSNumber
try await self.setTunnelNetworkSettings(tunnelNetworkSettings)
var nebulaErr: NSError?
self.nebula = MobileNebulaNewNebula(String(data: config, encoding: .utf8), key, self.site!.logFile, tunFD, &nebulaErr)
self.setTunnelNetworkSettings(tunnelNetworkSettings, completionHandler: {(error:Error?) in
if (error != nil) {
return completionHandler(error!)
}
var err: NSError?
self.nebula = MobileNebulaNewNebula(String(data: config, encoding: .utf8), key, self.site!.logFile, tunFD, &err)
self.startNetworkMonitor()
if nebulaErr != nil {
self.log.error("We had an error starting up: \(nebulaErr, privacy: .public)")
throw nebulaErr!
if err != nil {
self.log.error("We had an error starting up: \(err, privacy: .public)")
return completionHandler(err!)
}
self.nebula!.start()
self.dnUpdater.updateSingleLoop(site: self.site!, onUpdate: self.handleDNUpdate)
completionHandler(nil)
})
// We're done looking through managers
continue
}
}
}
}
private func handleDNUpdate(newSite: Site) {
@ -115,30 +136,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
// completionHandler()
// }
private func findManager() async throws -> NETunnelProviderManager {
let targetProtoConfig = self.protocolConfiguration as? NETunnelProviderProtocol
guard let targetProviderConfig = targetProtoConfig?.providerConfiguration else {
throw VPNStartError.noProviderConfig
}
let targetID = targetProviderConfig["id"] as? String
// Load vpn configs from system, and find the manager matching the one being started
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
for manager in managers {
let mgrProtoConfig = manager.protocolConfiguration as? NETunnelProviderProtocol
guard let mgrProviderConfig = mgrProtoConfig?.providerConfiguration else {
throw VPNStartError.noProviderConfig
}
let id = mgrProviderConfig["id"] as? String
if (id == targetID) {
return manager
}
}
// If we didn't find anything, throw an error
throw VPNStartError.noManagers
}
private func startNetworkMonitor() {
networkMonitor = NWPathMonitor()
networkMonitor!.pathUpdateHandler = self.pathUpdate
@ -183,10 +180,10 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
return str.sorted().joined(separator: ", ")
}
override func handleAppMessage(_ data: Data) async -> Data? {
override func handleAppMessage(_ data: Data, completionHandler: ((Data?) -> Void)? = nil) {
guard let call = try? JSONDecoder().decode(IPCRequest.self, from: data) else {
log.error("Failed to decode IPCRequest from network extension")
return nil
return
}
var error: Error?
@ -194,22 +191,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
// start command has special treatment due to needing to call two completers
if call.command == "start" {
do {
try await self.start()
self.start() { error in
// Notify the UI if we have a completionHandler
if completionHandler != nil {
if error == nil {
// No response data, this is expected on a clean start
return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil))
} catch {
defer {
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)))
} else {
// We failed, notify and shutdown
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error!.localizedDescription))))
self.cancelTunnelWithError(error)
}
return try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error.localizedDescription)))
}
}
return
}
if nebula == nil {
// Respond with an empty success message in the event a command comes in before we've truly started
log.warning("Received command but do not have a nebula instance")
return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil))
return completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .success, message: nil)))
}
//TODO: try catch over all this
@ -225,9 +227,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
if (error != nil) {
return try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error?.localizedDescription ?? "Unknown error")))
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .error, message: JSON(error?.localizedDescription ?? "Unknown error"))))
} else {
return try? JSONEncoder().encode(IPCResponse.init(type: .success, message: data))
completionHandler!(try? JSONEncoder().encode(IPCResponse.init(type: .success, message: data)))
}
}

View File

@ -489,8 +489,6 @@ struct IncomingSite: Codable {
// Stuff our details in the protocol
let proto = manager.protocolConfiguration as? NETunnelProviderProtocol ?? NETunnelProviderProtocol()
proto.providerBundleIdentifier = "net.defined.mobileNebula.NebulaNetworkExtension";
// WARN: If we stop setting providerConfiguration["id"] here, we'll need to use something else to match
// managers in PacketTunnelProvider.findManager
proto.providerConfiguration = ["id": self.id]
proto.serverAddress = "Nebula"