Title: DNS Proxy Not Capturing Traffic When Public DNS Is Set in WiFi Settings

I'm working on a Network Extension using NEDNSProxyProvider to inspect DNS traffic. However, I've run into a couple of issues:

DNS Proxy is not capturing traffic when a public DNS (like 8.8.8.8 or 1.1.1.1) is manually configured in the WiFi settings. It seems like the system bypasses the proxy in this case. Is this expected behavior? Is there a way to force DNS traffic through the proxy even if a public DNS is set?

Using DNS Proxy and DNS Settings simultaneously doesn't work. Is there a known limitation or a correct way to combine these?

How to set DNS or DNSSettings using DNSProxy?

import NetworkExtension
import SystemExtensions
import SwiftUI

protocol DNSProxyManagerDelegate {
    func managerStateDidChange(_ manager: DNSProxyManager)
}

class DNSProxyManager: NSObject {
    
    private let manager = NEDNSProxyManager.shared()
    var delegate: DNSProxyManagerDelegate?
    private(set) var isEnabled: Bool = false {
        didSet {
            delegate?.managerStateDidChange(self)
        }
    }
    
    var completion: (() -> Void)?
    
    override init() {
        super.init()
        self.load()
    }
    
    func toggle() {
        isEnabled ? disable() : start()
    }
    
    private func start() {
        let request = OSSystemExtensionRequest
            .activationRequest(forExtensionWithIdentifier: Constants.extensionBundleID,
                               queue: DispatchQueue.main)
        
        request.delegate = self
        OSSystemExtensionManager.shared.submitRequest(request)
        log.info("Submitted extension activation request")
    }
    
    private func enable() {
        update {
            self.manager.localizedDescription = "DNS Proxy"
            let proto = NEDNSProxyProviderProtocol()
            proto.providerBundleIdentifier = Constants.extensionBundleID
            self.manager.providerProtocol = proto
            self.manager.isEnabled = true
        }
    }
    
    private func disable() {
        update {
            self.manager.isEnabled = false
        }
    }
    
    private func remove() {
        update {
            self.manager.removeFromPreferences { _ in
                self.isEnabled = self.manager.isEnabled
            }
        }
    }
    
    
    private func update(_ body: @escaping () -> Void) {
        self.manager.loadFromPreferences { (error) in
            if let error = error {
                log.error("Failed to load DNS manager: \(error)")
                return
            }
            
            self.manager.saveToPreferences { (error) in
                if let error = error {
                    return
                }
                log.info("Saved DNS manager")
                self.isEnabled = self.manager.isEnabled
            }
        }
    }
    
    private func load() {
        manager.loadFromPreferences { error in
            guard error == nil else { return }
            self.isEnabled = self.manager.isEnabled
        }
    }
    
}

extension DNSProxyManager: OSSystemExtensionRequestDelegate {
    
    
    func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
        log.info("Extension activation request needs user approval")
    }
    
    func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
        log.error("Extension activation request failed: \(error)")
    }
    
    func request(_ request: OSSystemExtensionRequest, foundProperties properties: [OSSystemExtensionProperties]) {
        log.info("Extension activation request found properties: \(properties)")
    }
    
    func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
        guard result == .completed else {
            log.error("Unexpected result \(result.description) for system extension request")
            return
        }
        
        log.info("Extension activation request did finish with result: \(result.description)")
        enable()
        
    }
    
    func request(_ request: OSSystemExtensionRequest, actionForReplacingExtension existing: OSSystemExtensionProperties, withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
        log.info("Existing extension willt be replaced: \(existing.bundleIdentifier) -> \(ext.bundleIdentifier)")
        return .replace
    }
    
}
import NetworkExtension

class DNSProxyProvider: NEDNSProxyProvider {
    
    var handlers: [String: FlowHandler] = [:]
    var isReady = false
    let queue = DispatchQueue(label: "DNSProxyProvider")
    
    override func startProxy(options:[String: Any]? = nil, completionHandler: @escaping (Error?) -> Void) {
        completionHandler(nil)
    }
    
    override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        completionHandler()
    }
    
    override func handleNewUDPFlow(_ flow: NEAppProxyUDPFlow, initialRemoteEndpoint remoteEndpoint: NWEndpoint) -> Bool {
        
        let id = shortUUID()
        handlers[id] = FlowHandler(flow: flow, remoteEndpoint: remoteEndpoint, id: id, delegate: self)
        
        return true
    }
    
    override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
        return false
    }
}
class FlowHandler {

    let id: String
    let flow: NEAppProxyUDPFlow
    let remoteEndpoint: NWHostEndpoint
    let delegate: FlowHandlerDelegate
    private var connections: [String: RemoteConnection] = [:]
    private var pendingPacketsByDomain: [String: [(packet: Data, endpoint: NWEndpoint, uniqueID: String, timestamp: Date)]] = [:]
    private let packetQueue = DispatchQueue(label: "com.flowhandler.packetQueue")

    init(flow: NEAppProxyUDPFlow, remoteEndpoint: NWEndpoint, id: String, delegate: FlowHandlerDelegate) {
        log.info("Flow received for \(id) flow: \(String(describing: flow))")
        self.flow = flow
        self.remoteEndpoint = remoteEndpoint as! NWHostEndpoint
        self.id = id
        self.delegate = delegate
        defer { start() }
    }

    deinit {
        closeAll(nil)
    }

    func start() {
        flow.open(withLocalEndpoint: flow.localEndpoint as? NWHostEndpoint) { error in
            if let error = error {
                self.delegate.flowClosed(self)
                return
            }
            
            self.readFromFlow()
        }
    }

    func readFromFlow() {
        self.flow.readDatagrams { packets, endpoint, error in
            if let error = error {
                self.closeAll(error)
                return
            }

            guard let packets = packets, let endpoints = endpoint, !packets.isEmpty, !endpoints.isEmpty else {
                self.closeAll(nil)
                return
            }

            self.processFlowPackets(packets, endpoints)
            self.readFromFlow()
        }
    }
}

Any insights or suggestions would be greatly appreciated. Thanks!

Answered by DTS Engineer in 835399022
Is this expected behavior?

Yes. The system has a bias towards using secure DNS where possible, and those services provide secure DNS. I touch on this in this thread, with its reference to RFC 9462.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Is this expected behavior?

Yes. The system has a bias towards using secure DNS where possible, and those services provide secure DNS. I touch on this in this thread, with its reference to RFC 9462.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you for the clarification! If we set Public DNS in WiFi settings we can not capture network traffic in NEDNSProxyProvider.

Title: DNS Proxy Not Capturing Traffic When Public DNS Is Set in WiFi Settings
 
 
Q