@@ -3,112 +3,98 @@ import NetworkExtension
3
3
import os
4
4
import VPNLib
5
5
6
- @objc final class VPNXPCInterface : NSObject , VPNXPCClientCallbackProtocol , @unchecked Sendable {
6
+ @objc final class AppXPCListener : NSObject , AppXPCInterface , @unchecked Sendable {
7
7
private var svc : CoderVPNService
8
- private let logger = Logger ( subsystem: Bundle . main. bundleIdentifier!, category: " VPNXPCInterface " )
9
- private var xpc : VPNXPCProtocol ?
8
+ private let logger = Logger ( subsystem: Bundle . main. bundleIdentifier!, category: " AppXPCListener " )
9
+ private var connection : NSXPCConnection ?
10
10
11
11
init ( vpn: CoderVPNService ) {
12
12
svc = vpn
13
13
super. init ( )
14
14
}
15
15
16
- func connect( ) {
17
- logger. debug ( " VPN xpc connect called " )
18
- guard xpc == nil else {
19
- logger. debug ( " VPN xpc already exists " )
20
- return
16
+ func connect( ) -> NSXPCConnection {
17
+ if let connection {
18
+ return connection
21
19
}
22
- let networkExtDict = Bundle . main. object ( forInfoDictionaryKey: " NetworkExtension " ) as? [ String : Any ]
23
- let machServiceName = networkExtDict ? [ " NEMachServiceName " ] as? String
24
- let xpcConn = NSXPCConnection ( machServiceName: machServiceName!)
25
- xpcConn. remoteObjectInterface = NSXPCInterface ( with: VPNXPCProtocol . self)
26
- xpcConn. exportedInterface = NSXPCInterface ( with: VPNXPCClientCallbackProtocol . self)
27
- guard let proxy = xpcConn. remoteObjectProxy as? VPNXPCProtocol else {
28
- fatalError ( " invalid xpc cast " )
29
- }
30
- xpc = proxy
31
-
32
- logger. debug ( " connecting to machServiceName: \( machServiceName!) " )
33
20
34
- xpcConn. exportedObject = self
35
- xpcConn. invalidationHandler = { [ logger] in
36
- Task { @MainActor in
37
- logger. error ( " VPN XPC connection invalidated. " )
38
- self . xpc = nil
39
- self . connect ( )
40
- }
41
- }
42
- xpcConn. interruptionHandler = { [ logger] in
43
- Task { @MainActor in
44
- logger. error ( " VPN XPC connection interrupted. " )
45
- self . xpc = nil
46
- self . connect ( )
47
- }
21
+ let connection = NSXPCConnection (
22
+ machServiceName: helperAppMachServiceName,
23
+ options: . privileged
24
+ )
25
+ connection. remoteObjectInterface = NSXPCInterface ( with: HelperAppXPCInterface . self)
26
+ connection. exportedInterface = NSXPCInterface ( with: AppXPCInterface . self)
27
+ connection. exportedObject = self
28
+ connection. invalidationHandler = {
29
+ self . logger. error ( " XPC connection invalidated " )
30
+ self . connection = nil
31
+ _ = self . connect ( )
48
32
}
49
- xpcConn. resume ( )
50
- }
51
-
52
- func ping( ) {
53
- xpc? . ping {
54
- Task { @MainActor in
55
- self . logger. info ( " Connected to NE over XPC " )
56
- }
33
+ connection. interruptionHandler = {
34
+ self . logger. error ( " XPC connection interrupted " )
35
+ self . connection = nil
36
+ _ = self . connect ( )
57
37
}
38
+ logger. info ( " connecting to \( helperAppMachServiceName) " )
39
+ connection. resume ( )
40
+ self . connection = connection
41
+ return connection
58
42
}
59
43
60
- func getPeerState ( ) {
61
- xpc ? . getPeerState { data in
62
- Task { @MainActor in
63
- self . svc. onExtensionPeerState ( data )
64
- }
44
+ func onPeerUpdate ( _ diff : Data , reply : @escaping ( ) -> Void ) {
45
+ let reply = CompletionWrapper ( reply )
46
+ Task { @MainActor in
47
+ svc. onExtensionPeerUpdate ( diff )
48
+ reply ( )
65
49
}
66
50
}
67
51
68
- func onPeerUpdate( _ data: Data ) {
52
+ func onProgress( stage: ProgressStage , downloadProgress: DownloadProgress ? , reply: @escaping ( ) -> Void ) {
53
+ let reply = CompletionWrapper ( reply)
69
54
Task { @MainActor in
70
- svc. onExtensionPeerUpdate ( data)
55
+ svc. onProgress ( stage: stage, downloadProgress: downloadProgress)
56
+ reply ( )
71
57
}
72
58
}
59
+ }
73
60
74
- func onProgress( stage: ProgressStage , downloadProgress: DownloadProgress ? ) {
75
- Task { @MainActor in
76
- svc. onProgress ( stage: stage, downloadProgress: downloadProgress)
61
+ // These methods are called to request updatess from the Helper.
62
+ extension AppXPCListener {
63
+ func ping( ) async throws {
64
+ let conn = connect ( )
65
+ return try await withCheckedThrowingContinuation { continuation in
66
+ guard let proxy = conn. remoteObjectProxyWithErrorHandler ( { err in
67
+ self . logger. error ( " failed to connect to HelperXPC \( err. localizedDescription, privacy: . public) " )
68
+ continuation. resume ( throwing: err)
69
+ } ) as? HelperAppXPCInterface else {
70
+ self . logger. error ( " failed to get proxy for HelperXPC " )
71
+ continuation. resume ( throwing: XPCError . wrongProxyType)
72
+ return
73
+ }
74
+ proxy. ping {
75
+ self . logger. info ( " Connected to Helper over XPC " )
76
+ continuation. resume ( )
77
+ }
77
78
}
78
79
}
79
80
80
- // The NE has verified the dylib and knows better than Gatekeeper
81
- func removeQuarantine( path: String , reply: @escaping ( Bool ) -> Void ) {
82
- let reply = CallbackWrapper ( reply)
83
- Task { @MainActor in
84
- let prompt = """
85
- Coder Desktop wants to execute code downloaded from \
86
- \( svc. serverAddress ?? " the Coder deployment " ) . The code has been \
87
- verified to be signed by Coder.
88
- """
89
- let source = """
90
- do shell script " xattr -d com.apple.quarantine \( path) " \
91
- with prompt " \( prompt) " \
92
- with administrator privileges
93
- """
94
- let success = await withCheckedContinuation { continuation in
95
- guard let script = NSAppleScript ( source: source) else {
96
- continuation. resume ( returning: false )
97
- return
98
- }
99
- // Run on a background thread
100
- Task . detached {
101
- var error : NSDictionary ?
102
- script. executeAndReturnError ( & error)
103
- if let error {
104
- self . logger. error ( " AppleScript error: \( error) " )
105
- continuation. resume ( returning: false )
106
- } else {
107
- continuation. resume ( returning: true )
108
- }
81
+ func getPeerState( ) async throws {
82
+ let conn = connect ( )
83
+ return try await withCheckedThrowingContinuation { continuation in
84
+ guard let proxy = conn. remoteObjectProxyWithErrorHandler ( { err in
85
+ self . logger. error ( " failed to connect to HelperXPC \( err. localizedDescription, privacy: . public) " )
86
+ continuation. resume ( throwing: err)
87
+ } ) as? HelperAppXPCInterface else {
88
+ self . logger. error ( " failed to get proxy for HelperXPC " )
89
+ continuation. resume ( throwing: XPCError . wrongProxyType)
90
+ return
91
+ }
92
+ proxy. getPeerState { data in
93
+ Task { @MainActor in
94
+ self . svc. onExtensionPeerState ( data)
109
95
}
96
+ continuation. resume ( )
110
97
}
111
- reply ( success)
112
98
}
113
99
}
114
100
}
0 commit comments