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

Commitf3123f1

Browse files
chore: pass session token to network extension (#34)
1 parent511bafd commitf3123f1

File tree

12 files changed

+256
-159
lines changed

12 files changed

+256
-159
lines changed

‎.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,4 +290,8 @@ xcuserdata
290290
/*.gcno
291291
**/xcshareddata/WorkspaceSettings.xcsettings
292292

293+
### VSCode & Sweetpad ###
294+
.vscode/**
295+
buildServer.json
296+
293297
# End of https://www.toptal.com/developers/gitignore/api/xcode,jetbrains,macos,direnv,swift,swiftpm,objective-c

‎Coder Desktop/.swiftformat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--selfrequired log,info,error,debug,critical,fault
2+
--exclude **.pb.swift
3+
--condassignment always

‎Coder Desktop/Coder Desktop/Coder_DesktopApp.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ struct DesktopApp: App {
1616
.environmentObject(appDelegate.settings)
1717
}
1818
.windowResizability(.contentSize)
19-
SwiftUI.Settings{SettingsView<CoderVPNService>()
20-
.environmentObject(appDelegate.vpn)
21-
.environmentObject(appDelegate.settings)
19+
SwiftUI.Settings{
20+
SettingsView<CoderVPNService>()
21+
.environmentObject(appDelegate.vpn)
22+
.environmentObject(appDelegate.settings)
2223
}
2324
.windowResizability(.contentSize)
2425
}
@@ -32,7 +33,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
3233
letsettings:Settings
3334

3435
overrideinit(){
35-
// TODO: Replace with real implementation
3636
vpn=CoderVPNService()
3737
settings=Settings()
3838
session=SecureSession(onChange: vpn.configureTunnelProviderProtocol)

‎Coder Desktop/Coder Desktop/State.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ class SecureSession: ObservableObject, Session {
4141
if !hasSession{returnnil}
4242
letproto=NETunnelProviderProtocol()
4343
proto.providerBundleIdentifier="\(appId).VPN"
44-
proto.passwordReference=keychain[attributes:Keys.sessionToken]?.persistentRef
44+
// HACK: We can't write to the system keychain, and the user keychain
45+
// isn't accessible, so we'll use providerConfiguration, which is over XPC.
46+
proto.providerConfiguration=["token": sessionToken!]
4547
proto.serverAddress= baseAccessURL!.absoluteString
4648
return proto
4749
}

‎Coder Desktop/VPN/Manager.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ actor Manager {
7979
caselet.message(msg):
8080
handleMessage(msg)
8181
caselet.RPC(rpc):
82-
handleRPC(rpc)
82+
awaithandleRPC(rpc)
8383
}
8484
}
8585
}catch{
86-
logger.error("tunnel read loop failed:\(error)")
86+
logger.error("tunnel read loop failed:\(error.localizedDescription, privacy:.public)")
8787
tryawait tunnelHandle.close()
8888
// TODO: Notify app over XPC
8989
return
@@ -108,21 +108,33 @@ actor Manager {
108108
}
109109
}
110110

111-
func handleRPC(_ rpc:RPCRequest<Vpn_ManagerMessage,Vpn_TunnelMessage>){
111+
func handleRPC(_ rpc:RPCRequest<Vpn_ManagerMessage,Vpn_TunnelMessage>)async{
112112
guardlet msgType= rpc.msg.msgelse{
113113
logger.critical("received rpc with no type")
114114
return
115115
}
116116
switch msgType{
117117
caselet.networkSettings(ns):
118-
letneSettings=convertNetworkSettingsRequest(ns)
119-
ptp.setTunnelNetworkSettings(neSettings)
118+
do{
119+
tryawait ptp.applyTunnelNetworkSettings(ns)
120+
try?await rpc.sendReply(.with{ respin
121+
resp.networkSettings=.with{ settingsin
122+
settings.success=true
123+
}
124+
})
125+
}catch{
126+
try?await rpc.sendReply(.with{ respin
127+
resp.networkSettings=.with{ settingsin
128+
settings.success=false
129+
settings.errorMessage= error.localizedDescription
130+
}
131+
})
132+
}
120133
case.log,.peerUpdate,.start,.stop:
121134
logger.critical("received unexpected rpc: `\(String(describing: msgType))`")
122135
}
123136
}
124137

125-
// TODO: Call via XPC
126138
func startVPN()asyncthrows(ManagerError){
127139
logger.info("sending start rpc")
128140
guardlet tunFd= ptp.tunnelFileDescriptorelse{
@@ -149,7 +161,6 @@ actor Manager {
149161
// TODO: notify app over XPC
150162
}
151163

152-
// TODO: Call via XPC
153164
func stopVPN()asyncthrows(ManagerError){
154165
logger.info("sending stop rpc")
155166
letresp:Vpn_TunnelMessage
@@ -246,5 +257,5 @@ func writeVpnLog(_ log: Vpn_Log) {
246257
category: log.loggerNames.joined(separator:".")
247258
)
248259
letfields= log.fields.map{"\($0.name):\($0.value)"}.joined(separator:",")
249-
logger.log(level: level,"\(log.message):\(fields)")
260+
logger.log(level: level,"\(log.message, privacy:.public):\(fields, privacy:.public)")
250261
}

‎Coder Desktop/VPN/PacketTunnelProvider.swift

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ let CTLIOCGINFO: UInt = 0xC064_4E03
88
classPacketTunnelProvider:NEPacketTunnelProvider,@uncheckedSendable{
99
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"provider")
1010
privatevarmanager:Manager?
11+
// a `tunnelRemoteAddress` is required, but not currently used.
12+
privatevarcurrentSettings:NEPacketTunnelNetworkSettings=.init(tunnelRemoteAddress:"127.0.0.1")
1113

1214
vartunnelFileDescriptor:Int32?{
1315
varctlInfo=ctl_info()
@@ -41,21 +43,42 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
4143
returnnil
4244
}
4345

44-
overridefunc startTunnel(options _:[String:NSObject]?, completionHandler:@escaping(Error?)->Void){
46+
overridefunc startTunnel(
47+
options _:[String:NSObject]?, completionHandler:@escaping(Error?)->Void
48+
){
4549
logger.debug("startTunnel called")
4650
guard manager==nilelse{
4751
logger.error("startTunnel called with non-nil Manager")
48-
completionHandler(nil)
52+
completionHandler(PTPError.alreadyRunning)
4953
return
5054
}
55+
guardlet proto= protocolConfigurationas?NETunnelProviderProtocol,
56+
let baseAccessURL= proto.serverAddress
57+
else{
58+
logger.error("startTunnel called with nil protocolConfiguration")
59+
completionHandler(PTPError.missingConfiguration)
60+
return
61+
}
62+
// HACK: We can't write to the system keychain, and the NE can't read the user keychain.
63+
guardlet token= proto.providerConfiguration?["token"]as?Stringelse{
64+
logger.error("startTunnel called with nil token")
65+
completionHandler(PTPError.missingToken)
66+
return
67+
}
68+
logger.debug("retrieved token & access URL")
5169
letcompletionHandler=CallbackWrapper(completionHandler)
5270
Task{
53-
// TODO: Retrieve access URL & Token via Keychain
5471
dothrows(ManagerError){
72+
logger.debug("creating manager")
5573
manager=tryawaitManager(
5674
with:self,
57-
cfg:.init(apiToken:"fake-token", serverUrl:.init(string:"https://dev.coder.com")!)
75+
cfg:.init(
76+
apiToken: token, serverUrl:.init(string: baseAccessURL)!
77+
)
5878
)
79+
logger.debug("starting vpn")
80+
tryawait manager!.startVPN()
81+
logger.info("vpn started")
5982
completionHandler(nil)
6083
} catch{
6184
completionHandler(error)
@@ -64,15 +87,26 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
6487
}
6588
}
6689

67-
overridefunc stopTunnel(with _:NEProviderStopReason, completionHandler:@escaping()->Void){
90+
overridefunc stopTunnel(
91+
with _:NEProviderStopReason, completionHandler:@escaping()->Void
92+
){
6893
logger.debug("stopTunnel called")
69-
guardmanager!=nilelse{
94+
guardlet managerelse{
7095
logger.error("stopTunnel called with nil Manager")
7196
completionHandler()
7297
return
7398
}
74-
manager=nil
75-
completionHandler()
99+
100+
letcompletionHandler=CompletionWrapper(completionHandler)
101+
Task{[manager]in
102+
dothrows(ManagerError){
103+
tryawait manager.stopVPN()
104+
} catch{
105+
logger.error("error stopping manager:\(error.description, privacy:.public)")
106+
}
107+
completionHandler()
108+
}
109+
self.manager=nil
76110
}
77111

78112
overridefunc handleAppMessage(_ messageData:Data, completionHandler:((Data?)->Void)?){
@@ -92,4 +126,33 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
92126
// Add code here to wake up.
93127
logger.debug("wake called")
94128
}
129+
130+
// Wrapper around `setTunnelNetworkSettings` that supports merging updates
131+
func applyTunnelNetworkSettings(_ diff:Vpn_NetworkSettingsRequest)asyncthrows{
132+
logger.debug("applying settings diff:\(diff.debugDescription, privacy:.public)")
133+
134+
if diff.hasDnsSettings{
135+
currentSettings.dnsSettings=convertDnsSettings(diff.dnsSettings)
136+
}
137+
138+
if diff.mtu!=0{
139+
currentSettings.mtu=NSNumber(value: diff.mtu)
140+
}
141+
142+
if diff.hasIpv4Settings{
143+
currentSettings.ipv4Settings=convertIPv4Settings(diff.ipv4Settings)
144+
}
145+
if diff.hasIpv6Settings{
146+
currentSettings.ipv6Settings=convertIPv6Settings(diff.ipv6Settings)
147+
}
148+
149+
logger.info("applying settings:\(self.currentSettings.debugDescription, privacy:.public)")
150+
tryawaitsetTunnelNetworkSettings(currentSettings)
151+
}
152+
}
153+
154+
enumPTPError:Error{
155+
case alreadyRunning
156+
case missingConfiguration
157+
case missingToken
95158
}

‎Coder Desktop/VPNLib/Convert.swift

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,61 @@
11
import NetworkExtension
22
import os
33

4-
// swiftlint:disable:next function_body_length
5-
publicfunc convertNetworkSettingsRequest(_ req:Vpn_NetworkSettingsRequest)->NEPacketTunnelNetworkSettings{
6-
letnetworkSettings=NEPacketTunnelNetworkSettings(tunnelRemoteAddress: req.tunnelRemoteAddress)
7-
networkSettings.tunnelOverheadBytes=NSNumber(value: req.tunnelOverheadBytes)
8-
networkSettings.mtu=NSNumber(value: req.mtu)
4+
publicfunc convertDnsSettings(_ req:Vpn_NetworkSettingsRequest.DNSSettings)->NEDNSSettings{
5+
letdnsSettings=NEDNSSettings(servers: req.servers)
6+
dnsSettings.searchDomains= req.searchDomains
7+
dnsSettings.domainName= req.domainName
8+
dnsSettings.matchDomains= req.matchDomains
9+
dnsSettings.matchDomainsNoSearch= req.matchDomainsNoSearch
10+
return dnsSettings
11+
}
912

10-
if req.hasDnsSettings{
11-
letdnsSettings=NEDNSSettings(servers: req.dnsSettings.servers)
12-
dnsSettings.searchDomains= req.dnsSettings.searchDomains
13-
dnsSettings.domainName= req.dnsSettings.domainName
14-
dnsSettings.matchDomains= req.dnsSettings.matchDomains
15-
dnsSettings.matchDomainsNoSearch= req.dnsSettings.matchDomainsNoSearch
16-
networkSettings.dnsSettings= dnsSettings
13+
publicfunc convertIPv4Settings(_ req:Vpn_NetworkSettingsRequest.IPv4Settings)->NEIPv4Settings{
14+
letipv4Settings=NEIPv4Settings(addresses: req.addrs, subnetMasks: req.subnetMasks)
15+
if !req.router.isEmpty{
16+
ipv4Settings.router= req.router
1717
}
18-
19-
if req.hasIpv4Settings{
20-
letipv4Settings=NEIPv4Settings(addresses: req.ipv4Settings.addrs, subnetMasks: req.ipv4Settings.subnetMasks)
21-
ipv4Settings.router= req.ipv4Settings.router
22-
ipv4Settings.includedRoutes= req.ipv4Settings.includedRoutes.map{
23-
letroute=NEIPv4Route(destinationAddress: $0.destination, subnetMask: $0.mask)
18+
ipv4Settings.includedRoutes= req.includedRoutes.map{
19+
letroute=NEIPv4Route(destinationAddress: $0.destination, subnetMask: $0.mask)
20+
if !$0.router.isEmpty{
2421
route.gatewayAddress= $0.router
25-
return route
2622
}
27-
ipv4Settings.excludedRoutes= req.ipv4Settings.excludedRoutes.map{
28-
letroute=NEIPv4Route(destinationAddress: $0.destination, subnetMask: $0.mask)
23+
return route
24+
}
25+
ipv4Settings.excludedRoutes= req.excludedRoutes.map{
26+
letroute=NEIPv4Route(destinationAddress: $0.destination, subnetMask: $0.mask)
27+
if !$0.router.isEmpty{
2928
route.gatewayAddress= $0.router
30-
return route
3129
}
32-
networkSettings.ipv4Settings= ipv4Settings
30+
return route
3331
}
32+
return ipv4Settings
33+
}
3434

35-
if req.hasIpv6Settings{
36-
letipv6Settings=NEIPv6Settings(
37-
addresses: req.ipv6Settings.addrs,
38-
networkPrefixLengths: req.ipv6Settings.prefixLengths.map{NSNumber(value: $0)
39-
}
35+
publicfunc convertIPv6Settings(_ req:Vpn_NetworkSettingsRequest.IPv6Settings)->NEIPv6Settings{
36+
letipv6Settings=NEIPv6Settings(
37+
addresses: req.addrs,
38+
networkPrefixLengths: req.prefixLengths.map{NSNumber(value: $0)}
39+
)
40+
ipv6Settings.includedRoutes= req.includedRoutes.map{
41+
letroute=NEIPv6Route(
42+
destinationAddress: $0.destination,
43+
networkPrefixLength:NSNumber(value: $0.prefixLength)
4044
)
41-
ipv6Settings.includedRoutes= req.ipv6Settings.includedRoutes.map{
42-
letroute=NEIPv6Route(
43-
destinationAddress: $0.destination,
44-
networkPrefixLength:NSNumber(value: $0.prefixLength)
45-
)
45+
if !$0.router.isEmpty{
4646
route.gatewayAddress= $0.router
47-
return route
4847
}
49-
ipv6Settings.excludedRoutes= req.ipv6Settings.excludedRoutes.map{
50-
letroute=NEIPv6Route(
51-
destinationAddress: $0.destination,
52-
networkPrefixLength:NSNumber(value: $0.prefixLength)
53-
)
48+
return route
49+
}
50+
ipv6Settings.excludedRoutes= req.excludedRoutes.map{
51+
letroute=NEIPv6Route(
52+
destinationAddress: $0.destination,
53+
networkPrefixLength:NSNumber(value: $0.prefixLength)
54+
)
55+
if !$0.router.isEmpty{
5456
route.gatewayAddress= $0.router
55-
return route
5657
}
57-
networkSettings.ipv6Settings= ipv6Settings
58+
return route
5859
}
59-
returnnetworkSettings
60+
returnipv6Settings
6061
}

‎Coder Desktop/VPNLib/Receiver.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import SwiftProtobuf
66
actorReceiver<RecvMsg:Message>{
77
privateletdispatch:DispatchIO
88
privateletqueue:DispatchQueue
9-
privatevarrunning=false
109
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"proto")
1110

1211
/// Creates an instance using the given `DispatchIO` channel and queue.
@@ -58,11 +57,7 @@ actor Receiver<RecvMsg: Message> {
5857
/// Starts reading protocol messages from the `DispatchIO` channel and returns them as an `AsyncStream` of messages.
5958
/// On read or decoding error, it logs and closes the stream.
6059
func messages()throws(ReceiveError)->AsyncStream<RecvMsg>{
61-
if running{
62-
throw.alreadyRunning
63-
}
64-
running=true
65-
returnAsyncStream(
60+
AsyncStream(
6661
unfolding:{
6762
do{
6863
letlength=tryawaitself.readLen()
@@ -83,7 +78,6 @@ actor Receiver<RecvMsg: Message> {
8378
enumReceiveError:Error{
8479
case readError(String)
8580
case invalidLength
86-
case alreadyRunning
8781
}
8882

8983
func deserializeLen(_ data:Data)throws->UInt32{

‎Coder Desktop/VPNLib/Speaker.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ public actor Speaker<SendMsg: RPCMessage & Message, RecvMsg: RPCMessage & Messag
7979
}
8080
)
8181
receiver=Receiver(dispatch: dispatch, queue: queue)
82-
ifSendMsg.self==Vpn_TunnelMessage.self{
83-
role=.tunnel
82+
role=ifSendMsg.self==Vpn_TunnelMessage.self{
83+
.tunnel
8484
}else{
85-
role=.manager
85+
.manager
8686
}
8787
}
8888

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp