Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitab4af62

Browse files
committed
chore: support operating the VPN without the app
1 parentf53fadd commitab4af62

File tree

8 files changed

+113
-55
lines changed

8 files changed

+113
-55
lines changed

‎Coder Desktop/Coder Desktop/Coder_DesktopApp.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
4747
}
4848
}
4949

50+
// MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
5051
func applicationShouldTerminate(_:NSApplication)->NSApplication.TerminateReply{
5152
Task{
52-
await vpn.quit()
53+
await vpn.stop()
54+
NSApp.reply(toApplicationShouldTerminate:true)
5355
}
5456
return.terminateLater
5557
}

‎Coder Desktop/Coder Desktop/NetworkExtension.swift

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,6 @@ enum NetworkExtensionState: Equatable {
2424
/// An actor that handles configuring, enabling, and disabling the VPN tunnel via the
2525
/// NetworkExtension APIs.
2626
extensionCoderVPNService{
27-
// Updates the UI if a previous configuration exists
28-
func loadNetworkExtension()async{
29-
do{
30-
tryawaitgetTunnelManager()
31-
neState=.disabled
32-
}catch{
33-
neState=.unconfigured
34-
}
35-
}
36-
3727
func configureNetworkExtension(proto:NETunnelProviderProtocol)async{
3828
// removing the old tunnels, rather than reconfiguring ensures that configuration changes
3929
// are picked up.
@@ -74,39 +64,31 @@ extension CoderVPNService {
7464
func enableNetworkExtension()async{
7565
do{
7666
lettm=tryawaitgetTunnelManager()
77-
if !tm.isEnabled{
78-
tm.isEnabled=true
79-
tryawait tm.saveToPreferences()
80-
logger.debug("saved tunnel with enabled=true")
81-
}
8267
try tm.connection.startVPNTunnel()
8368
}catch{
84-
logger.error("enable network extension:\(error)")
69+
logger.error("start tunnel:\(error)")
8570
neState=.failed(error.localizedDescription)
8671
return
8772
}
88-
logger.debug("enabled andstarted tunnel")
73+
logger.debug("started tunnel")
8974
neState=.enabled
9075
}
9176

9277
func disableNetworkExtension()async{
9378
do{
9479
lettm=tryawaitgetTunnelManager()
9580
tm.connection.stopVPNTunnel()
96-
tm.isEnabled=false
97-
98-
tryawait tm.saveToPreferences()
9981
}catch{
100-
logger.error("disable network extension:\(error)")
82+
logger.error("stop tunnel:\(error)")
10183
neState=.failed(error.localizedDescription)
10284
return
10385
}
104-
logger.debug("saved tunnel with enabled=false")
86+
logger.debug("stopped tunnel")
10587
neState=.disabled
10688
}
10789

10890
@discardableResult
109-
privatefunc getTunnelManager()asyncthrows(VPNServiceError)->NETunnelProviderManager{
91+
func getTunnelManager()asyncthrows(VPNServiceError)->NETunnelProviderManager{
11092
vartunnels:[NETunnelProviderManager]=[]
11193
do{
11294
tunnels=tryawaitNETunnelProviderManager.loadAllFromPreferences()

‎Coder Desktop/Coder Desktop/SystemExtension.swift

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,16 @@ protocol SystemExtensionAsyncRecorder: Sendable {
2929
extensionCoderVPNService:SystemExtensionAsyncRecorder{
3030
func recordSystemExtensionState(_ state:SystemExtensionState)async{
3131
sysExtnState= state
32+
if state==.uninstalled{
33+
installSystemExtension()
34+
}
3235
if state==.installed{
36+
do{
37+
tryawaitgetTunnelManager()
38+
neState=.disabled
39+
}catch{
40+
neState=.unconfigured
41+
}
3342
// system extension was successfully installed, so we don't need the delegate any more
3443
systemExtnDelegate=nil
3544
}
@@ -64,7 +73,21 @@ extension CoderVPNService: SystemExtensionAsyncRecorder {
6473
return extensionBundle
6574
}
6675

67-
func installSystemExtension(){
76+
func checkSystemExtensionStatus(){
77+
logger.info("checking SystemExtension status")
78+
guardlet bundleID= extensionBundle.bundleIdentifierelse{
79+
logger.error("Bundle has no identifier")
80+
return
81+
}
82+
letrequest=OSSystemExtensionRequest.propertiesRequest(forExtensionWithIdentifier: bundleID, queue:.main)
83+
letdelegate=SystemExtensionDelegate(asyncDelegate:self)
84+
request.delegate= delegate
85+
systemExtnDelegate= delegate
86+
OSSystemExtensionManager.shared.submitRequest(request)
87+
logger.info("submitted SystemExtension properties request with bundleID:\(bundleID)")
88+
}
89+
90+
privatefunc installSystemExtension(){
6891
logger.info("activating SystemExtension")
6992
guardlet bundleID= extensionBundle.bundleIdentifierelse{
7093
logger.error("Bundle has no identifier")
@@ -74,11 +97,9 @@ extension CoderVPNService: SystemExtensionAsyncRecorder {
7497
forExtensionWithIdentifier: bundleID,
7598
queue:.main
7699
)
77-
letdelegate=SystemExtensionDelegate(asyncDelegate:self)
78-
systemExtnDelegate= delegate
79-
request.delegate= delegate
100+
request.delegate= systemExtnDelegate
80101
OSSystemExtensionManager.shared.submitRequest(request)
81-
logger.info("submitted SystemExtension request with bundleID:\(bundleID)")
102+
logger.info("submitted SystemExtensionactivaterequest with bundleID:\(bundleID)")
82103
}
83104
}
84105

@@ -88,6 +109,8 @@ class SystemExtensionDelegate<AsyncDelegate: SystemExtensionAsyncRecorder>:
88109
NSObject,OSSystemExtensionRequestDelegate
89110
{
90111
privatevarlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"vpn-installer")
112+
// TODO: Refactor this to use a continuation, so the result of a request can be
113+
// 'await'd for the determined state
91114
privatevarasyncDelegate:AsyncDelegate
92115

93116
init(asyncDelegate:AsyncDelegate){
@@ -138,4 +161,38 @@ class SystemExtensionDelegate<AsyncDelegate: SystemExtensionAsyncRecorder>:
138161
logger.info("Replacing\(request.identifier) v\(existing.bundleShortVersion) with v\(`extension`.bundleShortVersion)")
139162
return.replace
140163
}
164+
165+
publicfunc request(
166+
_:OSSystemExtensionRequest,
167+
foundProperties properties:[OSSystemExtensionProperties]
168+
){
169+
// In debug builds we always replace the SE to test
170+
// changes made without bumping the version
171+
#if DEBUG
172+
Task{[asyncDelegate]in
173+
await asyncDelegate.recordSystemExtensionState(.uninstalled)
174+
}
175+
return
176+
#else
177+
letversion=Bundle.main.object(forInfoDictionaryKey:"CFBundleVersion")as?String
178+
letshortVersion=Bundle.main.object(forInfoDictionaryKey:"CFBundleShortVersionString")as?String
179+
180+
letversionMatches= properties.contains{ sysexin
181+
sysex.isEnabled
182+
&& sysex.bundleVersion== version
183+
&& sysex.bundleShortVersion== shortVersion
184+
}
185+
if versionMatches{
186+
Task{[asyncDelegate]in
187+
await asyncDelegate.recordSystemExtensionState(.installed)
188+
}
189+
return
190+
}
191+
192+
// Either uninstalled or needs replacing
193+
Task{[asyncDelegate]in
194+
await asyncDelegate.recordSystemExtensionState(.uninstalled)
195+
}
196+
#endif
197+
}
141198
}

‎Coder Desktop/Coder Desktop/VPNService.swift

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ enum VPNServiceError: Error, Equatable {
4141
finalclassCoderVPNService:NSObject,VPNService{
4242
varlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"vpn")
4343
lazyvarxpc:VPNXPCInterface=.init(vpn:self)
44-
varterminating=false
4544
varworkspaces:[UUID:String]=[:]
4645

4746
@PublishedvartunnelState:VPNServiceState=.disabled
@@ -66,10 +65,7 @@ final class CoderVPNService: NSObject, VPNService {
6665

6766
overrideinit(){
6867
super.init()
69-
installSystemExtension()
70-
Task{
71-
awaitloadNetworkExtension()
72-
}
68+
checkSystemExtensionStatus()
7369
NotificationCenter.default.addObserver(
7470
self,
7571
selector: #selector(vpnDidUpdate(_:)),
@@ -82,6 +78,11 @@ final class CoderVPNService: NSObject, VPNService {
8278
NotificationCenter.default.removeObserver(self)
8379
}
8480

81+
func clearPeers(){
82+
agents=[:]
83+
workspaces=[:]
84+
}
85+
8586
func start()async{
8687
switch tunnelState{
8788
case.disabled,.failed:
@@ -102,19 +103,6 @@ final class CoderVPNService: NSObject, VPNService {
102103
logger.info("network extension stopped")
103104
}
104105

105-
// Instructs the service to stop the VPN and then quit once the stop event
106-
// is read over XPC.
107-
// MUST only be called from `NSApplicationDelegate.applicationShouldTerminate`
108-
// MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
109-
func quit()async{
110-
guard tunnelState==.connectedelse{
111-
NSApp.reply(toApplicationShouldTerminate:true)
112-
return
113-
}
114-
terminating=true
115-
awaitstop()
116-
}
117-
118106
func configureTunnelProviderProtocol(proto:NETunnelProviderProtocol?){
119107
Task{
120108
iflet proto{
@@ -145,6 +133,22 @@ final class CoderVPNService: NSObject, VPNService {
145133
}
146134
}
147135

136+
func onExtensionPeerState(_ data:Data?){
137+
logger.info("network extension peer state")
138+
guardlet dataelse{
139+
logger.error("could not retrieve peer state from network extension")
140+
return
141+
}
142+
do{
143+
letmsg=tryVpn_PeerUpdate(serializedBytes: data)
144+
debugPrint(msg)
145+
clearPeers()
146+
applyPeerUpdate(with: msg)
147+
}catch{
148+
logger.error("failed to decode peer update\(error)")
149+
}
150+
}
151+
148152
func applyPeerUpdate(with update:Vpn_PeerUpdate){
149153
// Delete agents
150154
update.deletedAgents
@@ -204,9 +208,6 @@ extension CoderVPNService {
204208
}
205209
switch connection.status{
206210
case.disconnected:
207-
if terminating{
208-
NSApp.reply(toApplicationShouldTerminate:true)
209-
}
210211
connection.fetchLastDisconnectError{ errin
211212
self.tunnelState=iflet err{
212213
.failed(.internalError(err.localizedDescription))
@@ -217,6 +218,11 @@ extension CoderVPNService {
217218
case.connecting:
218219
tunnelState=.connecting
219220
case.connected:
221+
// If we moved from disabled to connected, then the NE was already
222+
// running, and we need to request the current peer state
223+
ifself.tunnelState==.disabled{
224+
xpc.getPeerState()
225+
}
220226
tunnelState=.connected
221227
case.reasserting:
222228
tunnelState=.connecting

‎Coder Desktop/Coder Desktop/XPCInterface.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ import VPNLib
4545
}
4646
}
4747

48+
func getPeerState(){
49+
xpc.getPeerState{ datain
50+
Task{@MainActorin
51+
self.svc.onExtensionPeerState(data)
52+
}
53+
}
54+
}
55+
4856
func onPeerUpdate(_ data:Data){
4957
Task{@MainActorin
5058
svc.onExtensionPeerUpdate(data)

‎Coder Desktop/VPN/Manager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ actor Manager {
194194

195195
// Retrieves the current state of all peers,
196196
// as required when starting the app whilst the network extension is already running
197-
funcgetPeerInfo()asyncthrows(ManagerError)->Vpn_PeerUpdate{
197+
funcgetPeerState()asyncthrows(ManagerError)->Vpn_PeerUpdate{
198198
logger.info("sending peer state request")
199199
letresp:Vpn_TunnelMessage
200200
do{

‎Coder Desktop/VPN/XPCInterface.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ import VPNLib
2020
}
2121
}
2222

23-
func getPeerInfo(with reply:@escaping()->Void){
24-
// TODO: Retrieve from Manager
25-
reply()
23+
func getPeerState(with reply:@escaping(Data?)->Void){
24+
letreply=CallbackWrapper(reply)
25+
Task{
26+
letdata=try?await manager?.getPeerState().serializedData()
27+
reply(data)
28+
}
2629
}
2730

2831
func ping(with reply:@escaping()->Void){

‎Coder Desktop/VPNLib/XPC.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Foundation
22

33
@preconcurrency
44
@objcpublicprotocolVPNXPCProtocol{
5-
funcgetPeerInfo(with reply:@escaping()->Void)
5+
funcgetPeerState(with reply:@escaping(Data?)->Void)
66
func ping(with reply:@escaping()->Void)
77
}
88

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp