اپلیکیشن شما می‌گوید قطع است، نوار منو می‌گوید وصل است، و ترافیک آشکارا در جریان است. این شکاف یک شرایط مسابقه نیست که با تلاش دوباره درست شود: اپلیکیشن و تونل دو فرایند جدا هستند، و شیئی که کد شما می‌خواند یک نمای کش‌شده از پیکربندی‌ای است که سیستم نگه می‌دارد. سطح Personal VPN اپل یک API کنترل است، نه مسیر داده، و بیشتر رفتار عجیب آن از همین یک واقعیت ناشی می‌شود. آنچه در هر فراخوانی واقعاً رخ می‌دهد این است.

شیء مدیر یک نمای کش‌شده است، نه تونل

NETunnelProviderManager از یک مخزن ترجیحات که متعلق به سیستم است پر می‌شود، پس ویژگی‌هایش تا زمانی که loadFromPreferences کامل نشود معنایی ندارند. اگر پیش از آن connection.status را بخوانید، یک مقدار پیش‌فرض می‌گیرید، و به همین دلیل نخستین به‌روزرسانی وضعیت پس از راه‌اندازی مجدد قطع‌بودن را گزارش می‌کند در حالی که تونل بالاست. اول بارگذاری کنید، سپس وضعیت و پیکربندی پروتکل را از همان هندلر تکمیل بخوانید. تغییرات بعدی را به‌جای نظرسنجی از طریق NEVPNStatusDidChange ببینید، و توجه کنید که اعلان برای اتصال‌هایی هم فعال می‌شود که On Demand آغاز کرده است، جایی که هیچ فراخوانی اپلیکیشنی برای بازگرداندن چیزی وجود ندارد. همین برای تنظیمات پروتکل هم صادق است: آن‌ها از پیکربندی بارگذاری‌شده خوانده می‌شوند، نه از آنچه اپلیکیشن وارد کرده است.

خود تونل در یک فرایند ارائه‌دهنده Network Extension زندگی می‌کند، پس اپلیکیشن می‌تواند وضعیت خودش را از دست بدهد بدون اینکه اتصال را از دست بدهد. هر چیزی که رابط نشان می‌دهد باید از مدیر بازسازی شود، نه از نشست قبلی به یاد آورده شود.

ترتیب شروع: ذخیره، بارگذاری، سپس شروع

اتصال مبتنی بر ارائه‌دهنده به سه گام به ترتیب نیاز دارد. پیکربندی ارائه‌دهنده را بنویسید و saveToPreferences را فراخوانی کنید، مدیر را بارگذاری کنید تا پیکربندی ذخیره‌شده همانی باشد که در دست دارید، سپس از طریق NETunnelProviderSession.startVPNTunnel() شروع کنید. اگر ذخیره را رد کنید، شروع چیزی برای راه‌اندازی ندارد؛ اگر پیش از ذخیره بارگذاری کنید، مدیر هنوز پیکربندی قدیمی را بازتاب می‌دهد.

مقدار بولی که startVPNTunnel برمی‌گرداند فقط می‌گوید درخواست پذیرفته شد. خطاهای سطح ارائه‌دهنده بعداً به‌صورت تغییر وضعیت به قطع‌شده می‌رسند، که آن را از طریق اعلان NEVPNStatusDidChange می‌بینید، و هندلر اعلان باید پس از loadFromPreferences دوباره connection.status را بخواند چون مقدار کش‌شده ممکن است هنوز قدیمی باشد. خطاهای سطح درخواست بی‌درنگ از شکل استثنا‌انداز فراخوانی برمی‌گردند یا از طریق پارامتر خروجی خطای startVPNTunnelAndReturnError:. کدی که مقدار بازگشتی را نتیجه می‌داند، اتصالی را گزارش می‌کند که هرگز برقرار نشد. شکست خاموش دیگر، شناسه بستهٔ ارائه‌دهنده است که با افزونه نصب‌شده نمی‌خواند: درخواست رد می‌شود و هیچ چیز در پیام به شناسه اشاره نمی‌کند. اگر شناسه اشتباه باشد، درخواست معمولاً بی‌درنگ شکست می‌خورد، پیش از آنکه هیچ خط لاگی از ارائه‌دهنده ظاهر شود.

