-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathTunnelHandle.swift
91 lines (80 loc) · 3 KB
/
TunnelHandle.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
import Foundation
import os
let startSymbol = "OpenTunnel"
actor TunnelHandle {
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "tunnel-handle")
private let tunnelWritePipe: Pipe
private let tunnelReadPipe: Pipe
private let dylibHandle: UnsafeMutableRawPointer
var writeHandle: FileHandle { tunnelReadPipe.fileHandleForWriting }
var readHandle: FileHandle { tunnelWritePipe.fileHandleForReading }
init(dylibPath: URL) throws(TunnelHandleError) {
guard let dylibHandle = dlopen(dylibPath.path, RTLD_NOW | RTLD_LOCAL) else {
throw .dylib(dlerror().flatMap { String(cString: $0) } ?? "UNKNOWN")
}
self.dylibHandle = dylibHandle
guard let startSym = dlsym(dylibHandle, startSymbol) else {
throw .symbol(startSymbol, dlerror().flatMap { String(cString: $0) } ?? "UNKNOWN")
}
let openTunnelFn = unsafeBitCast(startSym, to: OpenTunnel.self)
tunnelReadPipe = Pipe()
tunnelWritePipe = Pipe()
let res = openTunnelFn(tunnelReadPipe.fileHandleForReading.fileDescriptor,
tunnelWritePipe.fileHandleForWriting.fileDescriptor)
guard res == 0 else {
throw .openTunnel(OpenTunnelError(rawValue: res) ?? .unknown)
}
}
// This could be an isolated deinit in Swift 6.1
func close() throws(TunnelHandleError) {
var errs: [Error] = []
if dlclose(dylibHandle) == 0 {
errs.append(TunnelHandleError.dylib(dlerror().flatMap { String(cString: $0) } ?? "UNKNOWN"))
}
do {
try writeHandle.close()
} catch {
errs.append(error)
}
do {
try readHandle.close()
} catch {
errs.append(error)
}
if !errs.isEmpty {
throw .close(errs)
}
}
}
enum TunnelHandleError: Error {
case dylib(String)
case symbol(String, String)
case openTunnel(OpenTunnelError)
case pipe(any Error)
case close([any Error])
var description: String {
switch self {
case let .pipe(err): return "pipe error: \(err)"
case let .dylib(d): return d
case let .symbol(symbol, message): return "\(symbol): \(message)"
case let .openTunnel(error): return "OpenTunnel: \(error.message)"
case let .close(errs): return "close tunnel: \(errs.map(\.localizedDescription).joined(separator: ", "))"
}
}
}
enum OpenTunnelError: Int32 {
case errDupReadFD = -2
case errDupWriteFD = -3
case errOpenPipe = -4
case errNewTunnel = -5
case unknown = -99
var message: String {
switch self {
case .errDupReadFD: return "Failed to duplicate read file descriptor"
case .errDupWriteFD: return "Failed to duplicate write file descriptor"
case .errOpenPipe: return "Failed to open the pipe"
case .errNewTunnel: return "Failed to create a new tunnel"
case .unknown: return "Unknown error code"
}
}
}