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

המתאים הוא לעתים נדירות האשם

הדבר הראשון שצריך להפריד הוא חסום מלשבור. שם חסום אמור להיכשל מהר ובאופן גלוי, כי הדפדפן מקבל תשובה שהשם לא קיים ועוצר. שם שבור נתקע, מנסה שוב عبر IPv6, נופל לתעבורה אחרת ומשאיר אותך עם מחוון מסתובב. אם אתה רואה מחוון מסתובב, רשימת ההיתרים שלך כנראה עונה במשהו שהלקוח לא יכול לעשות איתו דבר.

רשומת A ריקה היא הטעות הקלאסית. החזרת NOERROR עם אפס תשובות נראית כמו תשובה תקפה, ולכן פותר ה-stub מקבל אותה, הדפדפן מנסה שוב, מנסה AAAA, מנסה פותר אחר ומוותר הרבה יותר מאוחר. קוד תשובה 3, כלומר NXDOMAIN, עם חזרה על חלק השאלה והעתקת מזהה העסקה, מסיים את השיחה מיד. השתמש בו לכל שם שאתה דוחה במכוון.

IPv6 ראוי לאותה החלטה ולא לברירת מחדל. אם אתה עונה NXDOMAIN על A ומעביר AAAA, הלקוח יכול להגיע לשם החסום דרך IPv6 והמסנן דולף. יש מימושים שעונים לשאילתות AAAA חסומות בכתובת loopback כדי שהחיבור ייכשל מיד במקום ליפול לחלופה. בחר מדיניות אחת, החל אותה על שני סוגי הרשומות, ורשום ביומן את שניהם.

נתיב שתופס הכול מחזיק הכול

עיצוב של DNS בלבד אף פעם לא מוסיף addRoute("0.0.0.0", 0), ולכן רק שאילתות נכנסות לממשק ה-TUN וכל השאר ממשיך להשתמש ברשת שמתחת. זו הצורה הבטוחה, והיא גם מסבירה למה רשימות היתרים מסוימות נראות עובדות ב-Wi-Fi ונכשלות ברשת הסלולרית.

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

הסוקט של הפותר חייב לצאת מהמנהרה

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

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

גודל וסכומי בדיקה נכשלים בלי הודעה

השלכות שקטות מגיעות גם מגודל. תשובת DNS עם סט תשובות גדול, או לחיצת יד TCP עם אפשרויות נדיבות, יכולה לחרוג מ-MTU של נתיב שמעולם לא הגדרת במפורש; השבר נעלם במעלה הזרם והלקוח או מנסה שוב בקטן יותר או מפסיק לנסות. הגדרת ה-MTU של הממשק ל-1280 היא בחירה שמרנית ששורדת את רוב הנתיבים הסלולריים, ולהיות מפורש עדיף על ירושת ברירת מחדל שאיש לא מדד.

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

מה VpnService לא יעשה בשבילך

שני גבולות כדאי להכיר לפני שמתכננים סביבם. שום אפליקציה רגילה לא יכולה להתקין פרוקסי ברמת המערכת או קובץ PAC מחוץ ל-Wi-Fi, והחלק הזה לא השתנה. המסלול לפי אפליקציה כן: לפני API 29 ‏VpnService.Builder לא יכול היה לשאת פרוקסי בכלל, ומ-API 29 ‏setHttpProxy(ProxyInfo.buildPacProxy(uri)) מוסר לפלטפורמה סקריפט PAC להערכה עבור רשת ה-VPN. הערך כללים בקוד שלך כשאתה מכוון ל-API ישנים יותר או כשמודל ה-PAC לא יכול לבטא את הכלל. גם UDP לא בחינם: דפדפנים שולחים רק TCP דרך פרוקסי SOCKS, ורוב לקוחות SOCKS5 אף פעם לא מיישמים UDP ASSOCIATE, ולכן עיצוב מבוסס פרוקסי משליך או מדליף UDP בזמן ש-TCP נראה בריא.

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

בדוק בסדר הזה

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

  1. פרט את הנתיבים המותקנים ואמת אם חבילות שאינן DNS נכנסות בכלל לממשק ה-TUN.
  2. רשום ביומן את שם השאילתה, ההחלטה וקוד התשובה עבור שם חסום אחד ושם מאושר אחד.
  3. אמת שהסוקט של הפותר מוגן ושהפותר הנבחר עונה מתוך המנהרה.
  4. אמת את ה-MTU של הממשק ואת סכום הבדיקה של תשובה אחת שבנית ביד.
  5. חזור על אותו דף ב-Wi-Fi וברשת הסלולרית, כי פותרים של הספק וה-MTU של הנתיב שונים.

אם כל החמישה מחזיקים והדף עדיין נתקע, החשוד הבא הוא המעביר עצמו: השווה בקשה שנלכדה לתשובה שנלכדה וחפש מספר בתים חסר או סוקט חצי סגור. המדריך של VpnService.Builder מכסה את החוזים של נתיב, DNS ו-MTU, ו-protect() הוא קריאת VpnService נפרדת ולא אפשרות של ה-builder; ממשק מחובר רק אומר שהחוזה הראשון התקיים.

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