VpnService ऐप में स्प्लिट टनलिंग कुछ विशिष्ट जगहों पर टूटती है और हर जगह का उपाय अलग है। क्लाइंट केवल एक विफल गंतव्य दिखाता है, इसलिए पहला काम यह पता लगाना है कि आप किस सीमा से टकरा रहे हैं: builder को मिले रूट, वह प्रॉक्सी जिसका आप प्रमाणीकरण नहीं कर सकते, वह resolver जिसकी आपने केवल घोषणा की, या वह ऐप सूची जो प्लेटफ़ॉर्म प्रति उपयोगकर्ता लागू करता है। ठीक इसी कारण मैं चार-चरणीय जाँच रखता हूँ। यह नीचे दिए क्रम में चलती है और पहली विफलता पर रुक जाती है।

पुष्टि करें कि builder ने प्लेटफ़ॉर्म को क्या दिया

VpnService.Builder घोषणाओं की सूची है। establish() वर्चुअल इंटरफ़ेस के लिए ParcelFileDescriptor लौटाता है, और प्लेटफ़ॉर्म उस डिस्क्रिप्टर में केवल वही ट्रैफ़िक भेजता है जो addAddress और addRoute के प्रीफ़िक्स से मेल खाता है; addDnsServer क्लाइंट को बताता है कि किस resolver से पूछना है। आपके जोड़े किसी भी प्रीफ़िक्स के बाहर का गंतव्य आपके ऐप तक कभी नहीं पहुँचता, इंटरफ़ेस जो भी वादा करे। यही एक तथ्य कोड बदलने से पहले ही टूटी स्प्लिट टनलिंग की अधिकांश शिकायतें समझा देता है।

अपने builder ने जो पता, रूट और DNS सूचियाँ बनाईं उन्हें लॉग करें, फिर उनकी तुलना विफल गंतव्यों से करें। प्रबंधन होस्ट के लिए एक गुम /32 टूटी टनल से कहीं अधिक आम है। यदि सूची सही है तो आगे बढ़ें; यदि गलत है तो बाकी चेकलिस्ट व्यर्थ श्रम है।

excludeRoute केवल API 33 से मौजूद है

गंतव्य-आधारित बहिष्करण वह अनुरोध है जो सबसे अधिक सपोर्ट थ्रेड बनता है। builder.excludeRoute(IpPrefix) API 33 में आया; उससे पहले एकमात्र समर्थित तरीका यह है कि बहिष्कृत प्रीफ़िक्स के अलावा सब कुछ के लिए रूट जोड़ें, यानी पूरक प्रीफ़िक्स आपको स्वयं गणना करने होंगे। दोनों कॉल दर्पण-प्रतिबिंब नहीं हैं: एक इंटरफ़ेस के रूट-सेट से घटाता है, दूसरा सेट शून्य से बनाता है, और पुराना डिवाइस अज्ञात कॉल को फेंक देगा या चुपचाप अनदेखा कर देगा। Build.VERSION.SDK_INT पर शाखा बनाएँ और दोनों रास्तों को वास्तविक हार्डवेयर पर जाँचें।

पूरकों की गणना करते समय प्रीफ़िक्स संरेखण पर ध्यान दें। /16 रूट के भीतर /24 बहिष्करण के लिए वे आवरण प्रीफ़िक्स चाहिए जो शेष भाग का वास्तव में वर्णन करें, कोई विकृत /8 नहीं। अंतिम सूची लॉग करें और पुष्टि करें कि बहिष्कृत होस्ट अब भी टनल के बाहर के रास्ते से हल होता है।

प्रॉक्सी क्रेडेंशियल व्यक्त नहीं किए जा सकते

setHttpProxy(ProxyInfo) इंटरफ़ेस को HTTP या SOCKS प्रॉक्सी की ओर ले जाता है, और PAC URL प्रति गंतव्य उनके बीच चुन सकता है। न ProxyInfo और न PAC प्रारूप उपयोगकर्ता नाम और पासवर्ड रखते हैं, इसलिए प्रमाणीकरण वाली प्रॉक्सी प्लेटफ़ॉर्म API के बाहर रहती है। जिन ऐप्स को इसकी आवश्यकता होती है वे सामान्यतः userspace स्टैक, tun2socks या समान इंजन जोड़ते हैं और 407 चुनौती का उत्तर स्वयं देते हैं।

