Skip to content

Commit

Permalink
Refactor DNUpdater to an actor and try to fix warnings
Browse files Browse the repository at this point in the history
  • Loading branch information
jasikpark committed Feb 20, 2025
1 parent 59b566c commit 3593e8b
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 267 deletions.
60 changes: 22 additions & 38 deletions ios/NebulaNetworkExtension/PacketTunnelProvider.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import MobileNebula
import NetworkExtension
import SwiftyJSON
import os.log
import SwiftyJSON

enum VPNStartError: Error {
case noManagers
Expand All @@ -23,7 +23,7 @@ extension AppMessageError: LocalizedError {
}
}

class PacketTunnelProvider: NEPacketTunnelProvider {
final class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
private var networkMonitor: NWPathMonitor?

private var site: Site?
Expand Down Expand Up @@ -54,9 +54,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
do {
// Cannot use NETunnelProviderManager.loadAllFromPreferences() in earlier versions of iOS
// TODO: Remove else once we drop support for iOS 16
if ProcessInfo().isOperatingSystemAtLeast(
OperatingSystemVersion(majorVersion: 17, minorVersion: 0, patchVersion: 0))
{
if ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 17, minorVersion: 0, patchVersion: 0)) {
manager = try await findManager()
guard let foundManager = manager else {
throw VPNStartError.couldNotFindManager
Expand Down Expand Up @@ -91,12 +89,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
if err != nil {
throw err!
}
tunnelNetworkSettings.ipv4Settings = NEIPv4Settings(
addresses: [ipNet!.ip], subnetMasks: [ipNet!.maskCIDR]
)
var routes: [NEIPv4Route] = [
NEIPv4Route(destinationAddress: ipNet!.network, subnetMask: ipNet!.maskCIDR)
]
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
Expand All @@ -112,9 +106,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {

try await setTunnelNetworkSettings(tunnelNetworkSettings)
var nebulaErr: NSError?
nebula = MobileNebulaNewNebula(
String(data: config, encoding: .utf8), key, site!.logFile, tunFD, &nebulaErr
)
nebula = MobileNebulaNewNebula(String(data: config, encoding: .utf8), key, site!.logFile, tunFD, &nebulaErr)
startNetworkMonitor()

if nebulaErr != nil {
Expand All @@ -123,27 +115,24 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}

nebula!.start()
dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate)
await dnUpdater.updateSingleLoop(site: site!, onUpdate: handleDNUpdate)
}

private func handleDNUpdate(newSite: Site) {
do {
site = newSite
try nebula?.reload(
String(data: newSite.getConfig(), encoding: .utf8), key: newSite.getKey()
)
try nebula?.reload(String(data: newSite.getConfig(), encoding: .utf8), key: newSite.getKey())

} catch {
log.error(
"Got an error while updating nebula \(error.localizedDescription, privacy: .public)")
log.error("Got an error while updating nebula \(error.localizedDescription, privacy: .public)")
}
}

// TODO: Sleep/wake get called aggressively and do nothing to help us here, we should locate why that is and make these work appropriately
// override func sleep(completionHandler: @escaping () -> Void) {
// nebula!.sleep()
// completionHandler()
// }
// override func sleep(completionHandler: @escaping () -> Void) {
// nebula!.sleep()
// completionHandler()
// }

private func findManager() async throws -> NETunnelProviderManager {
let targetProtoConfig = protocolConfiguration as? NETunnelProviderProtocol
Expand Down Expand Up @@ -180,9 +169,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
networkMonitor = nil
}

override func stopTunnel(
with _: NEProviderStopReason, completionHandler: @escaping () -> Void
) {
override func stopTunnel(with _: NEProviderStopReason, completionHandler: @escaping () -> Void) {
nebula?.stop()
stopNetworkMonitor()
completionHandler()
Expand All @@ -193,8 +180,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
if routeDescription != cachedRouteDescription {
// Don't bother to rebind if we don't have any gateways
if routeDescription != "" {
nebula?.rebind(
"network change to: \(routeDescription); from: \(cachedRouteDescription ?? "none")")
nebula?.rebind("network change to: \(routeDescription); from: \(cachedRouteDescription ?? "none")")
}
cachedRouteDescription = routeDescription
}
Expand Down Expand Up @@ -235,8 +221,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
defer {
self.cancelTunnelWithError(error)
}
return try? JSONEncoder().encode(
IPCResponse(type: .error, message: JSON(error.localizedDescription)))
return try? JSONEncoder().encode(IPCResponse(type: .error, message: JSON(error.localizedDescription)))
}
}

Expand All @@ -258,10 +243,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}

if error != nil {
return try? JSONEncoder().encode(
IPCResponse(
type: .error, message: JSON(error?.localizedDescription ?? "Unknown error")
))
return try? JSONEncoder().encode(IPCResponse(type: .error, message: JSON(error?.localizedDescription ?? "Unknown error")))
} else {
return try? JSONEncoder().encode(IPCResponse(type: .success, message: data))
}
Expand All @@ -276,14 +258,16 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private func getHostInfo(args: JSON) -> (JSON?, (any Error)?) {
var err: NSError?
let res = nebula!.getHostInfo(
byVpnIp: args["vpnIp"].string, pending: args["pending"].boolValue, error: &err)
byVpnIp: args["vpnIp"].string, pending: args["pending"].boolValue, error: &err
)
return (JSON(res), err)
}

private func setRemoteForTunnel(args: JSON) -> (JSON?, (any Error)?) {
var err: NSError?
let res = nebula!.setRemoteForTunnel(
args["vpnIp"].string, addr: args["addr"].string, error: &err)
args["vpnIp"].string, addr: args["addr"].string, error: &err
)
return (JSON(res), err)
}

Expand All @@ -299,7 +283,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
_ = strcpy($0, "com.apple.net.utun_control")
}
}
for fd: Int32 in 0...1024 {
for fd: Int32 in 0 ... 1024 {
var addr = sockaddr_ctl()
var ret: Int32 = -1
var len = socklen_t(MemoryLayout.size(ofValue: addr))
Expand Down
11 changes: 7 additions & 4 deletions ios/NebulaNetworkExtension/Site.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ let statusString: [NEVPNStatus: String] = [
]

// Represents a site that was pulled out of the system configuration
class Site: Codable {
final class Site: Codable, @unchecked Sendable {
// Stored in manager
var name: String
var id: String
Expand Down Expand Up @@ -278,7 +278,10 @@ class Site: Codable {
}
}

if hasErrors && !managed {
if hasErrors, !managed {
errors.append("There are issues with 1 or more ca certificates")
}
if hasErrors, !managed {
errors.append("There are issues with 1 or more ca certificates")
}

Expand All @@ -294,7 +297,7 @@ class Site: Codable {
errors.append("Unable to create the site directory: \(error.localizedDescription)")
}

if managed && (try? getDNCredentials())?.invalid != false {
if managed, (try? getDNCredentials())?.invalid != false {
errors.append("Unable to fetch managed updates - please re-enroll the device")
}

Expand Down Expand Up @@ -426,7 +429,7 @@ class DNCredentials: Codable {
}

// This class represents a site coming in from flutter, meant only to be saved and re-loaded as a proper Site
struct IncomingSite: Codable {
struct IncomingSite: Codable, @unchecked Sendable {
var name: String
var id: String
var staticHostmap: [String: StaticHosts]
Expand Down
Loading

0 comments on commit 3593e8b

Please sign in to comment.