אתה צריך מסך אחד שיתנהג אחרת כשהמנהרה פעילה, ואין קריאה שאומרת לך את זה. זו מגבלת ההפצה: ב-macOS שום מסגרת ציבורית לא מדווחת אם VPN כלשהו מחובר, וב-iOS המצב שייך לאפליקציה שהתקינה את התצורה. הבדיקות שלמטה עוברות מהמגבלה הזו למשהו שאפשר להפיץ, וכל אחת מהן יכולה לסיים את החיפוש מוקדם.

בדיקה 1: האם בכלל אפשר לענות על השאלה?

ה-sandbox מכריע את השאלה לפני כל API. תהליך של צד שלישי לא יכול למנות את התוסף של ספק אחר, וגם לא לקרוא את התצורה של אותו תוסף, כך שאין אובייקט נתמך לשאול.

כל עקיפה נתקלת באותה חומה. רשימות ממשקים, scutil --nc list וקריאות ישירות של תצורת המערכת דורשות או הרשאות שה-sandbox מונע או התנהגות שאפל מעולם לא הבטיחה. אם התוכנית צריכה אחד מאלה, הבעיה היא התוכנית. תכונה שנבנתה על פרט לא מתועד היא כרטיס תמיכה עם תאריך אספקה.

בדיקה 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: מה יוריסטיקה באמת תגיד לך?

הגישה המועתקת בהרחבה שואלת את מילון ה-proxy של המערכת על הערכים המוגבלים שלו וקוראת את המפתחות, שבמקרה הם שמות ממשקים:

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 פרטי שנשבר בהפצה הבאה.

מאמרים קשורים