Skip to content

Commit c4c9f3f

Browse files
committed
working now
1 parent a439536 commit c4c9f3f

File tree

8 files changed

+168
-65
lines changed

8 files changed

+168
-65
lines changed

Coder Desktop/Coder Desktop/VPNService.swift

+10-18
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ enum VPNServiceError: Error, Equatable {
4343
}
4444

4545
@MainActor
46-
final class CoderVPNService: NSObject, VPNService, @preconcurrency VPNXPCClientCallbackProtocol {
46+
final class CoderVPNService: NSObject, VPNService {
4747
var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "vpn")
48-
var xpcConn: NSXPCConnection
48+
// TODO: better init maybe? kinda wonky
49+
lazy var xpc = VPNXPCInterface(vpn: self)
50+
4951
@Published var tunnelState: VPNServiceState = .disabled
5052
@Published var sysExtnState: SystemExtensionState = .uninstalled
5153
@Published var neState: NetworkExtensionState = .unconfigured
@@ -67,19 +69,7 @@ final class CoderVPNService: NSObject, VPNService, @preconcurrency VPNXPCClientC
6769
var systemExtnDelegate: SystemExtensionDelegate<CoderVPNService>?
6870

6971
override init() {
70-
let networkExtDict = Bundle.main.object(forInfoDictionaryKey: "NetworkExtension") as? [String: Any]
71-
let machServiceName = networkExtDict?["NEMachServiceName"] as? String
72-
xpcConn = NSXPCConnection(machServiceName: machServiceName!)
73-
xpcConn.remoteObjectInterface = NSXPCInterface(with: VPNXPCProtocol.self)
74-
xpcConn.exportedInterface = NSXPCInterface(with: VPNXPCClientCallbackProtocol.self)
75-
7672
super.init()
77-
xpcConn.exportedObject = self
78-
// xpcConn.invalidationHandler = {
79-
// // self.logger.error("XPC connection invalidated.")
80-
// print("XPC connection invalidated")
81-
// }
82-
xpcConn.resume()
8373
installSystemExtension()
8474
Task {
8575
await loadNetworkExtension()
@@ -91,6 +81,8 @@ final class CoderVPNService: NSObject, VPNService, @preconcurrency VPNXPCClientC
9181
if await startTask?.value != nil {
9282
return
9383
}
84+
// this ping is somewhat load bearing since it causes xpc to init
85+
xpc.ping()
9486
startTask = Task {
9587
tunnelState = .connecting
9688
await enableNetworkExtension()
@@ -137,7 +129,7 @@ final class CoderVPNService: NSObject, VPNService, @preconcurrency VPNXPCClientC
137129
}
138130
}
139131

140-
func onPeerUpdate(_ data: Data) {
132+
func onExtensionPeerUpdate(_ data: Data) {
141133
// TODO: handle peer update
142134
logger.info("network extension peer update")
143135
do {
@@ -148,17 +140,17 @@ final class CoderVPNService: NSObject, VPNService, @preconcurrency VPNXPCClientC
148140
}
149141
}
150142

151-
func onStart() {
143+
func onExtensionStart() {
152144
logger.info("network extension reported started")
153145
tunnelState = .connected
154146
}
155147

156-
func onStop() {
148+
func onExtensionStop() {
157149
logger.info("network extension reported stopped")
158150
tunnelState = .disabled
159151
}
160152

161-
func onError(_ error: NSError) {
153+
func onExtensionError(_ error: NSError) {
162154
logger.info("network extension reported error: \(error)")
163155
}
164156
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import os
2+
import Foundation
3+
import XPCHub
4+
import VPNXPC
5+
6+
@objc final class VPNXPCInterface: NSObject, XPCClientCallbackProtocol, @unchecked Sendable {
7+
private var svc: CoderVPNService
8+
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "VPNXPCInterface")
9+
private let xpc: VPNXPCProtocol
10+
11+
init(vpn: CoderVPNService) {
12+
svc = vpn
13+
14+
let networkExtDict = Bundle.main.object(forInfoDictionaryKey: "NetworkExtension") as? [String: Any]
15+
let machServiceName = networkExtDict?["NEMachServiceName"] as? String
16+
let xpcConn = NSXPCConnection(machServiceName: machServiceName!)
17+
xpcConn.remoteObjectInterface = NSXPCInterface(with: VPNXPCProtocol.self)
18+
xpcConn.exportedInterface = NSXPCInterface(with: VPNXPCClientCallbackProtocol.self)
19+
guard let proxy = xpcConn.remoteObjectProxy as? VPNXPCProtocol else {
20+
fatalError("invalid xpc cast")
21+
}
22+
xpc = proxy
23+
24+
super.init()
25+
26+
xpcConn.exportedObject = self
27+
xpcConn.invalidationHandler = { [weak self] in
28+
guard let self = self else { return }
29+
Task { @MainActor in
30+
self.logger.error("XPC connection invalidated.")
31+
}
32+
}
33+
xpcConn.interruptionHandler = { [weak self] in
34+
guard let self = self else { return }
35+
Task { @MainActor in
36+
self.logger.error("XPC connection interrupted.")
37+
}
38+
}
39+
xpcConn.resume()
40+
41+
xpc.ping {
42+
print("Got response from XPC")
43+
}
44+
}
45+
46+
func ping() {
47+
xpc.ping {
48+
Task { @MainActor in
49+
print("Got response from XPC")
50+
}
51+
}
52+
}
53+
54+
func onPeerUpdate(_ data: Data) {
55+
Task { @MainActor in
56+
svc.onExtensionPeerUpdate(data)
57+
}
58+
}
59+
60+
func onStart() {
61+
Task { @MainActor in
62+
svc.onExtensionStart()
63+
}
64+
}
65+
66+
func onStop() {
67+
Task { @MainActor in
68+
svc.onExtensionStop()
69+
}
70+
}
71+
72+
func onError(_ err: NSError) {
73+
Task { @MainActor in
74+
svc.onExtensionError(err)
75+
}
76+
}
77+
}

Coder Desktop/VPN/Manager.swift

+42-35
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import CoderSDK
22
import NetworkExtension
3-
import os
43
import VPNLib
4+
import XPCHub
55
import VPNXPC
6+
import os
67

78
actor Manager {
89
let ptp: PacketTunnelProvider
@@ -69,6 +70,7 @@ actor Manager {
6970
} catch {
7071
fatalError("openTunnelTask must only throw TunnelHandleError")
7172
}
73+
7274
readLoop = Task { try await run() }
7375
}
7476

@@ -85,17 +87,15 @@ actor Manager {
8587
} catch {
8688
logger.error("tunnel read loop failed: \(error)")
8789
try await tunnelHandle.close()
88-
if let connection = globalXPCListenerDelegate.getActiveConnection() {
89-
let client = connection.remoteObjectProxy as? VPNXPCClientCallbackProtocol
90-
client?.onError(error as NSError)
90+
if let conn = globalXPCListenerDelegate.getActiveConnection() {
91+
conn.onError(error as NSError)
9192
}
9293
return
9394
}
9495
logger.info("tunnel read loop exited")
9596
try await tunnelHandle.close()
96-
if let connection = globalXPCListenerDelegate.getActiveConnection() {
97-
let client = connection.remoteObjectProxy as? VPNXPCClientCallbackProtocol
98-
client?.onStop()
97+
if let conn = globalXPCListenerDelegate.getActiveConnection() {
98+
conn.onStop()
9999
}
100100
}
101101

@@ -106,12 +106,10 @@ actor Manager {
106106
}
107107
switch msgType {
108108
case .peerUpdate:
109-
if let connection = globalXPCListenerDelegate.getActiveConnection() {
110-
// We can call back to the client
109+
if let conn = globalXPCListenerDelegate.getActiveConnection() {
111110
do {
112-
let client = connection.remoteObjectProxy as? VPNXPCClientCallbackProtocol
113111
let data = try msg.peerUpdate.serializedData()
114-
client!.onPeerUpdate(data)
112+
conn.onPeerUpdate(data)
115113
} catch {
116114
logger.error("failed to send peer update to client: \(error)")
117115
}
@@ -140,35 +138,42 @@ actor Manager {
140138
func startVPN() async throws(ManagerError) {
141139
logger.info("sending start rpc")
142140
guard let tunFd = ptp.tunnelFileDescriptor else {
141+
logger.error("no fd")
143142
throw .noTunnelFileDescriptor
144143
}
145144
let resp: Vpn_TunnelMessage
146145
do {
147-
resp = try await speaker.unaryRPC(.with { msg in
148-
msg.start = .with { req in
149-
req.tunnelFileDescriptor = tunFd
150-
req.apiToken = cfg.apiToken
151-
req.coderURL = cfg.serverUrl.absoluteString
152-
}
153-
})
146+
resp = try await speaker.unaryRPC(
147+
.with { msg in
148+
msg.start = .with { req in
149+
req.tunnelFileDescriptor = tunFd
150+
req.apiToken = cfg.apiToken
151+
req.coderURL = cfg.serverUrl.absoluteString
152+
}
153+
})
154154
} catch {
155+
logger.error("rpc failed \(error)")
155156
throw .failedRPC(error)
156157
}
157158
guard case let .start(startResp) = resp.msg else {
159+
logger.error("incorrect response")
158160
throw .incorrectResponse(resp)
159161
}
160162
if !startResp.success {
163+
logger.error("no success")
161164
throw .errorResponse(msg: startResp.errorMessage)
162165
}
166+
logger.info("startVPN done")
163167
}
164168

165169
func stopVPN() async throws(ManagerError) {
166170
logger.info("sending stop rpc")
167171
let resp: Vpn_TunnelMessage
168172
do {
169-
resp = try await speaker.unaryRPC(.with { msg in
170-
msg.stop = .init()
171-
})
173+
resp = try await speaker.unaryRPC(
174+
.with { msg in
175+
msg.stop = .init()
176+
})
172177
} catch {
173178
throw .failedRPC(error)
174179
}
@@ -186,9 +191,10 @@ actor Manager {
186191
logger.info("sending peer state request")
187192
let resp: Vpn_TunnelMessage
188193
do {
189-
resp = try await speaker.unaryRPC(.with { msg in
190-
msg.getPeerUpdate = .init()
191-
})
194+
resp = try await speaker.unaryRPC(
195+
.with { msg in
196+
msg.getPeerUpdate = .init()
197+
})
192198
} catch {
193199
throw .failedRPC(error)
194200
}
@@ -240,17 +246,18 @@ enum ManagerError: Error {
240246
}
241247

242248
func writeVpnLog(_ log: Vpn_Log) {
243-
let level: OSLogType = switch log.level {
244-
case .info: .info
245-
case .debug: .debug
246-
// warn == error
247-
case .warn: .error
248-
case .error: .error
249-
// critical == fatal == fault
250-
case .critical: .fault
251-
case .fatal: .fault
252-
case .UNRECOGNIZED: .info
253-
}
249+
let level: OSLogType =
250+
switch log.level {
251+
case .info: .info
252+
case .debug: .debug
253+
// warn == error
254+
case .warn: .error
255+
case .error: .error
256+
// critical == fatal == fault
257+
case .critical: .fault
258+
case .fatal: .fault
259+
case .UNRECOGNIZED: .info
260+
}
254261
let logger = Logger(
255262
subsystem: "\(Bundle.main.bundleIdentifier!).dylib",
256263
category: log.loggerNames.joined(separator: ".")

Coder Desktop/VPN/PacketTunnelProvider.swift

+20-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import NetworkExtension
2-
import os
32
import VPNLib
3+
import VPNXPC
4+
import os
45

56
/* From <sys/kern_control.h> */
67
let CTLIOCGINFO: UInt = 0xC064_4E03
@@ -16,7 +17,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
1617
_ = strcpy($0, "com.apple.net.utun_control")
1718
}
1819
}
19-
for fd: Int32 in 0 ... 1024 {
20+
for fd: Int32 in 0...1024 {
2021
var addr = sockaddr_ctl()
2122
var ret: Int32 = -1
2223
var len = socklen_t(MemoryLayout.size(ofValue: addr))
@@ -44,7 +45,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
4445
override func startTunnel(
4546
options _: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void
4647
) {
47-
logger.debug("startTunnel called")
48+
logger.info("startTunnel called")
4849
guard manager == nil else {
4950
logger.error("startTunnel called with non-nil Manager")
5051
completionHandler(nil)
@@ -54,21 +55,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
5455
Task {
5556
// TODO: Retrieve access URL & Token via Keychain
5657
do throws(ManagerError) {
57-
logger.debug("creating manager")
58+
logger.info("creating manager")
5859
manager = try await Manager(
5960
with: self,
6061
cfg: .init(
61-
apiToken: "fake-token", serverUrl: .init(string: "https://dev.coder.com")!
62+
apiToken: "qGg1rDGWzL-a814TWDGcTDOs4AX7laDEI",
63+
serverUrl: .init(string: "https://dev.coder.com")!
6264
)
6365
)
6466
globalXPCListenerDelegate.vpnXPCInterface.setManager(manager)
6567
logger.debug("calling manager.startVPN")
66-
try await manager!.startVPN()
68+
// try await manager!.startVPN()
6769
logger.debug("vpn started")
70+
if let conn = globalXPCListenerDelegate.getActiveConnection() {
71+
conn.onStart()
72+
} else {
73+
logger.info("no active connection")
74+
}
6875
completionHandler(nil)
6976
} catch {
70-
completionHandler(error as NSError)
7177
logger.error("error starting manager: \(error.description, privacy: .public)")
78+
completionHandler(error as NSError)
7279
}
7380
}
7481
}
@@ -83,6 +90,12 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
8390
return
8491
}
8592

93+
if let conn = globalXPCListenerDelegate.getActiveConnection() {
94+
conn.onStop()
95+
} else {
96+
logger.info("no active connection")
97+
}
98+
8699
let managerCopy = manager
87100
Task {
88101
do throws(ManagerError) {

Coder Desktop/VPN/VPN.entitlements

+3
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@
1414
</array>
1515
<key>com.apple.security.network.client</key>
1616
<true/>
17+
<key>com.apple.security.network.server</key>
18+
<true/>
19+
1720
</dict>
1821
</plist>

0 commit comments

Comments
 (0)