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

Commit15f2bcc

Browse files
feat: add XPC communication to Network Extension (#29)
Co-authored-by: Ethan Dickson <ethan@coder.com>
1 parentf3123f1 commit15f2bcc

File tree

12 files changed

+350
-65
lines changed

12 files changed

+350
-65
lines changed

‎Coder Desktop/Coder Desktop/Coder_Desktop.entitlements

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
<true/>
1111
<key>com.apple.security.app-sandbox</key>
1212
<true/>
13+
<key>com.apple.security.application-groups</key>
14+
<array>
15+
<string>$(TeamIdentifierPrefix)com.coder.Coder-Desktop</string>
16+
</array>
1317
<key>com.apple.security.files.user-selected.read-only</key>
1418
<true/>
1519
<key>com.apple.security.network.client</key>

‎Coder Desktop/Coder Desktop/Coder_DesktopApp.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
4949

5050
func applicationShouldTerminate(_:NSApplication)->NSApplication.TerminateReply{
5151
Task{
52-
await vpn.stop()
53-
NSApp.reply(toApplicationShouldTerminate:true)
52+
await vpn.quit()
5453
}
5554
return.terminateLater
5655
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPEplist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plistversion="1.0">
4+
<dict>
5+
<key>NetworkExtension</key>
6+
<dict>
7+
<key>NEMachServiceName</key>
8+
<string>$(TeamIdentifierPrefix)com.coder.Coder-Desktop.VPN</string>
9+
</dict>
10+
</dict>
11+
</plist>

‎Coder Desktop/Coder Desktop/VPNService.swift

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import NetworkExtension
22
import os
33
import SwiftUI
4+
import VPNLib
5+
import VPNXPC
46

57
@MainActor
68
protocolVPNService:ObservableObject{
@@ -43,6 +45,9 @@ enum VPNServiceError: Error, Equatable {
4345
@MainActor
4446
finalclassCoderVPNService:NSObject,VPNService{
4547
varlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"vpn")
48+
lazyvarxpc:VPNXPCInterface=.init(vpn:self)
49+
varterminating=false
50+
4651
@PublishedvartunnelState:VPNServiceState=.disabled
4752
@PublishedvarsysExtnState:SystemExtensionState=.uninstalled
4853
@PublishedvarneState:NetworkExtensionState=.unconfigured
@@ -71,46 +76,45 @@ final class CoderVPNService: NSObject, VPNService {
7176
}
7277
}
7378

74-
varstartTask:Task<Void,Never>?
7579
func start()async{
76-
ifawait startTask?.value!=nil{
80+
switch tunnelState{
81+
case.disabled,.failed:
82+
break
83+
default:
7784
return
7885
}
79-
startTask=Task{
80-
tunnelState=.connecting
81-
awaitenableNetworkExtension()
8286

83-
// TODO: enable communication with the NetworkExtension to track state and agents. For
84-
// now, just pretend it worked...
85-
tunnelState=.connected
86-
}
87-
defer{ startTask=nil}
88-
await startTask?.value
87+
// this ping is somewhat load bearing since it causes xpc to init
88+
xpc.ping()
89+
tunnelState=.connecting
90+
awaitenableNetworkExtension()
91+
logger.debug("network extension enabled")
8992
}
9093

91-
var stopTask: Task<Void, Never>?
9294
func stop()async{
93-
// Wait for a start operation to finish first
94-
await startTask?.value
95-
guard state==.connectedelse{return}
96-
ifawait stopTask?.value!=nil{
97-
return
98-
}
99-
stopTask=Task{
100-
tunnelState=.disconnecting
101-
awaitdisableNetworkExtension()
95+
guard tunnelState==.connectedelse{return}
96+
tunnelState=.disconnecting
97+
awaitdisableNetworkExtension()
98+
logger.info("network extension stopped")
99+
}
102100

103-
// TODO: determine when the NetworkExtension is completely disconnected
104-
tunnelState=.disabled
101+
// Instructs the service to stop the VPN and then quit once the stop event
102+
// is read over XPC.
103+
// MUST only be called from `NSApplicationDelegate.applicationShouldTerminate`
104+
// MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
105+
func quit()async{
106+
guard tunnelState==.connectedelse{
107+
NSApp.reply(toApplicationShouldTerminate:true)
108+
return
105109
}
106-
defer{ stopTask=nil}
107-
awaitstopTask?.value
110+
terminating=true
111+
awaitstop()
108112
}
109113

110114
func configureTunnelProviderProtocol(proto:NETunnelProviderProtocol?){
111115
Task{
112-
ifproto!=nil{
113-
awaitconfigureNetworkExtension(proto: proto!)
116+
iflet proto{
117+
awaitconfigureNetworkExtension(proto: proto)
114118
// this just configures the VPN, it doesn't enable it
115119
tunnelState=.disabled
116120
}else{
@@ -119,10 +123,39 @@ final class CoderVPNService: NSObject, VPNService {
119123
neState=.unconfigured
120124
tunnelState=.disabled
121125
}catch{
122-
logger.error("failed toremoing network extension:\(error)")
126+
logger.error("failed toremove network extension:\(error)")
123127
neState=.failed(error.localizedDescription)
124128
}
125129
}
126130
}
127131
}
132+
133+
func onExtensionPeerUpdate(_ data:Data){
134+
// TODO: handle peer update
135+
logger.info("network extension peer update")
136+
do{
137+
letmsg=tryVpn_TunnelMessage(serializedBytes: data)
138+
debugPrint(msg)
139+
}catch{
140+
logger.error("failed to decode peer update\(error)")
141+
}
142+
}
143+
144+
func onExtensionStart(){
145+
logger.info("network extension reported started")
146+
tunnelState=.connected
147+
}
148+
149+
func onExtensionStop(){
150+
logger.info("network extension reported stopped")
151+
tunnelState=.disabled
152+
if terminating{
153+
NSApp.reply(toApplicationShouldTerminate:true)
154+
}
155+
}
156+
157+
func onExtensionError(_ error:NSError){
158+
logger.error("network extension reported error:\(error)")
159+
tunnelState=.failed(.internalError(error.localizedDescription))
160+
}
128161
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import Foundation
2+
import os
3+
import VPNXPC
4+
5+
@objcfinalclassVPNXPCInterface:NSObject,VPNXPCClientCallbackProtocol,@uncheckedSendable{
6+
privatevarsvc:CoderVPNService
7+
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"VPNXPCInterface")
8+
privateletxpc:VPNXPCProtocol
9+
10+
init(vpn:CoderVPNService){
11+
svc= vpn
12+
13+
letnetworkExtDict=Bundle.main.object(forInfoDictionaryKey:"NetworkExtension")as?[String:Any]
14+
letmachServiceName=networkExtDict?["NEMachServiceName"]as?String
15+
letxpcConn=NSXPCConnection(machServiceName: machServiceName!)
16+
xpcConn.remoteObjectInterface=NSXPCInterface(with:VPNXPCProtocol.self)
17+
xpcConn.exportedInterface=NSXPCInterface(with:VPNXPCClientCallbackProtocol.self)
18+
guardlet proxy= xpcConn.remoteObjectProxyas?VPNXPCProtocolelse{
19+
fatalError("invalid xpc cast")
20+
}
21+
xpc= proxy
22+
23+
super.init()
24+
25+
xpcConn.exportedObject=self
26+
xpcConn.invalidationHandler={[logger]in
27+
Task{@MainActorin
28+
logger.error("XPC connection invalidated.")
29+
}
30+
}
31+
xpcConn.interruptionHandler={[logger]in
32+
Task{@MainActorin
33+
logger.error("XPC connection interrupted.")
34+
}
35+
}
36+
xpcConn.resume()
37+
}
38+
39+
func ping(){
40+
xpc.ping{
41+
Task{@MainActorin
42+
self.logger.info("Connected to NE over XPC")
43+
}
44+
}
45+
}
46+
47+
func onPeerUpdate(_ data:Data){
48+
Task{@MainActorin
49+
svc.onExtensionPeerUpdate(data)
50+
}
51+
}
52+
53+
func onStart(){
54+
Task{@MainActorin
55+
svc.onExtensionStart()
56+
}
57+
}
58+
59+
func onStop(){
60+
Task{@MainActorin
61+
svc.onExtensionStop()
62+
}
63+
}
64+
65+
func onError(_ err:NSError){
66+
Task{@MainActorin
67+
svc.onExtensionError(err)
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp