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

Commit46c2c09

Browse files
chore: add network extension manager (#18)
Relates to#2.Adds the `Manager` abstraction that:- Downloads the dylib- Calls the `SignatureValidator` on a downloaded dylib- Passes network settings to `NEPacketTunnelProvider`- Owns the `TunnelHandle`- Reads and writes to the `Speaker`Eventually, it'll act as the middleman between the tunnel Speaker and the XPC speaker.
1 parent161e5c2 commit46c2c09

File tree

12 files changed

+415
-48
lines changed

12 files changed

+415
-48
lines changed

‎Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,7 @@
832832
DEAD_CODE_STRIPPING = YES;
833833
DEVELOPMENT_TEAM = 4399GN35BJ;
834834
GENERATE_INFOPLIST_FILE = YES;
835-
MACOSX_DEPLOYMENT_TARGET =15.0;
835+
MACOSX_DEPLOYMENT_TARGET =14.6;
836836
MARKETING_VERSION = 1.0;
837837
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests";
838838
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -851,7 +851,7 @@
851851
DEAD_CODE_STRIPPING = YES;
852852
DEVELOPMENT_TEAM = 4399GN35BJ;
853853
GENERATE_INFOPLIST_FILE = YES;
854-
MACOSX_DEPLOYMENT_TARGET =15.0;
854+
MACOSX_DEPLOYMENT_TARGET =14.6;
855855
MARKETING_VERSION = 1.0;
856856
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopTests";
857857
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -869,7 +869,7 @@
869869
DEAD_CODE_STRIPPING = YES;
870870
DEVELOPMENT_TEAM = 4399GN35BJ;
871871
GENERATE_INFOPLIST_FILE = YES;
872-
MACOSX_DEPLOYMENT_TARGET =15.0;
872+
MACOSX_DEPLOYMENT_TARGET =14.6;
873873
MARKETING_VERSION = 1.0;
874874
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests";
875875
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -887,7 +887,7 @@
887887
DEAD_CODE_STRIPPING = YES;
888888
DEVELOPMENT_TEAM = 4399GN35BJ;
889889
GENERATE_INFOPLIST_FILE = YES;
890-
MACOSX_DEPLOYMENT_TARGET =15.0;
890+
MACOSX_DEPLOYMENT_TARGET =14.6;
891891
MARKETING_VERSION = 1.0;
892892
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-DesktopUITests";
893893
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1038,7 +1038,7 @@
10381038
CURRENT_PROJECT_VERSION = 1;
10391039
DEVELOPMENT_TEAM = 4399GN35BJ;
10401040
GENERATE_INFOPLIST_FILE = YES;
1041-
MACOSX_DEPLOYMENT_TARGET =15.0;
1041+
MACOSX_DEPLOYMENT_TARGET =14.6;
10421042
MARKETING_VERSION = 1.0;
10431043
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests";
10441044
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1055,7 +1055,7 @@
10551055
CURRENT_PROJECT_VERSION = 1;
10561056
DEVELOPMENT_TEAM = 4399GN35BJ;
10571057
GENERATE_INFOPLIST_FILE = YES;
1058-
MACOSX_DEPLOYMENT_TARGET =15.0;
1058+
MACOSX_DEPLOYMENT_TARGET =14.6;
10591059
MARKETING_VERSION = 1.0;
10601060
PRODUCT_BUNDLE_IDENTIFIER = "com.coder.Coder-Desktop.VPNLibTests";
10611061
PRODUCT_NAME = "$(TARGET_NAME)";

‎Coder Desktop/Coder Desktop/Preview Content/PreviewClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct PreviewClient: Client {
2323
roles:[]
2424
)
2525
}catch{
26-
throwClientError.reqError(AFError.explicitlyCancelled)
26+
throw.reqError(.explicitlyCancelled)
2727
}
2828
}
2929
}

‎Coder Desktop/Coder Desktop/SDK/Client.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct CoderClient: Client {
3939
caselet.success(data):
4040
returnHTTPResponse(resp: out.response!, data: data, req: out.request)
4141
caselet.failure(error):
42-
throwClientError.reqError(error)
42+
throw.reqError(error)
4343
}
4444
}
4545

@@ -58,7 +58,7 @@ struct CoderClient: Client {
5858
caselet.success(data):
5959
returnHTTPResponse(resp: out.response!, data: data, req: out.request)
6060
caselet.failure(error):
61-
throwClientError.reqError(error)
61+
throw.reqError(error)
6262
}
6363
}
6464

@@ -71,9 +71,9 @@ struct CoderClient: Client {
7171
method: resp.req?.httpMethod,
7272
url: resp.req?.url
7373
)
74-
returnClientError.apiError(out)
74+
return.apiError(out)
7575
}catch{
76-
returnClientError.unexpectedResponse(resp.data[...1024])
76+
return.unexpectedResponse(resp.data[...1024])
7777
}
7878
}
7979

