-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add dylib downloader and validator (#16)
First PR for #2. This PR adds an abstraction for downloading & validating the dylib from a Coder server, and the network extension scaffolding. It also adds a `TunnelHandle` type for owning the pair of pipes passed to the dylib, and the handle to the dylib itself. You cannot create a unit test target that targets a System Extension. So, this PR extracts the portion of the network extension that we'd like to test into it's own Framework, `VPNLib`. Of note is that `SwiftProtobuf` doesn't have a stable ABI (as it doesn't use [library evolution](https://www.swift.org/blog/library-evolution/)), so the Framework has the `Build libraries for distribution` setting disabled. This shouldn't effect anything. Exporting the `SwiftProtobuf` types should be fine provided we don't import `SwiftProtobuf` in to the `VPN` target as well.
- Loading branch information
1 parent
e9f5c6f
commit 161e5c2
Showing
21 changed files
with
1,059 additions
and
387 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import NetworkExtension | ||
import os | ||
import VPNLib | ||
|
||
actor Manager { | ||
let ptp: PacketTunnelProvider | ||
|
||
var tunnelHandle: TunnelHandle? | ||
var speaker: Speaker<Vpn_ManagerMessage, Vpn_TunnelMessage>? | ||
// TODO: XPC Speaker | ||
|
||
private let dest = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) | ||
.first!.appending(path: "coder-vpn.dylib") | ||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "manager") | ||
|
||
init(with: PacketTunnelProvider) { | ||
ptp = with | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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" | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
Coder Desktop/VPN/com_coder_Coder_Desktop_VPN-Bridging-Header.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#ifndef CoderPacketTunnelProvider_Bridging_Header_h | ||
#define CoderPacketTunnelProvider_Bridging_Header_h | ||
|
||
// GoInt32 OpenTunnel(GoInt32 cReadFD, GoInt32 cWriteFD); | ||
typedef int(*OpenTunnel)(int, int); | ||
|
||
#endif /* CoderPacketTunnelProvider_Bridging_Header_h */ |
Oops, something went wrong.