Merge branch 'main' into ios-ui
This commit is contained in:
commit
1651872939
31 changed files with 900 additions and 117 deletions
|
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; };
|
||||
0B46E8E02AC918CA00BA2A3C /* BurrowIpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* BurrowIpc.swift */; };
|
||||
43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuView.swift */; };
|
||||
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; };
|
||||
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; };
|
||||
|
|
@ -46,6 +48,8 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0B28F1552ABF463A000D44B0 /* DataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTypes.swift; sourceTree = "<group>"; };
|
||||
0B46E8DF2AC918CA00BA2A3C /* BurrowIpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowIpc.swift; sourceTree = "<group>"; };
|
||||
43AA26D72A10004900F14CE6 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; };
|
||||
D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; };
|
||||
|
|
@ -122,6 +126,8 @@
|
|||
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */,
|
||||
D020F66629E4A95D002790F6 /* NetworkExtension-macOS.entitlements */,
|
||||
D020F66229E4A6E5002790F6 /* NetworkExtension.xcconfig */,
|
||||
0B28F1552ABF463A000D44B0 /* DataTypes.swift */,
|
||||
0B46E8DF2AC918CA00BA2A3C /* BurrowIpc.swift */,
|
||||
D0B98FD729FDDB57004E7149 /* libburrow */,
|
||||
);
|
||||
path = NetworkExtension;
|
||||
|
|
@ -304,6 +310,8 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */,
|
||||
0B46E8E02AC918CA00BA2A3C /* BurrowIpc.swift in Sources */,
|
||||
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
133
Apple/NetworkExtension/BurrowIpc.swift
Normal file
133
Apple/NetworkExtension/BurrowIpc.swift
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import Foundation
|
||||
import Network
|
||||
import os
|
||||
|
||||
final class LineProtocol: NWProtocolFramerImplementation {
|
||||
static let definition = NWProtocolFramer.Definition(implementation: LineProtocol.self)
|
||||
static let label = "Lines"
|
||||
init(framer: NWProtocolFramer.Instance) { }
|
||||
func start(framer: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult { .ready }
|
||||
func stop(framer: NWProtocolFramer.Instance) -> Bool { true }
|
||||
func wakeup(framer: NWProtocolFramer.Instance) { }
|
||||
func cleanup(framer: NWProtocolFramer.Instance) { }
|
||||
func lines(from buffer: UnsafeMutableRawBufferPointer?) -> (lines: [Data], size: Int)? {
|
||||
guard let buffer = buffer else { return nil }
|
||||
let lines = buffer
|
||||
.split(separator: 10)
|
||||
guard !lines.isEmpty else { return nil }
|
||||
let size = lines
|
||||
.lazy
|
||||
.map(\.count)
|
||||
.reduce(0, +) + lines.count
|
||||
let strings = lines
|
||||
.lazy
|
||||
.map { Data($0) }
|
||||
return (lines: Array(strings), size: size)
|
||||
}
|
||||
func handleInput(framer: NWProtocolFramer.Instance) -> Int {
|
||||
var result: [Data] = []
|
||||
_ = framer.parseInput(minimumIncompleteLength: 1, maximumLength: 16_000) { buffer, _ in
|
||||
guard let (lines, size) = lines(from: buffer) else {
|
||||
return 0
|
||||
}
|
||||
result = lines
|
||||
return size
|
||||
}
|
||||
for line in result {
|
||||
framer.deliverInput(data: line, message: .init(instance: framer), isComplete: true)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func handleOutput(
|
||||
framer: NWProtocolFramer.Instance,
|
||||
message: NWProtocolFramer.Message,
|
||||
messageLength: Int,
|
||||
isComplete: Bool
|
||||
) {
|
||||
do {
|
||||
try framer.writeOutputNoCopy(length: messageLength)
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NWConnection {
|
||||
func receiveMessage() async throws -> (Data?, NWConnection.ContentContext?, Bool) {
|
||||
try await withUnsafeThrowingContinuation { continuation in
|
||||
receiveMessage { completeContent, contentContext, isComplete, error in
|
||||
if let error = error {
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
continuation.resume(returning: (completeContent, contentContext, isComplete))
|
||||
}
|
||||
}
|
||||
}
|
||||
func send_raw(_ request: Data) async throws -> Data {
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
let comp: NWConnection.SendCompletion = .contentProcessed {error in
|
||||
if let error = error {
|
||||
continuation.resume(with: .failure(error))
|
||||
} else {
|
||||
continuation.resume(with: .success(request))
|
||||
}
|
||||
}
|
||||
self.send(content: request, completion: comp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class BurrowIpc {
|
||||
let connection: NWConnection
|
||||
private var generator = SystemRandomNumberGenerator()
|
||||
private var logger: Logger
|
||||
init(logger: Logger) {
|
||||
let params = NWParameters.tcp
|
||||
params.defaultProtocolStack
|
||||
.applicationProtocols
|
||||
.insert(NWProtocolFramer.Options(definition: LineProtocol.definition), at: 0)
|
||||
let connection = NWConnection(to: .unix(path: "burrow.sock"), using: params)
|
||||
connection.start(queue: .global())
|
||||
self.connection = connection
|
||||
self.logger = logger
|
||||
}
|
||||
func send<T: Request, U: Decodable>(_ request: T) async throws -> U {
|
||||
do {
|
||||
let id: UInt = generator.next(upperBound: UInt.max)
|
||||
var copy = request
|
||||
copy.id = id
|
||||
var data = try JSONEncoder().encode(request)
|
||||
data.append(contentsOf: [10])
|
||||
_ = try await self.connection.send_raw(data)
|
||||
return try JSONDecoder().decode(Response<U>.self, from: data).result
|
||||
} catch {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
func receive_raw() async throws -> Data {
|
||||
let (completeContent, _, _) = try await connection.receiveMessage()
|
||||
self.logger.info("Received raw message response")
|
||||
guard let data = completeContent else {
|
||||
throw BurrowError.resultIsNone
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func request<U: Decodable>(_ request: Request, type: U.Type) async throws -> U {
|
||||
do {
|
||||
var data: Data = try JSONEncoder().encode(request)
|
||||
data.append(contentsOf: [10])
|
||||
_ = try await self.connection.send_raw(data)
|
||||
self.logger.debug("message sent")
|
||||
let receivedData = try await receive_raw()
|
||||
self.logger.info("Received result: \(String(decoding: receivedData, as: UTF8.self))")
|
||||
return try self.parse_response(receivedData)
|
||||
} catch {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
func parse_response<U: Decodable>(_ response: Data) throws -> U {
|
||||
try JSONDecoder().decode(U.self, from: response)
|
||||
}
|
||||
}
|
||||
40
Apple/NetworkExtension/DataTypes.swift
Normal file
40
Apple/NetworkExtension/DataTypes.swift
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import Foundation
|
||||
|
||||
enum BurrowError: Error {
|
||||
case addrDoesntExist
|
||||
case resultIsError
|
||||
case cantParseResult
|
||||
case resultIsNone
|
||||
}
|
||||
|
||||
protocol Request: Codable {
|
||||
var id: UInt { get set }
|
||||
var command: String { get set }
|
||||
}
|
||||
|
||||
struct BurrowRequest: Request {
|
||||
var id: UInt
|
||||
var command: String
|
||||
}
|
||||
|
||||
struct Response<T>: Decodable where T: Decodable {
|
||||
var id: UInt
|
||||
var result: T
|
||||
}
|
||||
|
||||
// swiftlint:disable identifier_name
|
||||
struct BurrowResult<T>: Codable where T: Codable {
|
||||
var Ok: T?
|
||||
var Err: String?
|
||||
}
|
||||
|
||||
struct ServerConfigData: Codable {
|
||||
struct InternalConfig: Codable {
|
||||
let address: String?
|
||||
let name: String?
|
||||
let mtu: Int32?
|
||||
}
|
||||
let ServerConfig: InternalConfig
|
||||
}
|
||||
|
||||
// swiftlint:enable identifier_name
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>packet-tunnel-provider</string>
|
||||
|
|
|
|||
|
|
@ -1,39 +1,66 @@
|
|||
import libburrow
|
||||
import NetworkExtension
|
||||
import OSLog
|
||||
import os
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
let logger = Logger(subsystem: "com.hackclub.burrow", category: "General")
|
||||
let logger = Logger(subsystem: "com.hackclub.burrow", category: "frontend")
|
||||
var client: BurrowIpc?
|
||||
var osInitialized = false
|
||||
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
let fild = libburrow.retrieve()
|
||||
if fild == -1 {
|
||||
// Not sure if this is the right way to return an error
|
||||
logger.error("Failed to retrieve file descriptor for burrow.")
|
||||
let err = NSError(
|
||||
domain: "com.hackclub.burrow",
|
||||
code: 1_010,
|
||||
userInfo: [NSLocalizedDescriptionKey: "Failed to find TunInterface"]
|
||||
)
|
||||
completionHandler(err)
|
||||
logger.log("Starting tunnel")
|
||||
if !osInitialized {
|
||||
libburrow.initialize_oslog()
|
||||
osInitialized = true
|
||||
}
|
||||
libburrow.start_srv()
|
||||
client = BurrowIpc(logger: logger)
|
||||
logger.info("Started server")
|
||||
Task {
|
||||
do {
|
||||
let command = BurrowRequest(id: 0, command: "ServerConfig")
|
||||
guard let data = try await client?.request(command, type: Response<BurrowResult<ServerConfigData>>.self)
|
||||
else {
|
||||
throw BurrowError.cantParseResult
|
||||
}
|
||||
let encoded = try JSONEncoder().encode(data.result)
|
||||
self.logger.log("Received final data: \(String(decoding: encoded, as: UTF8.self))")
|
||||
guard let serverconfig = data.result.Ok else {
|
||||
throw BurrowError.resultIsError
|
||||
}
|
||||
guard let tunNs = self.generateTunSettings(from: serverconfig) else {
|
||||
throw BurrowError.addrDoesntExist
|
||||
}
|
||||
try await self.setTunnelNetworkSettings(tunNs)
|
||||
self.logger.info("Set remote tunnel address to \(tunNs.tunnelRemoteAddress)")
|
||||
completionHandler(nil)
|
||||
} catch {
|
||||
self.logger.error("An error occurred: \(error)")
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
logger.info("fd: \(fild)")
|
||||
completionHandler(nil)
|
||||
}
|
||||
|
||||
private func generateTunSettings(from: ServerConfigData) -> NETunnelNetworkSettings? {
|
||||
let cfig = from.ServerConfig
|
||||
guard let addr = cfig.address else {
|
||||
return nil
|
||||
}
|
||||
// Using a makeshift remote tunnel address
|
||||
let nst = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1")
|
||||
nst.ipv4Settings = NEIPv4Settings(addresses: [addr], subnetMasks: ["255.255.255.0"])
|
||||
logger.log("Initialized ipv4 settings: \(nst.ipv4Settings)")
|
||||
return nst
|
||||
}
|
||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
|
||||
if let handler = completionHandler {
|
||||
handler(messageData)
|
||||
}
|
||||
}
|
||||
|
||||
override func sleep(completionHandler: @escaping () -> Void) {
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override func wake() {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
int retrieve();
|
||||
void start_srv();
|
||||
void initialize_oslog();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue