@@ -3,112 +3,98 @@ import NetworkExtension
33import os
44import VPNLib
55
6- @objc final class VPNXPCInterface : NSObject , VPNXPCClientCallbackProtocol , @unchecked Sendable {
6+ @objc final class AppXPCListener : NSObject , AppXPCInterface , @unchecked Sendable {
77 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 ?
1010
1111 init ( vpn: CoderVPNService ) {
1212 svc = vpn
1313 super. init ( )
1414 }
1515
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
2119 }
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!) " )
3320
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 ( )
4832 }
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 ( )
5737 }
38+ logger. info ( " connecting to \( helperAppMachServiceName) " )
39+ connection. resume ( )
40+ self . connection = connection
41+ return connection
5842 }
5943
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 ( )
6549 }
6650 }
6751
68- func onPeerUpdate( _ data: Data ) {
52+ func onProgress( stage: ProgressStage , downloadProgress: DownloadProgress ? , reply: @escaping ( ) -> Void ) {
53+ let reply = CompletionWrapper ( reply)
6954 Task { @MainActor in
70- svc. onExtensionPeerUpdate ( data)
55+ svc. onProgress ( stage: stage, downloadProgress: downloadProgress)
56+ reply ( )
7157 }
7258 }
59+ }
7360
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+ }
7778 }
7879 }
7980
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)
10995 }
96+ continuation. resume ( )
11097 }
111- reply ( success)
11298 }
11399 }
114100}
0 commit comments