इस चयन के परिणाम हैं। userspace स्टैक केवल वही ट्रैफ़िक देखता है जो आप उसकी ओर रूट करते हैं, इसलिए उसे उसी डिस्क्रिप्टर सीमा के भीतर रहना होता है, और उसे अपने uplink सॉकेट के लिए वही protect() आवश्यकता विरासत में मिलती है। protect() छोड़ दें तो टनल अपना ही ट्रांसपोर्ट पकड़ लेती है, जो पहले पैकेट के बाद अटक जाने वाले हैंडशेक के रूप में दिखता है। ऐसी प्रॉक्सी के विरुद्ध जाँचें जो क्रेडेंशियल माँगती है, क्योंकि खुली प्रॉक्सी प्लेटफ़ॉर्म के रास्ते पार हो जाती है और कुछ सिद्ध नहीं करती।

प्रति-ऐप सूचियाँ और DNS भी घोषणाएँ हैं

Builder में addAllowedApplication और addDisallowedApplication दोनों हैं, और ये परस्पर विरोधी हैं: दोनों कॉल करने पर अपवाद फेंका जाता है। किसी भी स्थिति में सूची इंटरफ़ेस सेटअप पर प्रति उपयोगकर्ता लागू होती है, इसलिए कार्य प्रोफ़ाइल का ऐप आपकी टनल के बजाय कार्य प्रोफ़ाइल की टनल से नियंत्रित होता है। कोई API आपको दूसरे ऐप के निर्णय पर नियंत्रण नहीं देता: आप बाहर से पैकेट को अपनी टनल में नहीं धकेल सकते, और पहले से सक्रिय VPN को बंद या पुनःकॉन्फ़िगर नहीं कर सकते।

DNS वही पैटर्न अपनाता है। addDnsServer resolver का पता घोषित करता है; यह आपके ऐप को प्रश्नों के उत्तर देने नहीं देता। यदि आप स्वयं नाम हल करना चाहते हैं तो resolver चलाएँ और उसका पता टनल में रूट करें। जो ऐप्स किसी निश्चित होस्टनाम पर DoT या DoH उपयोग करते हैं वे आपके घोषित पते को बायपास कर सकते हैं, इसलिए नाम-रिज़ॉल्यूशन को सर्वोत्तम प्रयास मानें जब तक कि आप प्रश्न का रास्ता भी इंटरसेप्ट न करें।

मैनिफ़ेस्ट, always-on और चार-चरणीय परीक्षण

पात्रता मैनिफ़ेस्ट में घोषित होती है: BIND_VPN_SERVICE, android.net.VpnService इंटेंट फ़िल्टर, और foreground सेवा यदि टनल को पृष्ठभूमि में बने रहना है। Always-on VPN को अतिरिक्त रूप से स्वच्छ onRevoke चाहिए, क्योंकि सिस्टम इसे तब कॉल करता है जब उपयोगकर्ता सेटिंग्स बदलता है या कोई दूसरा ऐप नियंत्रण ले लेता है।

जाँचें क्रम से चलाएँ और पहली विफलता पर रुकें। पहले, builder को दी गई पता, रूट और DNS सूचियाँ निकालें। दूसरे, पुष्टि करें कि establish() ने डिस्क्रिप्टर लौटाया और ट्रांसपोर्ट सॉकेट सुरक्षित है। तीसरे, सम्मिलित प्रीफ़िक्स के भीतर एक और बहिष्कृत प्रीफ़िक्स के भीतर एक गंतव्य माँगें। चौथे, यह जाँचें कि वास्तव में किस resolver ने उत्तर दिया, ऐसा होस्टनाम पूछकर जिसे केवल आपका resolver ही हल कर सकता है।

पूर्ण हुआ हैंडशेक बाद के किसी भी चरण को सिद्ध नहीं करता। हर जाँच एक परत अलग करती है, इसलिए दूसरे चरण की सफलता चौथे के बारे में कुछ नहीं कहती।

VpnService.Builder संदर्भ builder की पूरी सतह सूचीबद्ध करता है। इसके आगे जो कुछ चाहिए वह दूसरा API, दूसरा ऐप या आपका अपना userspace स्टैक है, और वह स्प्लिट-टनल बग नहीं बल्कि डिज़ाइन निर्णय है।

संबंधित लेख