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

שלב 1: הוכיחו באיזו רשת האפליקציה נמצאת

קראו את התצוגה של האפליקציה עצמה ולא את שורת המצב. מתוך ConnectivityManager, קבלו את הרשת הפעילה, את LinkProperties שלה, את רשימת שרתי ה-DNS ואת הגדרת ה-DNS הפרטי. אם הרשימה מכילה את ה-resolver של ה-VPN, לאפליקציה יש נתיב אליו; אם היא מכילה את ה-resolver של ה-Wi-Fi, המנהרה אינה רשת ברירת המחדל של אותו תהליך.

סמל VPN אומר רק שקיימת מנהרה. הוא אינו אומר שהתהליך של האפליקציה נקשר אליה, ו-Android לא תמיד מנתב פתרון שמות דרך הרשת שנראית פעילה. אפליקציות VPN מקומיות שמעבירות תנועה להתקן אחר, רשתות Wi-Fi בלי אינטרנט ובנייות של יצרנים יכולות להשאיר את האפליקציה מבקשת את ה-resolver מברירת המחדל במקום את השרתים של המנהרה.

שלב 2: בקשו את תעבורת ה-VPN וקשרו את התהליך

כשהאפליקציה חייבת להשתמש ב-resolver של המנהרה, בקשו רשת עם תעבורת VPN וקשרו את התהליך בתוך ה-callback, כדי שהקוד לא ינחש מתי הרשת מופיעה:

val request = NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .build()

connectivityManager.requestNetwork(request, object : NetworkCallback() {
    override fun onAvailable(network: Network) {
        val bound = connectivityManager.bindProcessToNetwork(network)
        Log.i(TAG, "bound to VPN network: " + bound)
    }
})

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

שלב 3: הגבילו את הקישור למה שצריך אותו

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

שני מאפיינים של ה-resolver יכולים להביס את הקישור בלי שום שגיאה. DNS פרטי במצב מחמיר שולח שאילתות לשרת DoT בשם במקום ל-resolver של הרשת, ואפליקציה שמגיעה עם לקוח DNS-over-HTTPS משלה עוקפת את resolver המערכת לגמרי. בדקו את שניהם לפני שאתם מסיקים שהקישור נכשל. שאילתה שלעולם לא מגיעה לשרת ה-DNS של ה-VPN לא תופיע בשום יומן בצד המנהרה.

שלב 4: אל תסמכו על אילוץ CONNECTED

NetworkType.CONNECTED של WorkManager פירושו שרשת מחוברת, ותעבורת VPN מספקת אותו גם כשקישור ה-Wi-Fi או הסלולר שמתחתיו נעלם. המשימה רצה, כל בקשה נכשלת, ומדיניות הניסיונות החוזרים הופכת אותה לסערה שנראית כמו באג תזמון. המנהרה היא רשת; האילוץ מעולם לא היה בדיקת אינטרנט.

בדקו את היכולת שהעבודה באמת צריכה, כמו גישה מאומתת לאינטרנט, ושמרו על מדיניות ניסיונות חוזרים מוגבלת עם השהיה גדלה. אם המשימה צריכה שם פרטי, פתרו את השם ברשת הנבחרת לפני ביצוע העבודה, והיכשלו מהר עם שגיאה ברורה במקום לנסות שוב בקשה שאינה יכולה להצליח.

שלב 5: תעדו את הראיות ובדקו שוב את הטווח

תעדו את סוגי התעבורה של הרשת הנבחרת, את שרתי ה-DNS מתוך LinkProperties, אם הקישור החזיר אמת, ואת הכשל המדויק של הבקשה הראשונה. אחר כך הריצו שם פרטי ושם ציבורי תחת אותו קישור. הזוג הזה מראה אם האפליקציה השתמשה ב-resolver של המנהרה או נפלה בשקט לברירת המחדל של המכשיר, וזה ההבדל שהמשתמש חווה.

חזרו על זה בגרסת ה-Android הוותיקה ביותר שאתם תומכים בה ובגרסה נוכחית אחת. התנהגות ה-resolver שגורמת לנתיב ברירת המחדל להיראות תקין היא בדיוק המקום שבו מכשירים וגרסאות נבדלים, ובנייה שעוברת רק במכשיר הבדיקה החדש ביותר לא אומתה לטווח שאתם משלחים. adb shell dumpsys connectivity היא דרך מהירה לאשר אילו רשתות ויכולות המכשיר מדווח בזמן שאתם משחזרים את הכשל.

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