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

Commit8533b31

Browse files
chore: run coder connect networking from launchdaemon (#203)
Continues to address#201.This PR reworks all XPC connections, such that the networking code runs within the privileged helper, instead of the network extension.The XPC interfaces are described in `XPC.swift`, and roughly follow this sequence diagram: (One difference is that we don't posix spawn the tunnel in this PR)```mermaidsequenceDiagram note left of App: User requests to start VPN: App->>+NetExt: Start VPN NetExt->>+PrivHelper: Request start VPN with TUN FD note right of PrivHelper: Privileged helper downloads and verifies binary. PrivHelper->>Tunnel: posix_spawn child process with FDs PrivHelper->>+Tunnel: Send proto start request Tunnel-->>-PrivHelper: Send proto start response PrivHelper->>+NetExt: Request for network config change NetExt-->>-PrivHelper: Response for network config change PrivHelper-->>-NetExt: Start VPN respons NetExt-->>-App: VPN started App->>PrivHelper: Request peer state PrivHelper->>Tunnel: Request peer state Tunnel-->>PrivHelper: Peer state response PrivHelper-->>App: Peer state response note left of App: Tunnel updates (bypass NetExt): Tunnel->>PrivHelper: Tunnel update proto message PrivHelper->>App: Tunnel update proto message note left of App: User requests to stop VPN: App->>+NetExt: Stop VPN NetExt->>+PrivHelper: Request stop VPN PrivHelper->>+Tunnel: Request stop VPN Tunnel-->>-PrivHelper: Stop VPN response note right of Tunnel: Tunnel binary exits PrivHelper-->>-NetExt: Stop VPN response NetExt-->>-App: VPN stopped```Of note is that the network extension starts and stops the daemon running within the privileged helper.This is to support starting and stopping the VPN from the toggle in System Settings, and to ensure the "Connecting" and "Disconnecting" phase of the system VPN is indicative of the time the VPN is actually setting itself up and tearing itself down.To accomplish this, the privileged helper listens on two different service names. One is connected to by the app, the other the network extension. (Once an XPC listener is connected to, communication is bidirectional)
1 parent99c912b commit8533b31

20 files changed

+527
-486
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import Foundation
2+
import NetworkExtension
3+
import os
4+
import VPNLib
5+
6+
// This is the client for the app to communicate with the privileged helper.
7+
@objcfinalclassHelperXPCClient:NSObject,@uncheckedSendable{
8+
privatevarsvc:CoderVPNService
9+
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"HelperXPCClient")
10+
privatevarconnection:NSXPCConnection?
11+
12+
init(vpn:CoderVPNService){
13+
svc= vpn
14+
super.init()
15+
}
16+
17+
func connect()->NSXPCConnection{
18+
iflet connection{
19+
return connection
20+
}
21+
22+
letconnection=NSXPCConnection(
23+
machServiceName: helperAppMachServiceName,
24+
options:.privileged
25+
)
26+
connection.remoteObjectInterface=NSXPCInterface(with:HelperAppXPCInterface.self)
27+
connection.exportedInterface=NSXPCInterface(with:AppXPCInterface.self)
28+
connection.exportedObject=self
29+
connection.invalidationHandler={
30+
self.logger.error("XPC connection invalidated")
31+
self.connection=nil
32+
_=self.connect()
33+
}
34+
connection.interruptionHandler={
35+
self.logger.error("XPC connection interrupted")
36+
self.connection=nil
37+
_=self.connect()
38+
}
39+
logger.info("connecting to\(helperAppMachServiceName)")
40+
connection.resume()
41+
self.connection= connection
42+
return connection
43+
}
44+
45+
// Establishes a connection to the Helper, so it can send messages back.
46+
func ping()asyncthrows{
47+
letconn=connect()
48+
returntryawaitwithCheckedThrowingContinuation{ continuationin
49+
guardlet proxy= conn.remoteObjectProxyWithErrorHandler({ errin
50+
self.logger.error("failed to connect to HelperXPC\(err.localizedDescription, privacy:.public)")
51+
continuation.resume(throwing: err)
52+
})as?HelperAppXPCInterfaceelse{
53+
self.logger.error("failed to get proxy for HelperXPC")
54+
continuation.resume(throwing:XPCError.wrongProxyType)
55+
return
56+
}
57+
proxy.ping{
58+
self.logger.info("Connected to Helper over XPC")
59+
continuation.resume()
60+
}
61+
}
62+
}
63+
64+
func getPeerState()asyncthrows{
65+
letconn=connect()
66+
returntryawaitwithCheckedThrowingContinuation{ continuationin
67+
guardlet proxy= conn.remoteObjectProxyWithErrorHandler({ errin
68+
self.logger.error("failed to connect to HelperXPC\(err.localizedDescription, privacy:.public)")
69+
continuation.resume(throwing: err)
70+
})as?HelperAppXPCInterfaceelse{
71+
self.logger.error("failed to get proxy for HelperXPC")
72+
continuation.resume(throwing:XPCError.wrongProxyType)
73+
return
74+
}
75+
proxy.getPeerState{ datain
76+
Task{@MainActorin
77+
self.svc.onExtensionPeerState(data)
78+
}
79+
continuation.resume()
80+
}
81+
}
82+
}
83+
}
84+
85+
// These methods are called by the Helper over XPC
86+
extensionHelperXPCClient:AppXPCInterface{
87+
func onPeerUpdate(_ diff:Data, reply:@escaping()->Void){
88+
letreply=CompletionWrapper(reply)
89+
Task{@MainActorin
90+
svc.onExtensionPeerUpdate(diff)
91+
reply()
92+
}
93+
}
94+
95+
func onProgress(stage:ProgressStage, downloadProgress:DownloadProgress?, reply:@escaping()->Void){
96+
letreply=CompletionWrapper(reply)
97+
Task{@MainActorin
98+
svc.onProgress(stage: stage, downloadProgress: downloadProgress)
99+
reply()
100+
}
101+
}
102+
}

