import Foundation import NetworkExtension actor DNSService { enum Event { case packet(Data) } typealias EventHandler = @Sendable (Event) async -> Void private let serverIP: String private let publicDnsServers: [String] private let onEvent: EventHandler private var dnsClient: DNSCloudClient? private var dnsMonitorTask: Task? private var dnsLocalClient: DNSLocalClient? private var dnsLocalMonitorTask: Task? init(serverIP: String, publicDnsServers: [String], onEvent: @escaping EventHandler) { self.serverIP = serverIP self.publicDnsServers = publicDnsServers self.onEvent = onEvent } func start() { self.startCloud() self.startLocal() } func stop() async { let dnsClient = self.dnsClient self.dnsClient = nil let dnsMonitorTask = self.dnsMonitorTask self.dnsMonitorTask = nil let dnsLocalClient = self.dnsLocalClient self.dnsLocalClient = nil let dnsLocalMonitorTask = self.dnsLocalMonitorTask self.dnsLocalMonitorTask = nil dnsMonitorTask?.cancel() dnsClient?.stop() if let dnsMonitorTask { await dnsMonitorTask.value } dnsLocalMonitorTask?.cancel() await dnsLocalClient?.stop() if let dnsLocalMonitorTask { await dnsLocalMonitorTask.value } } func forward(ipPacketData: Data) { self.dnsClient?.forward(ipPacketData: ipPacketData) } func queryLocal(tracker: DNSLocalClient.DNSTracker, dnsPayload: Data) async { await self.dnsLocalClient?.query(tracker: tracker, dnsPayload: dnsPayload) } private func startCloud() { guard self.dnsMonitorTask == nil else { return } self.dnsMonitorTask = startMonitorTask(name: "dnsServiceCloudMonitor") { [weak self] in guard let self else { throw CancellationError() } try await self.runCloud() } } private func startLocal() { guard self.dnsLocalMonitorTask == nil else { return } self.dnsLocalMonitorTask = startMonitorTask(name: "dnsServiceLocalMonitor") { [weak self] in guard let self else { throw CancellationError() } try await self.runLocal() } } private func runCloud() async throws { let dnsClient = DNSCloudClient(serverIP: self.serverIP, port: 15353) self.dnsClient = dnsClient dnsClient.start() defer { dnsClient.stop() if self.dnsClient === dnsClient { self.dnsClient = nil } } let onEvent = self.onEvent try await withTaskCancellationHandler { for try await packet in dnsClient.packetFlow { try Task.checkCancellation() await onEvent(.packet(packet)) } } onCancel: { dnsClient.stop() } } private func runLocal() async throws { let dnsServer = self.publicDnsServers.randomElement() ?? "223.5.5.5" let dnsLocalClient = DNSLocalClient(host: dnsServer) await dnsLocalClient.start() self.dnsLocalClient = dnsLocalClient SDLLogger.log("[DNSService] dnsLocalClient started") defer { if self.dnsLocalClient === dnsLocalClient { self.dnsLocalClient = nil } } let onEvent = self.onEvent do { try await withTaskCancellationHandler { for try await packet in dnsLocalClient.packetFlow { try Task.checkCancellation() await onEvent(.packet(packet)) } } onCancel: { Task { await dnsLocalClient.stop() } } await dnsLocalClient.stop() } catch { await dnsLocalClient.stop() throw error } } }