टनल ऊपर आता है, कुंजी का चिह्न दिखता है, और ब्राउज़र में हर अनुरोध अब भी समय समाप्त होकर विफल होता है। VpnService ऐप में यह संयोजन आमतौर पर बताता है कि पढ़ने का लूप जीवित है और आउटपुट आधा टूटा है, यह नहीं कि सर्वर ने हैंडशेक अस्वीकार किया। Android पहले ही इंटरफ़ेस बना चुका है, ऐप का ट्रैफ़िक उसमें ले जा चुका है और आपकी प्रक्रिया को फ़ाइल डिस्क्रिप्टर दे चुका है। उस डिस्क्रिप्टर के बाद सब कुछ आपका कोड है, और एक छूटा हुआ कॉल पूरी धारा को निगलने के लिए काफ़ी है।

सिस्टम आपको क्या देता है और क्या नहीं

establish() एक tun इंटरफ़ेस के लिए ParcelFileDescriptor लौटाता है। सिस्टम बाहर जाने वाले IP पैकेट उसमें लिखता है और अपेक्षा करता है कि आपकी प्रक्रिया उन्हें पढ़े, तय करे कि वे कहाँ जाएँ और सामान्य सॉकेट से भेजे। नीचे कोई कर्नेल फ़ॉरवर्डिंग नहीं है, इसलिए जब कोई नहीं पढ़ता, पैकेट गिरा दिए जाते हैं और इंटरफ़ेस के काउंटर कभी नहीं हिलते।

अनुमान के बजाय गिनती से शुरू करें। हर सफल पठन पर काउंटर बढ़ाएँ और पहले कुछ पैकेट का गंतव्य और प्रोटोकॉल लॉग करें। यदि ट्रैफ़िक बनते समय काउंटर शून्य रहता है, तो गड़बड़ी आपके फ़ॉरवर्डिंग कोड से ऊपर है, जो खोज को बिल्डर कॉन्फ़िगरेशन और पठन मोड तक सीमित करता है। यदि काउंटर हिलता है और ब्राउज़र फिर भी विफल है, तो गड़बड़ी भेजने के रास्ते या वापसी के रास्ते में है।

ब्लॉकिंग पठन तय करता है कि लूप चलेगा या नहीं

setBlocking(true) डिस्क्रिप्टर को ब्लॉकिंग मोड में डालता है और API 21 से मौजूद है। पुराने संस्करणों में उपाय एक छोटा JNI सहायक है जो कच्चे डिस्क्रिप्टर पर fcntl से O_NONBLOCK साफ़ करता है। मोड महत्वपूर्ण है क्योंकि यह तय करता है कि बिना प्रतीक्षारत पैकेट के पठन को क्या करने की अनुमति है।

नॉन-ब्लॉकिंग मोड में वह पठन तुरंत ऋणात्मक मान के साथ लौटता है, और परिणाम अनदेखा करने वाला लूप कोई ट्रैफ़िक हिलाए बिना कोर घुमाता है। ब्लॉकिंग मोड में पठन प्रतीक्षा करता है, जो फ़ॉरवर्डिंग थ्रेड चाहता है, पर तब थ्रेड को रोका नहीं जा सकता। डिस्क्रिप्टर बंद करना पठन को विफल करने का समर्थित तरीका है ताकि लूप बाहर निकल सके। इसके बजाय प्रक्रिया मारना या थ्रेड रोकना अगली शुरुआत को ऐसे डिस्क्रिप्टर के साथ छोड़ सकता है जो अब इंटरफ़ेस से मेल नहीं खाता।

हर कॉल में इंटरफ़ेस के MTU आकार के बफ़र में एक पैकेट पढ़ें, और शून्य या ऋणात्मक रिटर्न को खाली पैकेट नहीं बल्कि त्रुटि मानें।

पहले भेजने से पहले protect(), बाद में नहीं

हर सॉकेट जो एनकैप्सुलेटेड पैकेट ले जाता है, पहले सुरक्षित होना चाहिए। असुरक्षित सॉकेट उन्हीं रूटिंग नियमों का पालन करता है जो आपने अभी लगाए हैं, इसलिए उसके पैकेट टनल में वापस जाते हैं और आपका अपना पठन लूप उन्हें फिर उठा लेता है। तब टनल अपना आउटपुट अनंत काल तक भेजता रहता है, और बाहर से यह ऐसा कनेक्शन दिखता है जो खुलता है और कभी जवाब नहीं देता।

