-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathPacketTunnelProvider.swift
157 lines (143 loc) · 5.76 KB
/
PacketTunnelProvider.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import NetworkExtension
import os
import VPNLib
/* From <sys/kern_control.h> */
let CTLIOCGINFO: UInt = 0xC064_4E03
class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "provider")
private var manager: Manager?
// a `tunnelRemoteAddress` is required, but not currently used.
private var currentSettings: NEPacketTunnelNetworkSettings = .init(tunnelRemoteAddress: "127.0.0.1")
var tunnelFileDescriptor: Int32? {
var ctlInfo = ctl_info()
withUnsafeMutablePointer(to: &ctlInfo.ctl_name) {
$0.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size(ofValue: $0.pointee)) {
_ = strcpy($0, "com.apple.net.utun_control")
}
}
for fd: Int32 in 0 ... 1024 {
var addr = sockaddr_ctl()
var ret: Int32 = -1
var len = socklen_t(MemoryLayout.size(ofValue: addr))
withUnsafeMutablePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
ret = getpeername(fd, $0, &len)
}
}
if ret != 0 || addr.sc_family != AF_SYSTEM {
continue
}
if ctlInfo.ctl_id == 0 {
ret = ioctl(fd, CTLIOCGINFO, &ctlInfo)
if ret != 0 {
continue
}
}
if addr.sc_id == ctlInfo.ctl_id {
return fd
}
}
return nil
}
override func startTunnel(
options _: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void
) {
logger.info("startTunnel called")
guard manager == nil else {
logger.error("startTunnel called with non-nil Manager")
completionHandler(makeNSError(suffix: "PTP", desc: "Already running"))
return
}
guard let proto = protocolConfiguration as? NETunnelProviderProtocol,
let baseAccessURL = proto.serverAddress
else {
logger.error("startTunnel called with nil protocolConfiguration")
completionHandler(makeNSError(suffix: "PTP", desc: "Missing Configuration"))
return
}
// HACK: We can't write to the system keychain, and the NE can't read the user keychain.
guard let token = proto.providerConfiguration?["token"] as? String else {
logger.error("startTunnel called with nil token")
completionHandler(makeNSError(suffix: "PTP", desc: "Missing Token"))
return
}
logger.debug("retrieved token & access URL")
let completionHandler = CallbackWrapper(completionHandler)
Task {
do throws(ManagerError) {
logger.debug("creating manager")
let manager = try await Manager(
with: self,
cfg: .init(
apiToken: token, serverUrl: .init(string: baseAccessURL)!
)
)
globalXPCListenerDelegate.vpnXPCInterface.manager = manager
logger.debug("starting vpn")
try await manager.startVPN()
logger.info("vpn started")
self.manager = manager
completionHandler(nil)
} catch {
logger.error("error starting manager: \(error.description, privacy: .public)")
completionHandler(
makeNSError(suffix: "Manager", desc: error.description)
)
}
}
}
override func stopTunnel(
with _: NEProviderStopReason, completionHandler: @escaping () -> Void
) {
logger.debug("stopTunnel called")
guard let manager else {
logger.error("stopTunnel called with nil Manager")
completionHandler()
return
}
let completionHandler = CompletionWrapper(completionHandler)
Task { [manager] in
do throws(ManagerError) {
try await manager.stopVPN()
} catch {
logger.error("error stopping manager: \(error.description, privacy: .public)")
}
globalXPCListenerDelegate.vpnXPCInterface.manager = nil
completionHandler()
}
self.manager = nil
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
// Add code here to handle the message.
if let handler = completionHandler {
handler(messageData)
}
}
override func sleep(completionHandler: @escaping () -> Void) {
// Add code here to get ready to sleep.
logger.debug("sleep called")
completionHandler()
}
override func wake() {
// Add code here to wake up.
logger.debug("wake called")
}
// Wrapper around `setTunnelNetworkSettings` that supports merging updates
func applyTunnelNetworkSettings(_ diff: Vpn_NetworkSettingsRequest) async throws {
logger.debug("applying settings diff: \(diff.debugDescription, privacy: .public)")
if diff.hasDnsSettings {
currentSettings.dnsSettings = convertDnsSettings(diff.dnsSettings)
}
if diff.mtu != 0 {
currentSettings.mtu = NSNumber(value: diff.mtu)
}
if diff.hasIpv4Settings {
currentSettings.ipv4Settings = convertIPv4Settings(diff.ipv4Settings)
}
if diff.hasIpv6Settings {
currentSettings.ipv6Settings = convertIPv6Settings(diff.ipv6Settings)
}
logger.info("applying settings: \(self.currentSettings.debugDescription, privacy: .public)")
try await setTunnelNetworkSettings(currentSettings)
}
}