@@ -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