Compare commits

...

10 commits

Author SHA1 Message Date
Caleb Jasik
043336c627
Merge 281d690ef3 into 3ec8868982 2025-07-22 13:07:23 -05:00
John Maguire
3ec8868982
Update Nebula to v1.9.6 (#286) 2025-07-18 12:15:17 -04:00
Caleb Jasik
281d690ef3
Use Swift 6 2025-03-27 12:35:12 -05:00
Caleb Jasik
a32d17705c
Use an AsyncStream to avoid sending @MainActor restricted AppDelegate into other isolation domains 2025-03-27 12:35:12 -05:00
Caleb Jasik
df4c3a51b8
Mark closures in DNUpdate as @Sendable 2025-03-27 12:35:12 -05:00
Caleb Jasik
8e9b5fcc4a
Mark PacketTunnelProvider as @unchecked Sendable 2025-03-27 12:35:12 -05:00
Caleb Jasik
f3882997be
Convert dict to Sendable string before throwing 2025-03-27 12:35:12 -05:00
Caleb Jasik
bc67c06ef7
Mark SiteUpdater as @unchecked Sendable 2025-03-27 12:35:12 -05:00
Caleb Jasik
500e49edc3
Mark Flutter import as @preconcurrency 2025-03-27 12:35:12 -05:00
Caleb Jasik
ae34c59456
Enable StrictConcurrency=complete swift feature flag
<https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md>

This causes several warnings. I'm going to start by trying to silence them by reflecting the current state of the project, rather than fixing them directly.
2025-03-27 12:35:11 -05:00
9 changed files with 49 additions and 30 deletions

View file

@ -23,7 +23,7 @@ extension AppMessageError: LocalizedError {
} }
} }
class PacketTunnelProvider: NEPacketTunnelProvider { class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
private var networkMonitor: NWPathMonitor? private var networkMonitor: NWPathMonitor?
private var site: Site? private var site: Site?

View file

@ -6,7 +6,7 @@ import os.log
let log = Logger(subsystem: "net.defined.mobileNebula", category: "Site") let log = Logger(subsystem: "net.defined.mobileNebula", category: "Site")
enum SiteError: Error { enum SiteError: Error {
case nonConforming(site: [String: Any]?) case nonConforming(site: String)
case noCertificate case noCertificate
case keyLoad case keyLoad
case keySave case keySave
@ -22,7 +22,7 @@ extension SiteError: CustomStringConvertible {
public var description: String { public var description: String {
switch self { switch self {
case .nonConforming(let site): case .nonConforming(let site):
return String("Non-conforming site \(String(describing: site))") return String("Non-conforming site \(site)")
case .noCertificate: case .noCertificate:
return "No certificate found" return "No certificate found"
case .keyLoad: case .keyLoad:
@ -150,7 +150,7 @@ let statusString: [NEVPNStatus: String] = [
] ]
// Represents a site that was pulled out of the system configuration // Represents a site that was pulled out of the system configuration
class Site: Codable { class Site: Codable, @unchecked Sendable {
// Stored in manager // Stored in manager
var name: String var name: String
var id: String var id: String
@ -208,7 +208,7 @@ class Site: Codable {
let id = dict?["id"] as? String ?? nil let id = dict?["id"] as? String ?? nil
if id == nil { if id == nil {
throw SiteError.nonConforming(site: dict) throw SiteError.nonConforming(site: String(describing: dict))
} }
try self.init(path: SiteList.getSiteConfigFile(id: id!, createDir: false)) try self.init(path: SiteList.getSiteConfigFile(id: id!, createDir: false))

View file

@ -563,6 +563,7 @@
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES; SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES; SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
@ -574,7 +575,7 @@
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES; SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES; SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES; SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
@ -784,6 +785,7 @@
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES; SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES; SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
@ -795,7 +797,7 @@
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES; SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES; SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES; SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
@ -849,6 +851,7 @@
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES; SWIFT_UPCOMING_FEATURE_CONCISE_MAGIC_FILE = YES;
SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES; SWIFT_UPCOMING_FEATURE_DEPRECATE_APPLICATION_MAIN = YES;
SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES; SWIFT_UPCOMING_FEATURE_DISABLE_OUTWARD_ACTOR_ISOLATION = YES;
@ -860,7 +863,7 @@
SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES; SWIFT_UPCOMING_FEATURE_INFER_SENDABLE_FROM_CAPTURES = YES;
SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES; SWIFT_UPCOMING_FEATURE_ISOLATED_DEFAULT_VALUES = YES;
SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES; SWIFT_UPCOMING_FEATURE_REGION_BASED_ISOLATION = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };

View file

@ -1,10 +1,10 @@
import MobileNebula @preconcurrency import MobileNebula
enum APIClientError: Error { enum APIClientError: Error {
case invalidCredentials case invalidCredentials
} }
class APIClient { struct APIClient: Sendable {
let apiClient: MobileNebulaAPIClient let apiClient: MobileNebulaAPIClient
let json = JSONDecoder() let json = JSONDecoder()

View file

@ -1,4 +1,4 @@
import Flutter @preconcurrency import Flutter
import MobileNebula import MobileNebula
import NetworkExtension import NetworkExtension
import SwiftyJSON import SwiftyJSON
@ -25,17 +25,19 @@ func MissingArgumentError(message: String, details: Any?) -> FlutterError {
) -> Bool { ) -> Bool {
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
dnUpdater.updateAllLoop { site in Task {
// Signal the site has changed in case the current site details screen is active for await site in dnUpdater.siteUpdates {
let container = self.sites?.getContainer(id: site.id) // Signal the site has changed in case the current site details screen is active
if container != nil { let container = self.sites?.getContainer(id: site.id)
// Update references to the site with the new site config if container != nil {
container!.site = site // Update references to the site with the new site config
container!.updater.update(connected: site.connected ?? false, replaceSite: site) container!.site = site
} container!.updater.update(connected: site.connected ?? false, replaceSite: site)
}
// Signal to the main screen to reload // Signal to the main screen to reload
self.ui?.invokeMethod("refreshSites", arguments: nil) self.ui?.invokeMethod("refreshSites", arguments: nil)
}
} }
guard let controller = window?.rootViewController as? FlutterViewController else { guard let controller = window?.rootViewController as? FlutterViewController else {

View file

@ -1,12 +1,12 @@
import Foundation import Foundation
import os.log import os.log
class DNUpdater { class DNUpdater: @unchecked Sendable {
private let apiClient = APIClient() private let apiClient = APIClient()
private let timer = RepeatingTimer(timeInterval: 15 * 60) // 15 * 60 is 15 minutes private let timer = RepeatingTimer(timeInterval: 15 * 60) // 15 * 60 is 15 minutes
private let log = Logger(subsystem: "net.defined.mobileNebula", category: "DNUpdater") private let log = Logger(subsystem: "net.defined.mobileNebula", category: "DNUpdater")
func updateAll(onUpdate: @escaping (Site) -> Void) { func updateAll(onUpdate: @Sendable @escaping (Site) -> Void) {
_ = SiteList { (sites, _) -> Void in _ = SiteList { (sites, _) -> Void in
// NEVPN seems to force us onto the main thread and we are about to make network calls that // NEVPN seems to force us onto the main thread and we are about to make network calls that
// could block for a while. Push ourselves onto another thread to avoid blocking the UI. // could block for a while. Push ourselves onto another thread to avoid blocking the UI.
@ -23,21 +23,33 @@ class DNUpdater {
} }
} }
func updateAllLoop(onUpdate: @escaping (Site) -> Void) { func updateAllLoop(onUpdate: @Sendable @escaping (Site) -> Void) {
timer.eventHandler = { timer.eventHandler = {
self.updateAll(onUpdate: onUpdate) self.updateAll(onUpdate: onUpdate)
} }
timer.resume() timer.resume()
} }
func updateSingleLoop(site: Site, onUpdate: @escaping (Site) -> Void) { // Site updates provides an async/await alternative to `.updateAllLoop` that doesn't require a sendable closure.
// https://developer.apple.com/documentation/swift/asyncstream
var siteUpdates: AsyncStream<Site> {
AsyncStream { continuation in
self.updateAllLoop(onUpdate: { site in
continuation.yield(site)
})
}
}
func updateSingleLoop(site: Site, onUpdate: @Sendable @escaping (Site) -> Void) {
timer.eventHandler = { timer.eventHandler = {
self.updateSite(site: site, onUpdate: onUpdate) self.updateSite(site: site, onUpdate: onUpdate)
} }
timer.resume() timer.resume()
} }
func updateSite(site: Site, onUpdate: @escaping (Site) -> Void) { func updateSite(site: Site, onUpdate: @Sendable @escaping (Site) -> Void) {
do { do {
if !site.managed { if !site.managed {
return return

View file

@ -78,14 +78,14 @@ class Sites {
} }
} }
class SiteUpdater: NSObject, FlutterStreamHandler { class SiteUpdater: NSObject, FlutterStreamHandler, @unchecked Sendable {
private var eventSink: FlutterEventSink? private var eventSink: FlutterEventSink?
private var eventChannel: FlutterEventChannel private var eventChannel: FlutterEventChannel
private var site: Site private var site: Site
private var notification: Any? private var notification: Any?
public var startFunc: (() -> Void)? public var startFunc: (() -> Void)?
private var configFd: Int32? = nil private var configFd: Int32?
private var configObserver: (any DispatchSourceFileSystemObject)? = nil private var configObserver: (any DispatchSourceFileSystemObject)?
init(messenger: any FlutterBinaryMessenger, site: Site) { init(messenger: any FlutterBinaryMessenger, site: Site) {
do { do {

View file

@ -9,7 +9,7 @@ toolchain go1.22.4
require ( require (
github.com/DefinedNet/dnapi v0.0.0-20241212205635-1d1f0084d118 github.com/DefinedNet/dnapi v0.0.0-20241212205635-1d1f0084d118
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/slackhq/nebula v1.9.5 github.com/slackhq/nebula v1.9.6
golang.org/x/crypto v0.32.0 golang.org/x/crypto v0.32.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )

View file

@ -133,6 +133,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slackhq/nebula v1.9.5 h1:ZrxcvP/lxwFglaijmiwXLuCSkybZMJnqSYI1S8DtGnY= github.com/slackhq/nebula v1.9.5 h1:ZrxcvP/lxwFglaijmiwXLuCSkybZMJnqSYI1S8DtGnY=
github.com/slackhq/nebula v1.9.5/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ= github.com/slackhq/nebula v1.9.5/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ=
github.com/slackhq/nebula v1.9.6 h1:Fl0LE2dHDeVEK3R+un59Z3V4ZzbZ6q2e/zF4ClaD5yo=
github.com/slackhq/nebula v1.9.6/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=