Android VpnService विषयीच्या चार गृहीतांनी मला एकदा आठवड्याचा मोठा भाग खर्च करायला लावला, आणि त्यातली एकही नेटवर्क प्रोटोकॉलविषयी नव्हती। ॲप म्हणत होते जोडले आहे, स्टेटस बारमध्ये किल्लीचा चिन्ह बसले होते, आणि ट्रॅफिक तरीही नेहमीच्या राउटनेच बाहेर जात होते। मी बोगदा माझाच आहे असे गृहीत धरत राहिलो जोपर्यंत मी तो थांबवत नाही, मी जोडलेले राउट हेच मला मिळणारे राउट आहेत, आणि stopSelf बोलावल्यास इंटरफेसही सोबत खाली येईल। सेवा अगदी दस्तऐवजात असल्याप्रमाणे वागत होती। तुटलेला भाग माझे मानसिक मॉडेल होते।

मिथ 1: बोगदा तुम्ही तो थांबवेपर्यंत उभा राहतो

Android एका वेळी एकच VPN इंटरफेस ठेवते। दुसरा उभारा आणि प्लॅटफॉर्म तो रांगेत ठेवत नाही, तुमचा निष्क्रिय करतो आणि तुमच्या सेवेवर onRevoke() बोलावतो। तुमचा कोड फक्त बटण दाबल्यावर सर्व उतरवत असेल, तर ताबा घेणे मागे एक डिस्क्रिप्टर आणि एक रीडर थ्रेड सोडते, आणि पुढील सुरुवात दोन्ही वारसा म्हणून घेते।

VpnService संदर्भ स्पष्ट सांगतो की ॲपकडे आधीच संमती असेल तर prepare() null परत करते, आणि वापरकर्त्याने अजून मंजूरी द्यायची असेल तर Intent परत करते। ती संमती तुम्ही हेतू startActivityForResult() मार्गे पोहोचवला तरच येते। हस्तांतरण वगळा आणि establish() null परत करते, जे परवानगीची अडचण नव्हे तर शांत अपयश म्हणून वाचले जाते।

Always-on VPN तुम्ही नियंत्रित न करता अशा मालकाची भर घालते। सिस्टम बूटनंतर किंवा क्रॅशनंतर बोगदा परत आणू शकते, आणि वापरकर्त्याने हाताने सुरू केलेल्या VPN ॲपला ती मागे टाकते। onRevoke() अशा प्रकारे बनवा की तो तुमच्या नेहमीच्या बंद करण्याइतकेच संसाधने बंद करेल, आणि त्याकडे कडक बाजूचा प्रसंग नव्हे तर मुख्य थांबवण्याचा मार्ग म्हणून पाहा। वापरकर्ते VPN ॲप बदलतात; तुमच्या कोडला ते टिकून राहावे लागते।

मिथ 2: addRoute नेमके तुम्ही सूचीबद्ध केलेलेच पकडते

कोणती पॅकेट तुमच्या डिस्क्रिप्टरपर्यंत पोहोचतात हे राउट ठरवतात, आणि व्याप्तीची चूक अशी चिडवणारी स्थिती निर्माण करते जिथे थोडा ट्रॅफिक बोगद्यातून जातो आणि थोडा जात नाही। addRoute("0.0.0.0", 0) सर्व IPv4 घेते, म्हणजे आता प्रत्येक पॅकेट तुमच्याकडे येते आणि ते पुढे पाठवावे लागते किंवा टाकून द्यावे लागते। एका होस्टसाठी /32 जवळपास काहीच घेत नाही, आणि बाकी सर्व तुमचे VPN कधीच इन्स्टॉल झालेच नाही असे वागते।

कुटुंबे वेगळी मोजली जातात। फक्त IPv4 असलेला राउट IPv6 अंतर्निहित नेटवर्कवर सोडतो, त्यामुळे दुहेरी-स्टॅक होस्ट बोगद्याच्या आत पूर्ण जोडलेला दिसत असूनही बाहेर नाव सोडवू शकतो आणि जोडू शकतो।

DNS तेच तर्क अनुसरते। addDnsServer() ने जोडलेले सर्व्हर बोगद्याचा रिझोल्व्हर वापरणाऱ्या ॲपकडे जातात, तर स्वतःचा एन्क्रिप्टेड DNS एंडपॉइंट किंवा होस्टनावावर सेट केलेला Private DNS असलेला ॲप त्यांना पूर्णपणे दुर्लक्षितो। जेव्हा एका ॲपसाठी नावे अयशस्वी होतात आणि दुसऱ्यासाठी चालतात, तेव्हा एकही राउटला हात लावण्यापूर्वी रिझोल्व्हरचा मार्ग तपासा।

