DNS Proxy extension not launching

Hello!

I created a simple DNS filter application for iOS but the extension is not launching. I am getting this log message in the console.

Failed to start extension edu.stanford.stilakid.testDnsFilter.DNSFiltering: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named edu.stanford.stilakid.testDnsFilter.DNSFiltering.apple-extension-service" UserInfo={NSDebugDescription=connection to service named edu.stanford.stilakid.testDnsFilter.DNSFiltering.apple-extension-service}

For another project with the same code for dns filtering but different bundleID, I also got the following log message.

Failed to start extension edu.stanford.sml.rdahlke.controlShift.DNSProxy: Error Domain=PlugInKit Code=4 "RBSLaunchRequest error trying to launch plugin edu.stanford.sml.rdahlke.controlShift.DNSProxy(D26CD63C-4656-4A30-99A0-7C867265DD75): Error Domain=RBSRequestErrorDomain Code=5 "Launch failed." UserInfo={NSLocalizedFailureReason=Launch failed., NSUnderlyingError=0xc62b8c0d0 {Error Domain=NSPOSIXErrorDomain Code=111 "Unknown error: 111" UserInfo={NSLocalizedDescription=Launchd job spawn failed}}}" UserInfo={NSLocalizedDescription=RBSLaunchRequest error trying to launch plugin edu.stanford.sml.rdahlke.controlShift.DNSProxy(D26CD63C-4656-4A30-99A0-7C867265DD75): Error Domain=RBSRequestErrorDomain Code=5 "Launch failed." UserInfo={NSLocalizedFailureReason=Launch failed., NSUnderlyingError=0xc62b8c0d0 {Error Domain=NSPOSIXErrorDomain Code=111 "Unknown error: 111" UserInfo={NSLocalizedDescription=Launchd job spawn failed}}}}

Also, the log messages I have defined inside the constructor of the dns proxy extension is nowhere to be found in the logs, so I am pretty sure the extension is failing to launch.

The debugger attached to the main target app shows no errors as well, so it is able to load and update dnsProtocol.

Here is the code:

//  DNSProxyProvider.swift
//  DNSFiltering
//
//  Created by Juben Rana on 2/20/24.
//

import NetworkExtension
import os.log

class DNSProxyProvider: NEDNSProxyProvider {
    // MARK: - Logger
    
    static let logger = Logger(subsystem: "edu.stanford.sml.rdahlke.controlShift", category: "dns-filter")
    
    override init() {
        Self.logger.log(level: .debug, "TestDns: dns proxy provider will init")
        self.logger = Self.logger
        super.init()
    }
    
    let logger: Logger
    

    override func startProxy(options:[String: Any]? = nil, completionHandler: @escaping (Error?) -> Void) {
        // Add code here to start the DNS proxy.
        logger.log(level: .debug, "TestDns: proxy will start")
        completionHandler(nil)
    }

    override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        // Add code here to stop the DNS proxy.
        logger.log(level: .debug, "TestDns: proxy will stop")
        completionHandler()
    }

    override func sleep(completionHandler: @escaping () -> Void) {
        // Add code here to get ready to sleep.
        completionHandler()
    }

    override func wake() {
        // Add code here to wake up.
    }

    override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
        // Add code here to handle the incoming flow.
        logger.log(level: .debug, "TestDns: proxy is handling flow")

        return false
    }

}
//  ContentView.swift
//  testDnsFilter
//
//  Created by Juben Rana on 2/20/24.
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
//            LoginScreen()
//                .onOpenURL { url in
//                    GIDSignIn.sharedInstance.handle(url)
//                }
            
            Spacer()
                        
            #if os(macOS)
            Text("I'm running on macOS")
            #else
            Text("I'm running on iOS")
            #endif
            
            Spacer()
            

            Button("Activate") {
                #if os(macOS)
                ContentFilterMac.shared.activate()
                #elseif os(iOS)
                ContentFilter.shared.enable()
                #endif
            }

            Spacer()

                        
            Button("Deactivate") {
                #if os(macOS)
                ContentFilterMac.shared.deactivate()
                #elseif os(iOS)
                ContentFilter.shared.disable()
                #endif
            }
            
            Spacer()
            
            Spacer()

        }
        .padding()
    }
}

#Preview {
    ContentView()
}
//
//  ContentFilter.swift
//  controlShift
//
//  Created by Juben Rana on 9/28/23.
//
//  This is only for macOS

