mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
fix(apple): Don't crash if tunnel manager session is unexpectedly nil (#7594)
In certain weird edge cases such as: - The user removes the VPN profile while Firezone is signed in, causing the `NETunnelProviderSession` to go invalid immediately - The user approves the system extension to load before the VPN profile access is granted then the TunnelManager will not be able to obtain a valid reference to a NETunnelProviderSession object. In these cases, for now, it makes more sense to fail silently than to crash, effectively making these operations a no-op until the user remedies the VPN profile. Currently the user is prompted to re-grant VPN profile whenever its status goes to `invalid`, so these cases don't technically fail without prompting the user. Draft because it's stacked on #7593 Fixes #7579 Fixes #7591 --------- Signed-off-by: Jamil <jamilbk@users.noreply.github.com> Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
This commit is contained in:
@@ -248,7 +248,7 @@ public class TunnelManager {
|
||||
}
|
||||
|
||||
do {
|
||||
try session().startTunnel(options: options)
|
||||
try session()?.startTunnel(options: options)
|
||||
} catch {
|
||||
Log.app.error("Error starting tunnel: \(error)")
|
||||
}
|
||||
@@ -257,21 +257,21 @@ public class TunnelManager {
|
||||
func stop(clearToken: Bool = false) {
|
||||
if clearToken {
|
||||
do {
|
||||
try session().sendProviderMessage(encoder.encode(TunnelMessage.signOut)) { _ in
|
||||
self.session().stopTunnel()
|
||||
try session()?.sendProviderMessage(encoder.encode(TunnelMessage.signOut)) { _ in
|
||||
self.session()?.stopTunnel()
|
||||
}
|
||||
} catch {
|
||||
Log.app.error("\(#function): \(error)")
|
||||
}
|
||||
} else {
|
||||
session().stopTunnel()
|
||||
session()?.stopTunnel()
|
||||
}
|
||||
}
|
||||
|
||||
func updateInternetResourceState() {
|
||||
guard session().status == .connected else { return }
|
||||
guard session()?.status == .connected else { return }
|
||||
|
||||
try? session().sendProviderMessage(encoder.encode(TunnelMessage.internetResourceEnabled(internetResourceEnabled))) { _ in }
|
||||
try? session()?.sendProviderMessage(encoder.encode(TunnelMessage.internetResourceEnabled(internetResourceEnabled))) { _ in }
|
||||
}
|
||||
|
||||
func toggleInternetResource(enabled: Bool) {
|
||||
@@ -280,10 +280,10 @@ public class TunnelManager {
|
||||
}
|
||||
|
||||
func fetchResources(callback: @escaping (ResourceList) -> Void) {
|
||||
guard session().status == .connected else { return }
|
||||
guard session()?.status == .connected else { return }
|
||||
|
||||
do {
|
||||
try session().sendProviderMessage(encoder.encode(TunnelMessage.getResourceList(resourceListHash))) { data in
|
||||
try session()?.sendProviderMessage(encoder.encode(TunnelMessage.getResourceList(resourceListHash))) { data in
|
||||
if let data = data {
|
||||
self.resourceListHash = Data(SHA256.hash(data: data))
|
||||
let decoder = JSONDecoder()
|
||||
@@ -301,7 +301,7 @@ public class TunnelManager {
|
||||
func clearLogs() async throws {
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
do {
|
||||
try session().sendProviderMessage(
|
||||
try session()?.sendProviderMessage(
|
||||
encoder.encode(TunnelMessage.clearLogs)
|
||||
) { _ in continuation.resume() }
|
||||
} catch {
|
||||
@@ -313,7 +313,7 @@ public class TunnelManager {
|
||||
func getLogFolderSize() async throws -> Int64 {
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
do {
|
||||
try session().sendProviderMessage(
|
||||
try session()?.sendProviderMessage(
|
||||
encoder.encode(TunnelMessage.getLogFolderSize)
|
||||
) { data in
|
||||
|
||||
@@ -345,7 +345,7 @@ public class TunnelManager {
|
||||
|
||||
func loop() {
|
||||
do {
|
||||
try session().sendProviderMessage(
|
||||
try session()?.sendProviderMessage(
|
||||
encoder.encode(TunnelMessage.exportLogs)
|
||||
) { data in
|
||||
guard let data = data
|
||||
@@ -385,7 +385,7 @@ public class TunnelManager {
|
||||
func consumeStopReason() async throws -> String {
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
do {
|
||||
try session().sendProviderMessage(
|
||||
try session()?.sendProviderMessage(
|
||||
encoder.encode(TunnelMessage.consumeStopReason)
|
||||
) { data in
|
||||
|
||||
@@ -413,12 +413,8 @@ public class TunnelManager {
|
||||
}
|
||||
}
|
||||
|
||||
private func session() -> NETunnelProviderSession {
|
||||
guard let manager = manager,
|
||||
let session = manager.connection as? NETunnelProviderSession
|
||||
else { fatalError("Could not cast tunnel connection to NETunnelProviderSession!") }
|
||||
|
||||
return session
|
||||
private func session() -> NETunnelProviderSession? {
|
||||
return manager?.connection as? NETunnelProviderSession
|
||||
}
|
||||
|
||||
// Subscribe to system notifications about our VPN status changing
|
||||
|
||||
@@ -12,6 +12,11 @@ export default function Apple() {
|
||||
>
|
||||
{/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */}
|
||||
<Unreleased>
|
||||
<ChangeItem pull="7594">
|
||||
Fixes a race condition that could cause the app to crash in rare
|
||||
circumstances if the VPN profile is removed from system settings while
|
||||
the app is running.
|
||||
</ChangeItem>
|
||||
<ChangeItem pull="7593">
|
||||
Fixes a bug where the VPN status would not properly update upon the
|
||||
first launch of the app.
|
||||
|
||||
Reference in New Issue
Block a user