मिथ 3: प्रति-ॲप नियम हा दोन्ही बाजूंना फिरवता येणारा स्विच आहे

addDisallowedApplication() आणि addAllowedApplication() दोन गाळण्या नव्हे, दोन मोड आहेत। बिल्डर प्रति बोगदा एकच मॉडेल स्वीकारतो, त्यामुळे तीन पॅकेजेस सोडून सर्व हवे असलेले ॲप प्रतिबंधित यादी वापरते आणि परवानगी दिलेली यादी रिकामी ठेवते। दोन्ही एकमेकांना वगळणारे आहेत, आणि लोकांना हे रनटाइममध्ये कळते तेव्हा सेटिंग्ज स्क्रीन आधीच दोन्ही टॉगल देऊन चुकलेली असते।

पॅकेज दृश्यमानताही चावते। Android 11 आणि त्यानंतर तुम्ही फक्त दिसणाऱ्या पॅकेजेसची नावे देऊ शकता, त्यामुळे तुमच्या ॲपला चालवायला परवानगी नसलेल्या क्वेरीतून बनवलेली वगळण्याची यादी शांतपणे नोंदी चुकवेल। यादीवर विश्वास ठेवण्यापूर्वी पॅकेज मॅनेजर प्रत्यक्षात काय परत करतो याशी ती तुलना करा।

वर्क प्रोफाइल प्रेक्षकांची फेरवाटणी करतात। प्रोफाइलमध्ये सुरू केलेला बोगदा ते प्रोफाइल आणि त्याची ॲप व्यापतो, तर वैयक्तिक बाजू आपले जोडणी टिकवते। हा एकदा घेतला जाणारा व्याप्तीचा निर्णय आहे, आणि त्याचे स्थान सपोर्ट तिकिटात नव्हे तर इंटरफेसमध्ये आहे।

मिथ 4: stopSelf बोगदा संपवते

नाही। इंटरफेस establish() परत करतो त्या डिस्क्रिप्टरमध्ये राहतो, आणि तो डिस्क्रिप्टर उघडा असतानाच सेवा ऑब्जेक्ट मरू शकतो। आधी ParcelFileDescriptor बंद करा, संदर्भ सोडा, रीडर आणि रायटर थ्रेड जोडा किंवा व्यत्यय आणा, मग stopForeground(true) आणि stopSelf() त्या क्रमाने बोलवा।

विसरलेला डिस्क्रिप्टर हे सेवा स्वतःच्या थांबवण्याच्या विनंतीतून टिकून राहिली असे वाटण्याचे नेहमीचे कारण आहे, आणि ते तिला जोडणाऱ्या ॲपपेक्षा जास्त जगणारे जुने राउट स्पष्ट करते।

उलटी चूकही तितकीच सामान्य आहे: रीडर थ्रेड वाचनाच्या मध्यभागी असताना डिस्क्रिप्टर बंद करणे, आणि मग त्या थ्रेडला कोणीही कोड न लिहिलेला अपवाद हाताळायला सोडणे। शटडाउन क्रम एकाच ठिकाणी ठेवा आणि वापरकर्त्याची क्रिया व onRevoke() दोन्ही त्यात बोलवू द्या।

बोगदा मेला वाटतो तेव्हा काय तपासावे

बोगदा वर येण्यापूर्वी दीर्घायुषी सॉकेट protect() ने संरक्षित करा। त्याशिवाय बोगद्याचे स्वतःचे वाहतूक पुन्हा बोगद्यात राउट होते आणि जोडणी सर्व्हर बिघाडासारखीच दिसणाऱ्या पद्धतीने अडकते। फक्त दूरच्या नेटवर्कच्या मागे असलेल्या पत्त्याविरुद्ध चाचणी करा, कॅशेमधून किंवा अंतर्निहित मार्गातून उत्तर देणाऱ्या सार्वजनिक साइटविरुद्ध नाही।

establish() चा निकाल, राउट, वगळलेली पॅकेजेस आणि बंद करण्याचे कारण नोंदवा। त्या चार ओळी पॅकेट कॅप्चरशिवाय संमतीची अडचण व्याप्तीच्या अडचणीपासून आणि जीवनचक्राच्या अडचणीपासून वेगळी करतात।

संबंधित लेख