import Foundation
import NetworkExtension
import os.log


// MARK: - Content Filter
class ContentFilter {
    
    // MARK: - Set Up
    
    static let shared = ContentFilter()
    
    
    private init() {
        Self.logger.log(level: .debug, "content filter will init")
        self.logger = Self.logger
    }
    
    
    // MARK: - Logger
    
    
    static let logger = Logger(subsystem: "edu.stanford.stilakid.testDnsFilter", category: "content-filter")
    
    
    
    let logger: Logger
    
    
    // MARK: - DNS Filter
    
    
    private let manager = NEDNSProxyManager.shared()
    
    
    func enable() {
        loadAndUpdatePreferences {
            self.manager.localizedDescription = "DNSProxySample"
            
            let dnsProtocol = NEDNSProxyProviderProtocol()
            dnsProtocol.providerBundleIdentifier = "edu.stanford.stilakid.testDnsFilter.DNSFiltering"
            
            self.manager.providerProtocol = dnsProtocol
            self.manager.isEnabled = true
        }
    }
    
    func disable() {
        loadAndUpdatePreferences {
            self.manager.isEnabled = false
        }
    }
    
    private func loadAndUpdatePreferences(_ completion: @escaping () -> Void) {
        manager.loadFromPreferences { error in
            guard error == nil else {
                debugPrint("DNSProxySample.App: load error")
                return
            }
            
            completion()
            
            self.manager.saveToPreferences { (error) in
                guard error == nil else {
                    debugPrint("DNSProxySample.App: save error")
                    return
                }
                
                debugPrint("DNSProxySample.App: saved")
            }
        }
    }
}

Replies

Error 111 is an internal error meaning ‘bad program’. It suggests that your extension process failed to start. Do you see a crash report for it?

Beyond that, check out my Debugging a Network Extension Provider.

Share and Enjoy

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

@eskimo Here are the crash reports generated today when I ran the program and encountered the same problem.

https://drive.google.com/drive/folders/1eMvYRTuN_FtgcWP1Sf6EDbBirOw34Dg2?usp=sharing

I couldn't attach the crash report directly to this message as it was giving me a weird error that my post contained sensitive information, which isn't true.

Also, here is my code on GitHub for easier reference: https://github.com/stilakid/dnsFilter-test

Your appex crashed with a language exception. Check out the backtrace:

Last Exception Backtrace:
0 CoreFoundation      … __exceptionPreprocess + 164
1 libobjc.A.dylib     … objc_exception_throw + 60
2 CoreFoundation      … -[__NSDictionaryM setObject:forKey:] + 1288
3 ExtensionFoundation … -[EXConcreteExtensionContextVendor _onGlobalStateQueueOnly_setPrincipalObject:forUUID:] + 220
4 ExtensionFoundation … __112-[EXConcreteExtensionContextVendor _beginRequestWithExtensionItems:listenerEndpoint:withContextUUID:completion:]_block_invoke + 724
5 libdispatch.dylib   … _dispatch_call_block_and_release + 32

The exception cames from frame 2. In most cases an exception like this is because someone passed nil to -setObject:forKey:. Now look at the caller, frame 3. The method name is -_onGlobalStateQueueOnly_setPrincipalObject:forUUID:. That suggests that our extension subsystem was unable to instantiate the principle object for your appex. Check that the NSExtension dictionary in your appex’s Info.plist is set correctly, and specifically the NSExtensionPrincipalClass property.

IMPORTANT Make sure you look at the property in the built binary, not the property in your source code. The source code can lie if there’s a build system issue.

Share and Enjoy

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

  • @eskimo please advice on how I can do this (checking the property in the built binary).

Add a Comment

@eskimo please advice on how I can do this (checking the property in the built binary.) Also, let me know if followups like this one should be posted as a reply or a comment to your reply.

If you post replies as comments, I’m not notified of them. See Quinn’s Top Ten DevForums Tips for this and other titbits.

The Info.plist in your NE provider must contain an NSExtension dictionary with (at least) two properties:

  • NSExtensionPointIdentifier — For a DNS proxy, this must be com.apple.networkextension.dns-proxy.

  • NSExtensionPrincipalClass — This is the name of your NEDNSProxyProvider subclass.

The error you’re seeing suggests you have a problem with the latter.

Share and Enjoy

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