أنشئ شاشة VPN داخل تطبيق iOS وستكون أول مفاجأة هي أن قائمة البروتوكولات في تطبيق الإعدادات ليست قائمة API. يسمح لك إطار Network Extension بإنشاء إعداد VPN شخصي واحد يملكه ذلك التطبيق، ولا يقبل إلا بضعة كائنات بروتوكول. كل ما يتوقعه الناس سواه، من L2TP إلى ملف تعريف مُنزَّل، يقع خارج تلك السطحية أو يكون خاصًّا بـ Apple. والإخفاقات التي تتبع ليست عادةً أخطاءً برمجية، بل هي API يرفض مهمة لم تُسند إليه قط.

الأسطورة: API ليس محررًا للإعدادات

الاعتقاد السائد أن iOS، بما أنه يستطيع تهيئة ملفات IKEv1 وIKEv2 وL2TP وIPsec، يستطيع أي تطبيق إنشاء أي منها برمجيًّا. وهذا غير صحيح. يكشف الإطار عن خانة إعداد واحدة فقط للتطبيق المستدعي، وتلك الخانة لا تقبل إلا مجموعة صغيرة من فئات البروتوكول العامة. وكل ما يتوقعه الناس سواه، من L2TP إلى ملف تعريف مُنزَّل، يقع خارج تلك السطحية أو يكون خاصًّا بـ Apple.

الإخفاقات التي تتبع ليست عادةً أخطاءً برمجية. بل هي API يرفض مهمة لم تُسند إليه قط، والحل أن تصمّم الشاشة حول ما يملكه API.

ثلاثة كائنات بروتوكول وتلك التي لا وجود لها

خياراتك هي IKEv1 عبر NEVPNProtocolIPSec، وIKEv2 عبر NEVPNProtocolIKEv2، كما يدعم NETunnelProviderProtocol حالتك إن كنت تُضمِّن امتداد نفق حزم خاصًّا بك. وتَسرُد مرجع NEVPNManager كائنات البروتوكول المقبولة. لا يظهر L2TP ولا PPTP في تلك القائمة أبدًا لأن Apple لم تنشر أي فئات لهما. ومحاولة أيٍّ منهما تنتهي عند الحفظ بخطأ، أو الأسوأ، بإعداد يرفض النظام استخدامه بهدوء.

شيفرة سطح المكتب تتبع قواعد مختلفة. في macOS، تُكدِّس SCNetworkInterfaceCreateWithInterface نوع واجهة مثل L2TP فوق IPv4 وتَصِله بخدمة شبكة، ثم يبدأ networksetup -connectpppoeservice النتيجة. ويلزم لذلك صلاحيات مسؤول وأداة مساعدة، لأن السر المشترك وكلمة المرور يدخلان سلسلة مفاتيح النظام. وانقل ذلك التصميم إلى iOS فلن يُترجم شيء.

مرّر مرجع سلسلة مفاتيح، لا كلمة المرور

لا تقبل أي فئة من فئتي IKE كلمة مرور كنص. والخاصيتان passwordReference و sharedSecretReference تطلبان مرجعًا دائمًا في سلسلة المفاتيح، تحصل عليه من SecItemCopyMatching بطلب kSecReturnPersistentRef. وتمرير بيانات العنصر، أو إدخال نص في الحقل بالقوة، يُنتج فشلًا عند الحفظ أو اتصالًا ببيانات اعتماد فارغة. والمرجع ليس أكثر متانة من عنصر سلسلة المفاتيح الذي يقف خلفه، ولهذا يجب أن يوجد العنصر قبل تجميع الإعداد. أنشئه في المعاملة نفسها التي تحفظ بها فتزيل صنفًا كاملًا من أخطاء الترتيب.

المصادقة بالشهادة تسلك مسارًا مختلفًا: identityData مع identityDataPassword لكتلة PKCS#12. وتضمين تلك الكتلة داخل حزمة التطبيق عادةٌ موروثة من الشيفرة النموذجية لا قرار إصدار، لأن كلمة المرور تسافر معها وكل نسخة من التطبيق تحمل الهوية نفسها.

الاستحقاق والتوافر يحكمان كل شيء

قد لا يوجد المدير إطلاقًا، والسبب هو التزويد. يحتاج معرّف تطبيقك إلى قدرة Network Extension مع استحقاق VPN الشخصي، ويجب أن يحمل الملف الذي يوقّع البناء كليهما.

تجاوز ذلك فيُعيد المثيل المشترك nil، أو يفشل الحفظ بخطأ لا يذكر الاستحقاق أبدًا. والتوافر هو البوابة الأخرى. استهدف أي شيء أقدم من iOS 8 فيغيب الرمز وقت التشغيل، فيكون الإخفاق انهيارًا أو مديرًا قيمته nil بدلًا من مدير بلا إعداد. تحقق من زمن التشغيل والاستحقاق قبل تصحيح رسم الكائنات.

حمّل، ثم عدّل، ثم احفظ، ثم راقب ترتيب عند الطلب

تعامل مع دورة الحياة كتحميل ثم تعديل ثم حفظ، ولا تتخطَّ التحميل أبدًا. إلى أن يكتمل loadFromPreferences، لا يحتفظ المدير بشيء ولا يمكن قراءة كائن البروتوكول أو استبداله.

الحفظ الثاني يستبدل الإعداد بدلًا من إضافة آخر، لذا قد يُتلف حفظٌ نصف مكتمل ملفًّا كان يعمل. وقواعد عند الطلب تشكّل مصفوفة مرتبة، وهذا الترتيب هو السياسة. ضع قاعدة اتصال من أي مكان قبل قاعدة الفصل، فيتبع الفصل اليدوي إعادة اتصال فورية، وهو ما يبدو عيبًا لكنه يتبع التقييم الموثّق.

انقل قاعدة الفصل إلى المقدمة عندما يجب أن ينتصر إجراء المستخدم. وما يبقى خارج سيطرة التطبيق لا يقل أهمية. فالمسارات تأتي من النفق أو من سياسة الخادم لا من تفضيل تضبطه أنت، مع مزوّد نفق حزم يبلّغ بها عند بدئه. لا يستطيع التطبيق سرد أو تعديل الملفات التي ثبّتها تطبيقات أخرى أو ملفات الإعداد، فلا تَعِد بشاشة تعرض كل شبكة VPN على الجهاز.

مقالات ذات صلة