تشخیص VPN فعال درون یک برنامه Mac سندباکسشده
هیچ API عمومی برای این پرسش وجود ندارد و ترفند نام رابط تنها یک اکتشافی است. شش بررسی برای اینکه تصمیم بگیرید برنامهتان واقعاً باید به چه چیزی وابسته باشد.
به یک صفحه نیاز دارید که وقتی تونل برپاست رفتار متفاوتی داشته باشد، و هیچ فراخوانیای نیست که این را به شما بگوید. این همان محدودیت عرضه است: در macOS هیچ چارچوب عمومیای گزارش نمیدهد که آیا یک VPN دلخواه متصل است یا نه، و در iOS وضعیت متعلق به برنامهای است که پیکربندی را نصب کرده است. بررسیهای زیر از این محدودیت به چیزی میرسند که میتوانید عرضه کنید، و هر یک از آنها میتواند جستوجو را زودتر پایان دهد.
بررسی 1: آیا اصلاً میتوان به این پرسش پاسخ داد؟
سندباکس پیش از هر APIای این پرسش را حل میکند. یک فرایند شخص ثالث نمیتواند افزونه فروشنده دیگری را برشمارد و نمیتواند پیکربندی آن افزونه را هم بخواند، پس هیچ شیء پشتیبانیشدهای برای پرسیدن وجود ندارد.
هر راه دورزدنی به همان دیوار میخورد. فهرستهای رابط، scutil --nc list و خواندنهای مستقیم پیکربندی سیستم یا به مجوزهایی نیاز دارند که سندباکس نگه میدارد یا به رفتاری وابستهاند که اپل هرگز وعده نداده است. اگر طرح به یکی از اینها نیاز دارد، مشکل خود طرح است. قابلیتی که بر جزئیاتی مستندنشده بنا شده، یک تیکت پشتیبانی با تاریخ تحویل است.
بررسی 2: آیا برنامه شما خودش تونل را مدیریت میکند؟
اگر چنین است، برای پیکربندی خودتان پاسخی پشتیبانیشده دارید. ترجیحات را بارگذاری کنید، خطا را مدیریت کنید، سپس وضعیت را بخوانید و بهجای نظرسنجی دورهای در تغییرات مشترک شوید:
vpnManager.loadFromPreferences { error in
// handle a load failure before reading status
}
NotificationCenter.default.addObserver(
forName: .NEVPNStatusDidChange,
object: nil,
queue: nil
) { note in
let status = (note.object as? NEVPNConnection)?.status
}دقیق باشید درباره آنچه این برمیگرداند: وضعیت تونلی که خودتان نصب کردهاید. کاربری که کلاینت دیگری اجرا میکند بهصورت قطعشده نمایش داده میشود، و این باگ کد شما نیست. پیش از ساختن بر پاسخ، تصمیم بگیرید قابلیت شما واقعاً کدام یک از این دو پرسش را میپرسد.
بررسی 3: یک اکتشافی واقعاً چه چیزی به شما میگوید؟
رویکردی که بسیار کپی میشود از فرهنگ لغت پروکسی سیستم مدخلهای دامنهدارش را میپرسد و کلیدها را میخواند، که تصادفاً نام رابطها هستند:
let settings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() as NSDictionary?
let scoped = settings?["__SCOPED__"] as? [String: Any]
let names = scoped?.keys ?? []کلیدی که با tap، tun، ppp، ipsec یا utun شروع شود نشان میدهد یک رابط با دامنه تونل وجود دارد. تطبیق نام کامل فوراً شکست میخورد، چون iOS 12 با utun1 و utun2 آمد و بعضی پروفایلها بهصورت ipsec4، ppp0 یا utun0 ظاهر میشوند. بهجای آن پیشوندها را مقایسه کنید.
با این حال، یک نام هیچ مالکی را شناسایی نمیکند. قابلیتهای سیستم و افزونههای فروشندگان دیگر هم رابطهای utun میسازند، و یکی میتواند وجود داشته باشد بیآنکه هیچ بخشی از ترافیک شما را حمل کند. آن را نشانهای بخوانید که چیزی شبیه تونل در نزدیکی هست، نه قویتر. مثبت کاذب در اینجا اگر فقط یک قابلیت را مشروط کنید ارزان است، و اگر صفحه ورود را مشروط کنید گران.
بررسی 4: آیا میتوانید بهجای آن مقصد را آزمایش کنید؟
اگر برنامه فقط باید بداند یک سرویس داخلی در دسترس است یا نه، همان سرویس را آزمایش کنید. یک کاوش کوتاه با مهلت زمانی به پرسشی پاسخ میدهد که قابلیت واقعاً به آن وابسته است، و وقتی کاربر کلاینت را عوض میکند یا تونل شکلش را تغییر میدهد همچنان کار میکند.
دسترسپذیری یک مشاهده درباره مسیر شماست. نام رابط یک استنتاج درباره چیزی مجاور مسیر شماست، و به همین دلیل اکتشافی در گزارشهای باگ بحث میسازد و کاوش شواهد. اگر کاوش شکست بخورد در حالی که کاربر اصرار دارد VPN روشن است، اکنون مسیر مشخصی برای اشکالزدایی دارید: تحلیل نام، مسیر یا مجوز سرویس.
بررسی 5: آیا پلتفرم میتواند آنچه را لازم دارید اعمال کند؟
وقتی الزام، تضمینی است که چیزی از تونل فرار نکند، جای پروفایل همانجاست، نه آشکارساز. روشن کردن includeAllNetworks در یک پیکربندی تونل کامل در iOS 14 یا جدیدتر رفتار پلتفرم را تغییر میدهد بهجای آنکه از برنامه شما بخواهد آن را استنتاج کند. این پرچم در مرجع NEVPNProtocol مستند شده است.
این نوع دیگری از پاسخ است: اعمال بهجای مشاهده. همچنین فقط برای پروفایلهایی اعمال میشود که خودتان کنترل میکنید، پس به برنامه مصرفکنندهای که تونل دیگری را تماشا میکند کمکی نمیکند.
بررسی 6: شکست را ایمن و قابل مشاهده کنید
فقط قابلیتی را خاموش کنید که به تونل نیاز دارد و بقیه برنامه را قابل استفاده بگذارید. آشکارسازی که هرگاه مطمئن نیست کل رابط را پنهان میکند، برای هر کسی که تونلش نامی را به کار میبرد که هرگز پیشبینی نکردهاید یک قطعی میسازد. بهجای آن به رفتار محافظهکارانه برگردید: قابلیتهای محلی کار میکنند و فقط فراخوانی راه دوری که واقعاً به تونل نیاز دارد غیرفعال میشود. این انتخاب یک تصمیم محصول است و شایسته است کنار اکتشافی نوشته شود.
آنچه مشاهده کردید ثبت کنید: نامهای رابط، نسخه پلتفرم و نتیجه یک کاوش. این گزارش بعدی را از بحث به بررسی تبدیل میکند. سپس هرجا اعلان پشتیبانیشدهای دارید آن را ترجیح دهید، چون یک پیکربندی مدیریتشده میتواند قصد را صریح حمل کند و بیشتر دلیل بررسی نامها را از میان بردارد.
اگر بررسیهای 1 تا 5 شما را بدون پاسخ پشتیبانیشده گذاشتند، کاوش را عرضه کنید و نتیجه را صادقانه برچسب بزنید. یک سیگنال مستند تلاش حداکثری تصمیم محصول بهتری است از یک API خصوصی که در انتشار بعدی میشکند.