بارگذاری دوبارهٔ ترجیحات پس از شروع همان چیزی است که صفحه‌های وضعیت را درست می‌کند. مدیر وقتی سیستم آن را تغییر می‌دهد پیکربندی جدیدی به شما هل نمی‌دهد.

مواد اعتماد از طریق پروفایل می‌آید، هرگز از کد اپلیکیشن

اپلیکیشن هیچ راه پشتیبانی‌شده‌ای برای افزودن گواهی ریشه به مخزن اعتماد iOS ندارد، پس پیکربندی Personal VPN فقط می‌تواند به موادی ارجاع دهد که دستگاه از قبل به آن اعتماد دارد: زنجیره‌ای که با پروفایل نصب‌شده، با مدیریت دستگاه، یا با CA‌ای که سیستم به دلایل دیگر به آن اعتماد دارد برقرار شده است. اپلیکیشنی که در نخستین اجرا CA خودش را می‌سازد و انتظار دارد سیستم آن را بپذیرد، در اعتبارسنجی داخل ارائه‌دهنده شکست می‌خورد، جایی که رابط اپلیکیشن دلیل را نمی‌بیند.

این مرز را همان محدودیت طراحی که هست بپندارید و لنگر اعتماد را همراه پروفایل ثبت ارسال کنید. احراز هویت VPN را از تصمیم اعتماد جدا نگه دارید، چون در جاهای مختلف شکست می‌خورند و افراد مختلفی آن‌ها را اشکال‌زدایی می‌کنند.

شمارنده‌ها از رابط می‌آیند، نه از API VPN

مدیر وضعیت و پیکربندی را آشکار می‌کند و هیچ چیزی که بایت هر تونل را بشمارد. اگر به اعداد مصرف نیاز دارید، getifaddrs() را فراخوانی کنید و if_data را برای رابط مربوط بخوانید: en0 برای Wi-Fi، رابط‌های pdp_ip برای شبکه سلولی و یک دستگاه utun برای خود تونل. شمارنده‌های utun ترافیک درونی را توصیف می‌کنند و سرباز پروتکل را شامل می‌شوند.

این اعداد برآورد هستند، نه حسابداری به‌ازای هر اپلیکیشن، و وقتی رابط دوباره ساخته شود بازنشانی می‌شوند. در رابط همین‌طور برچسب بزنید، وگرنه کسی از آن‌ها برای صورت‌حساب مشتری استفاده می‌کند. اگر اعداد به‌ازای هر اپلیکیشن می‌خواهید، به سازوکاری کاملاً متفاوت نیاز دارید، نه خواندن بهتر این فیلدها.

پاک‌سازی به stopVPNTunnel تعلق دارد، نه به خاتمه

applicationWillTerminate چند ثانیه به شما می‌دهد پیش از آنکه سیستم فرایند را بکشد، که جای مطمئنی برای برچیدن اتصال نیست. توقف را به stopVPNTunnel() بفرستید یا آن را در callback توقف ارائه‌دهنده مدیریت کنید، و شناسه مدیر یا پیکربندی‌ای را که با آن شروع کردید نگه دارید تا توقف همان اتصال را هدف بگیرد نه هر پیکربندی روی دستگاه.

آن شناسه را کنار پروفایلی که به آن تعلق دارد ذخیره کنید، چون شیء مدیر ثبت نمی‌کند کاربر کدام پیکربندی را انتخاب کرده است. و به یاد داشته باشید اتصالی که On Demand بالا می‌آورد هیچ فراخوانی اپلیکیشنی پشت خود ندارد، پس صفحه وضعیت هنوز باید پیش از کشیدن هر چیزی ترجیحات را بارگذاری کند.

مقالات مرتبط