Skip to content

Commit 55633ff

Browse files
authored
Merge pull request #476 from Sovereign-Engineering/includeAllNetworks2
Add option to use `includeAllNetworks`.
2 parents e7516df + 004fca8 commit 55633ff

File tree

8 files changed

+121
-9
lines changed

8 files changed

+121
-9
lines changed

apple/client/OsStatus.swift

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,29 @@ class OsStatus: Encodable {
1010
var internetAvailable: Bool = false
1111
var osVpnStatus: NEVPNStatus
1212
let srcVersion = sourceVersion()
13+
var strictLeakPrevention: Bool
1314
var updaterStatus = UpdaterStatus()
1415
var debugBundleStatus = DebugBundleStatus()
1516

16-
init(osVpnStatus: NEVPNStatus) {
17+
init(
18+
strictLeakPrevention: Bool,
19+
osVpnStatus: NEVPNStatus,
20+
) {
1721
self.osVpnStatus = osVpnStatus
22+
self.strictLeakPrevention = strictLeakPrevention
1823
}
1924

20-
static func watchable(connection: NEVPNConnection) -> WatchableValue<OsStatus> {
21-
let notifications = NotificationCenter.default.notifications(named: .NEVPNStatusDidChange, object: connection)
22-
let w = WatchableValue(OsStatus(osVpnStatus: connection.status))
25+
static func watchable(
26+
manager: NEVPNManager,
27+
) -> WatchableValue<OsStatus> {
28+
var lastIncludeAllNetworks = switch manager.protocolConfiguration {
29+
case let .some(proto): proto.includeAllNetworks
30+
case nil: false // Report safe default.
31+
}
32+
let w = WatchableValue(OsStatus(
33+
strictLeakPrevention: lastIncludeAllNetworks,
34+
osVpnStatus: manager.connection.status
35+
))
2336
Task {
2437
for await path in NWPathMonitor().stream() {
2538
logger.info("NWPathMonitor event: \(path.debugDescription, privacy: .public)")
@@ -29,16 +42,44 @@ class OsStatus: Encodable {
2942
}
3043
}
3144
}
45+
46+
let vpnConfigNotifications = NotificationCenter.default.notifications(named: .NEVPNConfigurationChange, object: manager)
3247
Task {
33-
for await _ in notifications {
34-
let osVpnStatus = connection.status
48+
for await _ in vpnConfigNotifications {
49+
let includeAllNetworks: Bool
50+
if let proto = manager.protocolConfiguration {
51+
includeAllNetworks = proto.includeAllNetworks
52+
} else {
53+
logger.warning("NEVPNManager.protocolConfiguration is nil")
54+
includeAllNetworks = false // Safe default
55+
}
56+
57+
logger.info("NEVPNConfigurationChangeNotification includeAllNetworks \(includeAllNetworks, privacy: .public)")
58+
59+
if includeAllNetworks == lastIncludeAllNetworks {
60+
continue
61+
}
62+
63+
lastIncludeAllNetworks = includeAllNetworks
64+
_ = w.update { value in
65+
value.strictLeakPrevention = includeAllNetworks
66+
value.version = UUID()
67+
}
68+
}
69+
}
70+
71+
let vpnStatusNotifications = NotificationCenter.default.notifications(named: .NEVPNStatusDidChange, object: manager.connection)
72+
Task {
73+
for await _ in vpnStatusNotifications {
74+
let osVpnStatus = manager.connection.status
3575
logger.info("NEVPNStatus event: \(osVpnStatus, privacy: .public)")
3676
_ = w.update { value in
3777
value.osVpnStatus = osVpnStatus
3878
value.version = UUID()
3979
}
4080
}
4181
}
82+
4283
return w
4384
}
4485
}

apple/client/TunnelProvider.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class TunnelProviderInit {
7777
let proto = NETunnelProviderProtocol()
7878
proto.providerBundleIdentifier = extensionBundleID()
7979
proto.serverAddress = "obscura.net"
80+
proto.includeAllNetworks = manager.protocolConfiguration?.includeAllNetworks ?? false
8081
manager.protocolConfiguration = proto
8182
manager.isEnabled = true
8283

apple/client/app_state.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class AppState: ObservableObject {
1616
) {
1717
self.manager = manager
1818
self.status = initialStatus
19-
self.osStatus = OsStatus.watchable(connection: manager.connection)
19+
self.osStatus = OsStatus.watchable(manager: manager)
2020

2121
Task { @MainActor in
2222
var version: UUID = initialStatus.version
@@ -63,6 +63,36 @@ class AppState: ObservableObject {
6363
}
6464
}
6565

66+
func setIncludeAllNetworks(enable: Bool) async throws {
67+
guard let proto = self.manager.protocolConfiguration else {
68+
throw "NEVPNManager.protocolConfiguration is nil"
69+
}
70+
71+
Self.logger.info("setIncludeAllNetworks \(proto.includeAllNetworks, privacy: .public)\(enable, privacy: .public)")
72+
73+
if proto.includeAllNetworks == enable { return }
74+
75+
proto.includeAllNetworks = enable
76+
do {
77+
try await self.manager.saveToPreferences()
78+
return
79+
} catch {
80+
Self.logger.error("Failed to save NEVPNManager: \(error.localizedDescription)")
81+
}
82+
83+
do {
84+
try await self.manager.loadFromPreferences()
85+
return
86+
} catch {
87+
Self.logger.error("Failed to reload NEVPNManager: \(error.localizedDescription)")
88+
}
89+
90+
proto.includeAllNetworks = false
91+
Self.logger.warning("Marking local includeAllNetworks to false as a safe default.")
92+
93+
throw "Unable to save VPN configuration."
94+
}
95+
6696
func enableTunnel(_ tunnelArgs: TunnelArgs) async throws(String) {
6797
for _ in 1 ..< 3 {
6898
// Checking the status to decide whether to use `startVPNTunnel` or the `setTunnelArgs` command is not necessary for correct behavior. Handling of the `errorCodeTunnelInactive` error code is sufficient to always do the right thing eventually. However, this does require an app message round-trip to the NE, which can be a little slow at times.

apple/client/command.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ enum Command: Codable {
1010
case stopTunnel
1111
case debuggingArchive
1212
case revealItemInDir(path: String)
13+
case setStrictLeakPrevention(enable: Bool)
1314
case registerAsLoginItem
1415
case unregisterAsLoginItem
1516
case isRegisteredAsLoginItem
@@ -55,6 +56,13 @@ func handleWebViewCommand(command: Command) async throws(String) -> String {
5556
case .resetUserDefaults:
5657
// NOTE: only shown in the Developer View
5758
appState.resetUserDefaults()
59+
case .setStrictLeakPrevention(let enable):
60+
do {
61+
try await appState.setIncludeAllNetworks(enable: enable)
62+
} catch {
63+
logger.error("Could not set includeAllNetworks \(error, privacy: .public)")
64+
throw errorCodeOther
65+
}
5866
case .jsonFfiCmd(cmd: let jsonCmd, let timeoutMs):
5967
let attemptTimeout: Duration? = switch timeoutMs {
6068
case .some(let ms): .milliseconds(ms)

obscura-ui/src/bridge/commands.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ export function logout() {
7373
return jsonFfiCmd('logout');
7474
}
7575

76+
export async function setStrictLeakPrevention(enable: boolean): Promise<void> {
77+
await invoke('setStrictLeakPrevention', { enable });
78+
}
79+
7680
export function connect(exit: string | null = null) {
7781
let jsonTunnelArgs = JSON.stringify(({ exit }));
7882
return invoke('startTunnel', { tunnelArgs: jsonTunnelArgs });

obscura-ui/src/common/appContext.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface OsStatus {
3838
internetAvailable: boolean,
3939
osVpnStatus: NEVPNStatus,
4040
srcVersion: string
41+
strictLeakPrevention: boolean,
4142
updaterStatus: UpdaterStatus,
4243
debugBundleStatus: {
4344
inProgress?: boolean,

obscura-ui/src/common/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ export function percentEncodeQuery(params: Record<string, string>) {
110110

111111
const DEFAULT_ERROR_MSG = "An unexpected error has occurred.";
112112

113+
export function errMsg(error: unknown): string {
114+
if (error instanceof Error) {
115+
return error.message;
116+
}
117+
console.warn(fmt`errMsg: error = ${error} is not an instance of Error`);
118+
return DEFAULT_ERROR_MSG;
119+
}
120+
113121
export function normalizeError(error: unknown): Error {
114122
if (error instanceof Error) {
115123
return error;

obscura-ui/src/views/DeveloperView.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Accordion, Autocomplete, Button, Group, JsonInput, Stack, Text, TextInput, Title } from '@mantine/core';
1+
import { Accordion, Autocomplete, Button, Group, JsonInput, Stack, Switch, Text, TextInput, Title } from '@mantine/core';
22
import { useInterval } from '@mantine/hooks';
33
import { notifications } from '@mantine/notifications';
44
import Cookies from 'js-cookie';
@@ -8,7 +8,7 @@ import * as commands from '../bridge/commands';
88
import { Exit } from '../common/api';
99
import { AppContext } from '../common/appContext';
1010
import { localStorageGet, LocalStorageKey } from '../common/localStorage';
11-
import { IS_WK_WEB_VIEW } from '../common/utils';
11+
import { errMsg, IS_WK_WEB_VIEW } from '../common/utils';
1212
import DevSendCommand from '../components/DevSendCommand';
1313
import DevSetApiUrl from '../components/DevSetApiUrl';
1414

@@ -36,6 +36,9 @@ export default function DeveloperViewer() {
3636

3737
const [accordionValues, setAccordionValues] = useState<string[]>([]);
3838

39+
const [strictLeakLoading, setStrictLeakLoading] = useState(false);
40+
const [strictLeakError, setStrictLeakError] = useState<string>();
41+
3942
return <Stack p={20} mb={50}>
4043
<Title order={3}>Developer View</Title>
4144
<Title order={4}>Statuses</Title>
@@ -78,6 +81,22 @@ export default function DeveloperViewer() {
7881
<Autocomplete onChange={setLocalStorageKey} label='local storage key' data={Object.values(LocalStorageKey)} />
7982
<Button onClick={() => setLocalStorageValue(localStorageGet(localStorageKey as LocalStorageKey))}>Get</Button>
8083
</Group>
84+
<Switch
85+
error={strictLeakError}
86+
checked={osStatus.strictLeakPrevention}
87+
disabled={strictLeakLoading}
88+
onChange={async event => {
89+
try {
90+
setStrictLeakLoading(true);
91+
setStrictLeakError(undefined);
92+
await commands.setStrictLeakPrevention(event.currentTarget.checked);
93+
} catch (err) {
94+
setStrictLeakError(errMsg(err));
95+
} finally {
96+
setStrictLeakLoading(false);
97+
}
98+
}}
99+
label="Enable Strict Leak Prevention" />
81100
<JsonInput value={localStorageValue ?? 'null'} contentEditable={false} />
82101
</Stack>;
83102
}

0 commit comments

Comments
 (0)