أضفت نطاقًا إلى قائمة السماح، ووصل الاستعلام إلى تطبيقك، وأعاد المطابق نتيجة مسموح، ومع ذلك لا تُحمَّل الصفحة أبدًا. عند هذه النقطة يبدأ معظم الناس في إعادة كتابة المطابق وإضافة مقارنات بنجوم بديلة. من تجربتي، يكون المطابق صحيحًا عادةً، ويكون الخلل في طبقة تالية: في إجابة DNS التي صنعتها، أو المسار الذي ثبّتته، أو المقبس الذي استخدمته لتمرير الاستعلام. القائمة هي الجزء السهل؛ مسار الحزمة هو حيث تنكسر قوائم السماح.

نادرًا ما يكون المطابق هو العلّة

أول ما يجب فصله هو الفرق بين محجوب ومعطوب. الاسم المحجوب يجب أن يفشل سريعًا وبشكل مرئي، لأن المتصفح يتلقى إجابة تقول إن الاسم غير موجود فيتوقف. الاسم المعطوب يتعلّق، ويعيد المحاولة عبر IPv6، وينتقل إلى ناقل آخر، ويتركك أمام مؤشر تحميل دوّار. إذا رأيت مؤشرًا دوّارًا، فقائمة السماح لديك على الأرجح تجيب بشيء لا يستطيع العميل التصرف بناءً عليه.

سجل A الفارغ هو الخطأ الكلاسيكي. إعادة NOERROR بلا أي إجابات تبدو ردًا صالحًا، فيقبله محلّل الطرف العميل، ويعيد المتصفح المحاولة، ويجرّب 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 منفصل وليس خيارًا في الباني؛ والواجهة المتصلة تعني فقط أن العقد الأول نجح.

مقالات ذات صلة