Xamarin iOS VPN app extension not starting packet tunnel provider class

garrow mobile 96 Reputation points
2021-10-04T16:47:15.383+00:00

I am developing a VPN app for iOS using Xamarin. As per iOS docs there needs to be an app extension which would finally connect VPN to a remote server.

Question 1. Can I create an app extension supporting 'Packet Tunnel' in Xamarin on Mac OS X using Visual Studio code. When I try to create an app extension in the wizard there is no VPN extension option. I created an Action Sheet extension and manually modified Info.plist and Entitlement.plist file to support VPN a hack.

With above changes I was able to create an app extension and was able to deploy on a real device. ow the main iOS app was able to save VPN profile and I could see it in settings. My current app extension tunnel provider class is empty I just have simple log lines.

Question 2. I was expecting I would get some logs and it would eventually fail as there is no code, but my class is never called. There is no stacktrace. Only thing I could extract from console connecting to real device is as follows.

Unhandled Exception:
Foundation.MonoTouchException: Objective-C exception thrown. Name: NSInvalidArgumentException Reason: *** -[__NSDictionaryM setObject:forKey:]: object cannot be nil (key: 7E3DD3B9-285A-4EB1-9DCA-6EC5B89214DA)
Native stack trace:
    0  CoreFoundation           0x00000001b130a9a4 <redacted> + 252
    1  libobjc.A.dylib           0x00000001b04e39f8 objc_exception_throw + 56
    2  CoreFoundation           0x00000001b12843f8 _CFArgv + 0
    3  CoreFoundation           0x00000001b11f9228 <redacted> + 940
    4  Foundation             0x00000001b1d288b4 <redacted> + 100
    5  Foundation             0x00000001b1d28124 <redacted> + 764
    6  libdispatch.dylib          0x00000001b0d48a38 <redacted> + 24
    7  libdispatch.dylib          0x00000001b0d497d4 <redacted> + 16
    8  libdispatch.dylib          0x00000001b0cf2324 <redacted> + 592
    9  libdispatch.dylib          0x00000001b0cf2e40 <redacte<…>

I am starting tunnel as follows. The below code is inside NETunnelProviderManager.LoadAllFromPreferences completion handler.

var activationAttemptId = new NSUuid().AsString() + "-SECRET-VPN";
Console.WriteLine("Activation UUID " + activationAttemptId);
var options = new NSDictionary<NSString, NSObject>((NSString)NSObject.FromObject("activationAttemptId"), NSObject.FromObject(activationAttemptId));
var sessionConnection = tunnelProviderManager.Connection as NETunnelProviderSession;

NSError terror;

sessionConnection.StartTunnel(options, out terror);

if (terror != null && terror.Code != null)
{
       Console.Error.WriteLine("While starting VPN error ...." + terror.Code);
}

My configuration saving code is as follows.

var tunnelProviderManager = new NETunnelProviderManager();
tunnelProviderManager.LocalizedDescription = "Second Xam VPN";


var protocolConfiguration = new NETunnelProviderProtocol();
protocolConfiguration.ProviderBundleIdentifier = "xxx.xxx";
protocolConfiguration.ServerAddress = "XXX:XXX";
protocolConfiguration.ProviderConfiguration = new NSDictionary<NSString, NSObject>();

tunnelProviderManager.ProtocolConfiguration = protocolConfiguration;

tunnelProviderManager.Enabled = true;