‎Coder-Desktop/Coder-Desktop/VPN/VPNService.swift‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ enum VPNServiceError: Error, Equatable {
5454
@MainActor
5555
finalclassCoderVPNService:NSObject,VPNService{
5656
varlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"vpn")
57-
lazyvarxpc:VPNXPCInterface=.init(vpn:self)
57+
lazyvarxpc:HelperXPCClient=.init(vpn:self)
5858

5959
@PublishedvartunnelState:VPNServiceState=.disabled{
6060
didSet{
@@ -138,10 +138,10 @@ final class CoderVPNService: NSObject, VPNService {
138138
}
139139
}
140140

141-
func onExtensionPeerUpdate(_data:Data){
141+
func onExtensionPeerUpdate(_diff:Data){
142142
logger.info("network extension peer update")
143143
do{
144-
letmsg=tryVpn_PeerUpdate(serializedBytes:data)
144+
letmsg=tryVpn_PeerUpdate(serializedBytes:diff)
145145
debugPrint(msg)
146146
applyPeerUpdate(with: msg)
147147
}catch{
@@ -199,16 +199,18 @@ extension CoderVPNService {
199199
break
200200
// Non-connecting -> Connecting: Establish XPC
201201
case(_,.connecting):
202-
xpc.connect()
203-
xpc.ping()
202+
// Detached to run ASAP
203+
// TODO: Switch to `Task.immediate` once stable
204+
Task.detached{try?awaitself.xpc.ping()}
204205
tunnelState=.connecting
205206
// Non-connected -> Connected:
206207
// - Retrieve Peers
207208
// - Run `onStart` closure
208209
case(_,.connected):
209210
onStart?()
210-
xpc.connect()
211-
xpc.getPeerState()
211+
// Detached to run ASAP
212+
// TODO: Switch to `Task.immediate` once stable
213+
Task.detached{try?awaitself.xpc.getPeerState()}
212214
tunnelState=.connected
213215
// Any -> Reasserting
214216
case(_,.reasserting):

‎Coder-Desktop/Coder-Desktop/Views/LoginForm.swift‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ struct LoginForm: View {
9090
return
9191
}
9292
// x.compare(y) is .orderedDescending if x > y
93-
guardSignatureValidator.minimumCoderVersion.compare(semver, options:.numeric)!=.orderedDescendingelse{
93+
guardValidator.minimumCoderVersion.compare(semver, options:.numeric)!=.orderedDescendingelse{
9494
loginError=.outdatedCoderVersion
9595
return
9696
}
@@ -192,13 +192,13 @@ struct LoginForm: View {
192192
@discardableResult
193193
func validateURL(_ url:String)throws(LoginError)->URL{
194194
guardlet url=URL(string: url)else{
195-
throwLoginError.invalidURL
195+
throw.invalidURL
196196
}
197197
guard url.scheme=="https"else{
198-
throwLoginError.httpsRequired
198+
throw.httpsRequired
199199
}
200200
guard url.host!=nilelse{
201-
throwLoginError.noHost
201+
throw.noHost
202202
}
203203
return url
204204
}
@@ -221,7 +221,7 @@ enum LoginError: Error {
221221
"Invalid URL"
222222
case.outdatedCoderVersion:
223223
"""
224-
The Coder deployment must be version\(SignatureValidator.minimumCoderVersion)
224+
The Coder deployment must be version\(Validator.minimumCoderVersion)
225225
or higher to use Coder Desktop.
226226
"""
227227
caselet.failedAuth(err):

‎Coder-Desktop/Coder-Desktop/XPCInterface.swift‎

Lines changed: 0 additions & 114 deletions
This file was deleted.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp