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

Commit617a941

Browse files
committed
chore: add network extension manager
1 parentae5b3e2 commit617a941

File tree

16 files changed

+461
-80
lines changed

16 files changed

+461
-80
lines changed

‎Coder Desktop/.swiftlint.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ type_name:
88
identifier_name:
99
allowed_symbols:"_"
1010
min_length:1
11+
cyclomatic_complexity:
12+
warning:15

‎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 DesktopTests/AgentsTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct AgentsTests {
3434
func agentsWhenVPNOff()throws{
3535
vpn.state=.disabled
3636

37-
#expect(throws:(anyError).self){
37+
#expect(throws:Error.self){
3838
_=try view.inspect().find(ViewType.ForEach.self)
3939
}
4040
}
@@ -80,7 +80,7 @@ struct AgentsTests {
8080
vpn.state=.connected
8181
vpn.agents=createMockAgents(count:3)
8282

83-
#expect(throws:(anyError).self){
83+
#expect(throws:Error.self){
8484
_=try view.inspect().find(ViewType.Toggle.self)
8585
}
8686
}

‎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/Coder DesktopTests/VPNStateTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ struct VPNStateTests {
7171

7272
tryawaitViewHosting.host(view.environmentObject(vpn)){
7373
tryawait sut.inspection.inspect{ viewin
74-
#expect(throws:(anyError).self){
74+
#expect(throws:Error.self){
7575
_=try view.find(ViewType.Text.self)
7676
}
7777
}

‎Coder Desktop/VPN/Manager.swift

Lines changed: 172 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,186 @@ import VPNLib
55
actorManager{
66
letptp:PacketTunnelProvider
77
letdownloader:Downloader
8+
letcfg:ManagerConfig
89

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

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

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

‎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: Receive 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

‎Coder Desktop/VPN/TunnelHandle.swift

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,12 @@ actor TunnelHandle {
1515

1616
init(dylibPath:URL)throws(TunnelHandleError){
1717
guardlet dylibHandle=dlopen(dylibPath.path, RTLD_NOW | RTLD_LOCAL)else{
18-
varerrStr="UNKNOWN"
19-
lete=dlerror()
20-
if e!=nil{
21-
errStr=String(cString: e!)
22-
}
23-
throw.dylib(errStr)
18+
throw.dylib(dlerror().flatMap{String(cString: $0)}??"UNKNOWN")
2419
}
2520
self.dylibHandle= dylibHandle
2621

2722
guardlet startSym=dlsym(dylibHandle, startSymbol)else{
28-
varerrStr="UNKNOWN"
29-
lete=dlerror()
30-
if e!=nil{
31-
errStr=String(cString: e!)
32-
}
33-
throw.symbol(startSymbol, errStr)
23+
throw.symbol(startSymbol,dlerror().flatMap{String(cString: $0)}??"UNKNOWN")
3424
}
3525
letopenTunnelFn=unsafeBitCast(startSym, to:OpenTunnel.self)
3626
tunnelReadPipe=Pipe()
@@ -42,21 +32,42 @@ actor TunnelHandle {
4232
}
4333
}
4434

45-
func close()throws{
46-
dlclose(dylibHandle)
35+
// This could be an isolated deinit in Swift 6.1
36+
func close()throws(TunnelHandleError){
37+
varerrs:[Error]=[]
38+
ifdlclose(dylibHandle)==0{
39+
errs.append(TunnelHandleError.dylib(dlerror().flatMap{String(cString: $0)}??"UNKNOWN"))
40+
}
41+
do{
42+
try writeHandle.close()
43+
}catch{
44+
errs.append(error)
45+
}
46+
do{
47+
try readHandle.close()
48+
}catch{
49+
errs.append(error)
50+
}
51+
if !errs.isEmpty{
52+
throw.close(errs)
53+
}
4754
}
4855
}
4956

5057
enumTunnelHandleError:Error{
5158
case dylib(String)
5259
case symbol(String,String)
5360
case openTunnel(OpenTunnelError)
61+
case pipe(Error)
62+
case close([Error])
5463

5564
vardescription:String{
5665
switchself{
66+
caselet.pipe(err):return"pipe error:\(err)"
5767
caselet.dylib(d):return d
5868
caselet.symbol(symbol, message):return"\(symbol):\(message)"
5969
caselet.openTunnel(error):return"OpenTunnel:\(error.message)"
70+
caselet.close(errs):return errs.map(\.localizedDescription).joined(separator:",")
6071
}
6172
}
6273
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp