import AsyncAlgorithms import BurrowConfiguration import BurrowCore import libburrow import NetworkExtension import os class PacketTunnelProvider: NEPacketTunnelProvider { enum Error: Swift.Error { case missingTunnelConfiguration } private static let logger = Logger.logger(for: PacketTunnelProvider.self) override init() { do { libburrow.spawnInProcess( socketPath: try Constants.socketURL.path(percentEncoded: false), databasePath: try Constants.databaseURL.path(percentEncoded: false) ) } catch { Self.logger.error("Failed to spawn networking thread: \(error)") } } nonisolated override func startTunnel(options: [String: NSObject]? = nil) async throws { do { let client = try TunnelClient.unix(socketURL: Constants.socketURL) let configuration = try await Array(client.tunnelConfiguration(.init()).prefix(1)).first guard let settings = configuration?.settings else { throw Error.missingTunnelConfiguration } try await setTunnelNetworkSettings(settings) _ = try await client.tunnelStart(.init()) Self.logger.log("Started tunnel with network settings: \(settings)") } catch { Self.logger.error("Failed to start tunnel: \(error)") throw error } } nonisolated override func stopTunnel(with reason: NEProviderStopReason) async { do { let client = try TunnelClient.unix(socketURL: Constants.socketURL) _ = try await client.tunnelStop(.init()) Self.logger.log("Stopped client") } catch { Self.logger.error("Failed to stop tunnel: \(error)") } } } extension Burrow_TunnelConfigurationResponse { fileprivate var settings: NEPacketTunnelNetworkSettings { let ipv6Addresses = addresses.filter { IPv6Address($0) != nil } let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1") settings.mtu = NSNumber(value: mtu) settings.ipv4Settings = NEIPv4Settings( addresses: addresses.filter { IPv4Address($0) != nil }, subnetMasks: ["255.255.255.0"] ) settings.ipv6Settings = NEIPv6Settings( addresses: ipv6Addresses, networkPrefixLengths: ipv6Addresses.map { _ in 64 } ) return settings } }