tunnelProviderManager.SaveToPreferences((NSError error) => {// ...logic here .....});

I'll really appreciate If some guidance is provided how to tackle the issue may be some debug help, I have intermediate skills with Xamarin and overall mobile development.

Both apps and apps extension Info.plist and Entitlement.plist have correct entitlements.

One thing would like to share Visual Studio for Mac 2019 adds incorrect entitlement when graphical editor is used. It adds value for key com.apple.developer.networking.networkextension as packet-tunnel instead of packet-tunnel-provider.

Developer technologies | .NET | Xamarin
0 comments No comments
{count} votes

Accepted answer
  1. garrow mobile 96 Reputation points
    2021-10-06T04:37:06.13+00:00

    Two issues.

    1. I still do not agree the value for key com.apple.developer.networking.networkextension is packet-tunnel. You cannot deploy to a real device with that value. Even you download provisional certificate from Apple developer website with which you are signing and open it in text editor though it is a binary file, it clearly states the value as packet-tunnel-provider, how come then packet-tunnel works is a mystery to me. I lost 2 weeks for it.
    2. As I mentioned I changed 'Action Sheet' extension to 'Network Extension', during the convert process I missed adding [Register] annotation on my PacketTunnelProvider class that I registered as the class supporting extension code. Probably NSDictionary nil error was related to it, not sure.

    Now extension class is called, constructor and StartTunnel method is called and I can see logs in console app still not in 'Application Output', may be it will show logs for main app only.


1 additional answer

Sort by: Most helpful
  1. Wenyan Zhang (Shanghai Wicresoft Co,.Ltd.) 36,436 Reputation points Microsoft External Staff
    2021-10-05T03:17:15.677+00:00

    Hello,
    Welcome to our Microsoft Q&A platform!

    [__NSDictionaryM setObject:forKey:]: object cannot be nil means that inserting nil into NSDictionary caused a crash, NSDictionary in Objective-C does not support nil as a key or value.

    NSDictionary is used in two places:

    var options = new NSDictionary<NSString, NSObject>((NSString)NSObject.FromObject("activationAttemptId"), NSObject.FromObject(activationAttemptId));  
    protocolConfiguration.ProviderConfiguration = new NSDictionary<NSString, NSObject>();  
    

    Howerver, I can't reproduce the issue, I also change NSDictionary to NSMutableDictionary, the demo doesn't crash. You could check if NSDictionary is used in other places and make a breakpoint to determine which line of the code caused this crash. I suspect that something went wrong when you modified Info.plist and Entitlement.plist file,because there are also Dictionary in these two files , you could check it.

    Another Issue
    It adds value for key com.apple.developer.networking.networkextension as packet-tunnel instead of packet-tunnel-provider.
    When you work with Network Extensions, packet-tunnel will be added, my VS for Mac version is 8.10.10 . For more information, you can refer to
    https://learn.microsoft.com/en-us/xamarin/ios/deploy-test/provisioning/capabilities/?tabs=macos

    137605-%E6%88%AA%E5%B1%8F2021-10-05-%E4%B8%8A%E5%8D%88110854.png
    137586-%E6%88%AA%E5%B1%8F2021-10-05-%E4%B8%8A%E5%8D%88110910.png

    ------Update-----

    About permission denied this issue, you need to check the APP bundle and profile, this doc shows how to work with Capabilities in Xamarin.iOS

    1. Go to Appledeveloper portal
    2. Select Identifiers => App IDs
    3. Select your app
    4. Click edit button, check the network-extension checkbox

    My Test

    using NetworkExtension;  
    
    
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate  
        {  
            public override bool FinishedLaunching(UIApplication app, NSDictionary options)  
            {  
                global::Xamarin.Forms.Forms.Init();  
                LoadApplication(new App());  
                test();  
                //test1();  
                return base.FinishedLaunching(app, options);  
            }  
            public void test()  
            {  
                var activationAttemptId = new NSUuid().AsString() + "-SECRET-VPN";  
                Console.WriteLine("Activation UUID " + activationAttemptId);  
                var options = new NSMutableDictionary<NSString, NSObject>((NSString)NSObject.FromObject("activationAttemptId"), NSObject.FromObject(activationAttemptId));  
                Console.WriteLine("options " + options);  
            }  
            public void test1()  
            {  
                var tunnelProviderManager = new NETunnelProviderManager();  
                tunnelProviderManager.LocalizedDescription = "Second Xam VPN";  
      
      
                var protocolConfiguration = new NETunnelProviderProtocol();  
                protocolConfiguration.ProviderBundleIdentifier = "xxx.xxx";  
                protocolConfiguration.ServerAddress = "XXX:XXX";  
                protocolConfiguration.ProviderConfiguration = new NSDictionary<NSString, NSObject>();  
      
                tunnelProviderManager.ProtocolConfiguration = protocolConfiguration;  
      
                tunnelProviderManager.Enabled = true;  
      
                tunnelProviderManager.SaveToPreferences((NSError error) => {// ...logic here .....  
                                                                            //  
                                                                             });  
      
                }  
        }  
    

    Make sure check NetworkExtensions in VS, and provisioning profile is right.
    137672-image.png

    Best Regards,
    Wenyan Zhang


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.