Android VpnService की अनुमति-सूची: अनुमत पेज अब भी क्यों अटकते हैं
आपकी अनुमति-सूची संभवतः ठीक है। पेज DNS उत्तर, रूट तालिका, रिज़ॉल्वर सॉकेट या MTU के कारण अटकते हैं। जाँच का क्रम यह है।
आपने एक डोमेन अनुमति-सूची में जोड़ा, क्वेरी आपके ऐप तक पहुँचती है, मैचर अनुमत लौटाता है, और पेज फिर भी कभी लोड नहीं होता। उस समय अधिकतर लोग मैचर दोबारा लिखने और वाइल्डकार्ड तुलना जोड़ने लगते हैं। मेरे अनुभव में मैचर आमतौर पर सही होता है, और विफलता एक परत नीचे बैठती है: आपके बनाए DNS उत्तर में, आपके द्वारा इंस्टॉल किए गए रूट में, या उस सॉकेट में जिससे आपने क्वेरी आगे भेजी। सूची आसान हिस्सा है; पैकेट का रास्ता वह जगह है जहाँ अनुमति-सूचियाँ टूटती हैं।
मैचर ही विरले दोषी होता है
पहले अलग करना यह है कि ब्लॉक किया गया और टूटा हुआ। ब्लॉक किए गए नाम को तेज़ी से और दिखाई देने वाले ढंग से विफल होना चाहिए, क्योंकि ब्राउज़र को उत्तर मिलता है कि नाम मौजूद नहीं है और वह रुक जाता है। टूटा हुआ नाम अटकता है, IPv6 पर दोबारा कोशिश करता है, दूसरे परिवहन पर गिरता है, और आपको घूमते संकेतक के साथ छोड़ देता है। यदि आपको घूमता संकेतक दिखता है, तो आपकी अनुमति-सूची संभवतः ऐसा कुछ उत्तर दे रही है जिस पर क्लाइंट कुछ नहीं कर सकता।
खाली A रिकॉर्ड क्लासिक गलती है। शून्य उत्तरों के साथ NOERROR लौटाना वैध उत्तर जैसा दिखता है, इसलिए stub रिज़ॉल्वर उसे स्वीकार करता है, ब्राउज़र फिर कोशिश करता है, AAAA आज़माता है, दूसरा रिज़ॉल्वर आज़माता है, और बहुत देर से हार मानता है। प्रश्न अनुभाग को दोहराकर और लेन-देन ID की प्रतिलिपि के साथ उत्तर कोड 3, यानी NXDOMAIN, बातचीत तुरंत समाप्त कर देता है। आप जानबूझकर जिन नामों को अस्वीकार करते हैं, उन सबके लिए इसे इस्तेमाल करें।
IPv6 भी डिफ़ॉल्ट के बजाय वही निर्णय का हकदार है। यदि आप A के लिए NXDOMAIN उत्तर देते हैं और 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 आपके लिए क्या नहीं करेगा
इनके आसपास डिज़ाइन करने से पहले दो सीमाएँ जानना उपयोगी है। कोई सामान्य ऐप Wi-Fi के बाहर सिस्टम-व्यापी प्रॉक्सी या PAC फ़ाइल इंस्टॉल नहीं कर सकता, और वह हिस्सा नहीं बदला है। प्रति-ऐप रास्ता बदला है: API 29 से पहले VpnService.Builder प्रॉक्सी बिल्कुल नहीं ले जा सकता था, और API 29 से setHttpProxy(ProxyInfo.buildPacProxy(uri)) प्लेटफ़ॉर्म को VPN नेटवर्क के लिए मूल्यांकन हेतु PAC स्क्रिप्ट देता है। जब आप पुराने API को लक्षित करें या PAC मॉडल नियम व्यक्त न कर सके तो नियम अपने कोड में मूल्यांकित करें। UDP भी मुफ़्त नहीं है: ब्राउज़र SOCKS प्रॉक्सी के ज़रिए केवल TCP भेजते हैं, और अधिकतर SOCKS5 क्लाइंट कभी UDP ASSOCIATE लागू नहीं करते, इसलिए प्रॉक्सी-आधारित डिज़ाइन UDP छोड़ देता है या लीक करता है जबकि TCP स्वस्थ दिखता है।
यही दूसरी सीमा कारण है कि UDP-आधारित प्रोटोकॉल सामान्य नेटवर्क पर काम कर सकता है और उस क्षण विफल हो सकता है जब आपका VPN जुड़ता है। यदि सुरंग से UDP ले जाने की अपेक्षा है, तो फ़ॉरवर्डर को उसके लिए फ़ॉरवर्डिंग पथ लागू करना होगा, केवल दूसरे सॉकेट प्रकार वाला TCP पथ नहीं।
इस क्रम में जाँचें
सूची को क्रम से करें और पहले विफल चरण पर रुकें, क्योंकि पिछला टिके बिना हर बाद की जाँच शोर है।
- इंस्टॉल किए गए रूट सूचीबद्ध करें और पुष्टि करें कि गैर-DNS पैकेट TUN इंटरफ़ेस में प्रवेश करते हैं या नहीं।
- एक ब्लॉक किए गए और एक स्वीकृत नाम के लिए क्वेरी नाम, निर्णय और उत्तर कोड लॉग करें।
- पुष्टि करें कि रिज़ॉल्वर सॉकेट सुरक्षित है और चुना गया रिज़ॉल्वर सुरंग के भीतर से उत्तर देता है।
- इंटरफ़ेस MTU और हाथ से बनाए गए एक उत्तर के चेकसम की पुष्टि करें।
- वही पेज Wi-Fi और मोबाइल नेटवर्क पर दोहराएँ, क्योंकि कैरियर रिज़ॉल्वर और पथ MTU भिन्न होते हैं।
यदि पाँचों टिकें और पेज फिर भी अटके, तो अगला संदिग्ध फ़ॉरवर्डर स्वयं है: पकड़े गए अनुरोध की पकड़े गए उत्तर से तुलना करें और गायब बाइट गणना या आधा-बंद सॉकेट खोजें। VpnService.Builder संदर्भ रूट, DNS और MTU अनुबंधों को कवर करता है, और protect() builder विकल्प नहीं बल्कि अलग VpnService कॉल है; जुड़ा हुआ इंटरफ़ेस केवल यह बताता है कि पहला अनुबंध पूरा हुआ।