क्रम है protect, फिर connect या bind, फिर send। connect के बाद protect कॉल करने से पहले का रूट निर्णय बना रह सकता है, इसलिए कॉल true लौटाने पर भी सॉकेट गलत रास्ता इस्तेमाल करता रहता है। जो बूलियन यह लौटाता है उसे जाँचें और false होने पर ज़ोर से विफल हों, ऐसा send करने की बजाय जो काम नहीं कर सकता।

जिस सॉकेट ने कभी protect() नहीं छुआ उस पर sendto से आने वाली अनुमति त्रुटि इस गलती की सामान्य पहचान है।

UDP नमूना HTTPS नहीं ले जाता

जो फ़ॉरवर्डिंग नमूना अधिकांश ट्यूटोरियल कॉपी करते हैं वह केवल UDP संभालता है। TCP को हैंडशेक, क्रम संख्याएँ, स्वीकृतियाँ और पुनःप्रेषण चाहिए, और tun डिस्क्रिप्टर तथा सॉकेट के बीच पेलोड कॉपी करने वाला लूप इनमें से कुछ नहीं देता। इसीलिए UDP DNS क्वेरी पार हो सकती है जबकि हर HTTPS अनुरोध अटक जाता है। या तो उपयोगकर्ता अंतरिक्ष में TCP स्टैक चलाएँ या कनेक्शन स्थानीय प्रॉक्सी को दें जो डिवाइस पर TCP समाप्त करता है और धारा आगे भेजता है।

कुछ भी फिर से लिखने से पहले विभाजन जाँचें: एक UDP क्वेरी भेजें और उसी होस्ट पर एक TCP कनेक्शन खोलें। यदि UDP रास्ता काम करता है और TCP रास्ता पहले SYN पर अटकता है, तो समस्या फ़ॉरवर्डिंग कोड में है, सर्वर में नहीं। वह एक तुलना बहुत समय बचाती है जो ऐसे सर्वर लॉग पढ़ने में बर्बाद होता है जिनमें कुछ उपयोगी नहीं होता।

रूट सूची और प्रति-ऐप फ़िल्टर आप जो देखते हैं उसे सीमित करते हैं

addAddress, addRoute और addDns तय करते हैं कि सिस्टम टनल में क्या भेजता है। रूट सूची से बाहर का गंतव्य कभी आपके डिस्क्रिप्टर तक नहीं पहुँचता, इसलिए कोई भी मात्रा में फ़ॉरवर्डिंग तर्क इसे ठीक नहीं करेगा। आप जो बिल्डर कॉल करते हैं उन्हें लॉग करें और उन पतों से तुलना करें जिन्हें ऐप वास्तव में पाने की कोशिश करता है।

प्रति-ऐप चयन की अपनी न्यूनतम सीमा है: addAllowedApplication और addDisallowedApplication को API 21 चाहिए, और पुराने संस्करणों में ये विधियाँ बस नहीं हैं। पैकेट के अनुसार फ़िल्टर करने वाला डिज़ाइन वहाँ बिल्कुल नहीं चल सकता।

पुनःस्थापन पुराने डिस्क्रिप्टर छोड़ जाता है

establish() फिर से कॉल करने पर नया डिस्क्रिप्टर मिलता है और पिछला इंटरफ़ेस बंद हो जाता है। पुराना डिस्क्रिप्टर अब अमान्य है, इसलिए उस पर अब भी ब्लॉक थ्रेड को त्रुटि मिलती है, और पुराने रास्ते के लिए बने सॉकेट बदलने पड़ सकते हैं। onRevoke उस टनल के लिए कॉलबैक है जिसे उपयोगकर्ता या सिस्टम ने छीन लिया। जीवनचक्र एक ही स्वामी के पास रखना, जो दोबारा स्थापित करने से पहले पाठक को रोकता है, ज़्यादातर जुड़ा-पर-मृत स्थितियाँ रोकता है।

हर त्रुटि को उसके चरण के अनुसार पढ़ें। recvfrom से कनेक्शन रीसेट का अर्थ है कि दूर का सिरा बंद हो गया जबकि आपका कोड अब भी पढ़ रहा था। गलत पते की त्रुटि हेडर पार्सिंग या ऑफ़सेट बग की ओर इशारा करती है, नेटवर्क की ओर नहीं।

काम का क्रम: ब्लॉकिंग मोड चालू, प्रति पठन एक पैकेट, हेडर पार्स, सॉकेट सुरक्षित, पेलोड भेजा गया, हैंडओवर पर साफ़ बंद, और गंतव्य को कवर करने वाली रूट सूची।

संबंधित लेख