‎Coder Desktop/Coder Desktop/SDK/User.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extension CoderClient {
99
do{
1010
returntryCoderClient.decoder.decode(User.self, from: res.data)
1111
}catch{
12-
throwClientError.unexpectedResponse(res.data[...1024])
12+
throw.unexpectedResponse(res.data[...1024])
1313
}
1414
}
1515
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct LoginForm<C: Client, S: Session>: View {
7070
loading=true
7171
defer{ loading=false}
7272
letclient=C(url: url, token: sessionToken)
73-
dothrows(ClientError){
73+
do{
7474
_=tryawait client.user("me")
7575
}catch{
7676
loginError=.failedAuth(error)

‎Coder Desktop/Coder DesktopTests/Util.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ struct MockClient: Client {
6868
structMockErrorClient:Client{
6969
init(url _:URL, token _:String?){}
7070
func user(_:String)asyncthrows(ClientError)->Coder_Desktop.User{
71-
throwClientError.reqError(.explicitlyCancelled)
71+
throw.reqError(.explicitlyCancelled)
7272
}
7373
}
7474

‎Coder Desktop/VPN/Manager.swift

Lines changed: 190 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,203 @@ import VPNLib
44

55
actorManager{
66
letptp:PacketTunnelProvider
7+
letcfg:ManagerConfig
78

8-
vartunnelHandle:TunnelHandle?
9-
varspeaker:Speaker<Vpn_ManagerMessage,Vpn_TunnelMessage>?
9+
lettunnelHandle:TunnelHandle
10+
letspeaker:Speaker<Vpn_ManagerMessage,Vpn_TunnelMessage>
11+
varreadLoop:Task<Void,anyError>!
1012
// TODO: XPC Speaker
1113

1214
privateletdest=FileManager.default.urls(for:.documentDirectory, in:.userDomainMask)
1315
.first!.appending(path:"coder-vpn.dylib")
1416
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"manager")
1517

16-
init(with:PacketTunnelProvider){
18+
init(with:PacketTunnelProvider, cfg:ManagerConfig)asyncthrows(ManagerError){
1719
ptp= with
20+
self.cfg= cfg
21+
#if arch(arm64)
22+
letdylibPath= cfg.serverUrl.appending(path:"bin/coder-vpn-arm64.dylib")
23+
#elseif arch(x86_64)
24+
letdylibPath= cfg.serverUrl.appending(path:"bin/coder-vpn-amd64.dylib")
25+
#else
26+
fatalError("unknown architecture")
27+
#endif
28+
do{
29+
tryawaitdownload(src: dylibPath, dest: dest)
30+
}catch{
31+
throw.download(error)
32+
}
33+
do{
34+
trySignatureValidator.validate(path: dest)
35+
}catch{
36+
throw.validation(error)
37+
}
38+
do{
39+
try tunnelHandle=TunnelHandle(dylibPath: dest)
40+
}catch{
41+
throw.tunnelSetup(error)
42+
}
43+
speaker=awaitSpeaker<Vpn_ManagerMessage,Vpn_TunnelMessage>(
44+
writeFD: tunnelHandle.writeHandle,
45+
readFD: tunnelHandle.readHandle
46+
)
47+
do{
48+
tryawait speaker.handshake()
49+
}catch{
50+
throw.handshake(error)
51+
}
52+
readLoop=Task{tryawaitrun()}
1853
}
54+
55+
func run()asyncthrows{
56+
do{
57+
fortryawaitmin speaker{
58+
switch m{
59+
caselet.message(msg):
60+
handleMessage(msg)
61+
caselet.RPC(rpc):
62+
handleRPC(rpc)
63+
}
64+
}
65+
}catch{
66+
logger.error("tunnel read loop failed:\(error)")
67+
tryawait tunnelHandle.close()
68+
// TODO: Notify app over XPC
69+
return
70+
}
71+
logger.info("tunnel read loop exited")
72+
tryawait tunnelHandle.close()
73+
// TODO: Notify app over XPC
74+
}
75+
76+
func handleMessage(_ msg:Vpn_TunnelMessage){
77+
guardlet msgType= msg.msgelse{
78+
logger.critical("received message with no type")
79+
return
80+
}
81+
switch msgType{
82+
case.peerUpdate:
83+
{}() // TODO: Send over XPC
84+
caselet.log(logMsg):
85+
writeVpnLog(logMsg)
86+
case.networkSettings,.start,.stop:
87+
logger.critical("received unexpected message: `\(String(describing: msgType))`")
88+
}
89+
}
90+
91+
func handleRPC(_ rpc:RPCRequest<Vpn_ManagerMessage,Vpn_TunnelMessage>){
92+
guardlet msgType= rpc.msg.msgelse{
93+
logger.critical("received rpc with no type")
94+
return
95+
}
96+
switch msgType{
97+
caselet.networkSettings(ns):
98+
letneSettings=convertNetworkSettingsRequest(ns)
99+
ptp.setTunnelNetworkSettings(neSettings)
100+
case.log,.peerUpdate,.start,.stop:
101+
logger.critical("received unexpected rpc: `\(String(describing: msgType))`")
102+
}
103+
}
104+
105+
// TODO: Call via XPC
106+
func startVPN()asyncthrows(ManagerError){
107+
logger.info("sending start rpc")
108+
guardlet tunFd= ptp.tunnelFileDescriptorelse{
109+
throw.noTunnelFileDescriptor
110+
}
111+
letresp:Vpn_TunnelMessage
112+
do{
113+
resp=tryawait speaker.unaryRPC(.with{ msgin
114+
msg.start=.with{ reqin
115+
req.tunnelFileDescriptor= tunFd
116+
req.apiToken= cfg.apiToken
117+
req.coderURL= cfg.serverUrl.absoluteString
118+
}
119+
})
120+
}catch{
121+
throw.failedRPC(error)
122+
}
123+
guard caselet.start(startResp)= resp.msgelse{
124+
throw.incorrectResponse(resp)
125+
}
126+
if !startResp.success{
127+
throw.errorResponse(msg: startResp.errorMessage)
128+
}
129+
// TODO: notify app over XPC
130+
}
131+
132+
// TODO: Call via XPC
133+
func stopVPN()asyncthrows(ManagerError){
134+
logger.info("sending stop rpc")
135+
letresp:Vpn_TunnelMessage
136+
do{
137+
resp=tryawait speaker.unaryRPC(.with{ msgin
138+
msg.stop=.init()
139+
})
140+
}catch{
141+
throw.failedRPC(error)
142+
}
143+
guard caselet.stop(stopResp)= resp.msgelse{
144+
throw.incorrectResponse(resp)
145+
}
146+
if !stopResp.success{
147+
throw.errorResponse(msg: stopResp.errorMessage)
148+
}
149+
// TODO: notify app over XPC
150+
}
151+
152+
// TODO: Call via XPC
153+
// Retrieves the current state of all peers,
154+
// as required when starting the app whilst the network extension is already running
155+
func getPeerInfo()asyncthrows(ManagerError){
156+
logger.info("sending peer state request")
157+
letresp:Vpn_TunnelMessage
158+
do{
159+
resp=tryawait speaker.unaryRPC(.with{ msgin
160+
msg.getPeerUpdate=.init()
161+
})
162+
}catch{
163+
throw.failedRPC(error)
164+
}
165+
guard case.peerUpdate= resp.msgelse{
166+
throw.incorrectResponse(resp)
167+
}
168+
// TODO: pass to app over XPC
169+
}
170+
}
171+
172+
publicstructManagerConfig{
173+
letapiToken:String
174+
letserverUrl:URL
175+
}
176+
177+
enumManagerError:Error{
178+
case download(DownloadError)
179+
case tunnelSetup(TunnelHandleError)
180+
case handshake(HandshakeError)
181+
case validation(ValidationError)
182+
case incorrectResponse(Vpn_TunnelMessage)
183+
case failedRPC(anyError)
184+
case errorResponse(msg:String)
185+
case noTunnelFileDescriptor
186+
}
187+
188+
func writeVpnLog(_ log:Vpn_Log){
189+
letlevel:OSLogType=switch log.level{
190+
case.info:.info
191+
case.debug:.debug
192+
// warn == error
193+
case.warn:.error
194+
case.error:.error
195+
// critical == fatal == fault
196+
case.critical:.fault
197+
case.fatal:.fault
198+
case.UNRECOGNIZED:.info
199+
}
200+
letlogger=Logger(
201+
subsystem:"\(Bundle.main.bundleIdentifier!).dylib",
202+
category: log.loggerNames.joined(separator:".")
203+
)
204+
letfields= log.fields.map{"\($0.name):\($0.value)"}.joined(separator:",")
205+
logger.log(level: level,"\(log.message):\(fields)")
19206
}

‎Coder Desktop/VPN/PacketTunnelProvider.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import os
55
letCTLIOCGINFO:UInt=0xC064_4E03
66

77
classPacketTunnelProvider:NEPacketTunnelProvider,@uncheckedSendable{
8-
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"network-extension")
8+
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"packet-tunnel-provider")
99
privatevarmanager:Manager?
1010

11-
privatevartunnelFileDescriptor:Int32?{
11+
publicvartunnelFileDescriptor:Int32?{
1212
varctlInfo=ctl_info()
1313
withUnsafeMutablePointer(to:&ctlInfo.ctl_name){
1414
$0.withMemoryRebound(to:CChar.self, capacity:MemoryLayout.size(ofValue: $0.pointee)){
@@ -46,7 +46,13 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
4646
completionHandler(nil)
4747
return
4848
}
49-
manager=Manager(with:self)
49+
Task{
50+
// TODO: Retrieve access URL & Token via Keychain
51+
manager=tryawaitManager(
52+
with:self,
53+
cfg:.init(apiToken:"fake-token", serverUrl:.init(string:"https://dev.coder.com")!)
54+
)
55+
}
5056
completionHandler(nil)
5157
}
5258

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp