मान VpnService.Builder.addDnsServer तक स्ट्रिंग के रूप में पहुँचा और builder ने कुछ भी स्थापित होने से पहले वहीं उसे अस्वीकार कर दिया। यह अस्वीकृति स्थानीय है: किसी resolver से संपर्क नहीं हुआ, कोई tunnel नहीं है और जो विफल हुआ वह फ़ॉर्मैट जाँच है।

यह मायने रखता है क्योंकि सामान्य VPN ऐप में तीन समान DNS इनपुट होते हैं: प्रोफ़ाइल में लिखा मान, कॉन्फ़िगरेशन के समय सर्वर भेजता मान, और सिस्टम स्तर पर Private DNS। केवल पहले दो ही इस कॉल तक पहुँच सकते हैं।

तैयारी ऑब्जेक्ट तर्क की जाँच तुरंत करता है

VpnService.Builder tunnel नहीं है। यह पते, रूट और सर्वर इकट्ठा करने वाला तैयारी ऑब्जेक्ट है, और केवल establish() ही उन सूचियों को जीवंत ParcelFileDescriptor और इंटरफ़ेस में बदलता है। DNS प्रविष्टि addDnsServer कॉल करते समय जाँची जाती है, स्थापित करते समय नहीं।

दो परिणाम आते हैं: स्टैक ट्रेस छोटा और जानबूझकर अनुपयोगी है, और लापरवाह catch पते व रूट वाला पर resolver रहित tunnel छोड़ देता है।

builder आपकी दी हुई कोई भी सूची रखता है; जाँच पास करने वाला सर्वर जोड़ना केवल यह सिद्ध करता है कि स्ट्रिंग निर्माण के समय पता लिटरल के रूप में विश्लेषित हुई।

तर्क क्या हो सकता है

पैरामीटर पता है, endpoint नहीं। Builder संदर्भ IPv4 या IPv6 लिटरल लेता है, और विश्लेषण केवल संख्यात्मक है: android.net.InetAddresses.parseNumericAddress API 29 से उस पार्सर का सार्वजनिक चेहरा है।

व्यावहारिक मैपिंग ऐसी दिखती है:

InputResultReason
10.8.0.1acceptedIPv4 literal
2606:4700:4700::1111acceptedIPv6 literal
dns.example.comrejectedhostname; numeric parsing does not resolve it
10.8.0.1:53rejectedport belongs to your own config model
https://dns.example.com/dns-queryrejectedDoH URL, a different configuration path
127.0.0.1rejectedloopback is not a remote resolver
0.0.0.0 or ::accepted by the builderparses fine, but names no destination; only your own validator rejects it

संस्करण इस पर असर डालता है कि आप कैसे सत्यापित करते हैं, इस पर नहीं कि builder क्या स्वीकार करता है। InetAddresses API 29 से उपलब्ध है, इसलिए Android 10 में विश्लेषण एक कॉल है; addDnsServer(InetAddress) मूल रूप है, String नया जोड़। API 29 के नीचे InetAddress.getByName अवरोधक नेटवर्क खोज से होस्टनाम हल करेगा।

loopback यहाँ अस्वीकार होता है, wildcard नहीं

डेस्कटॉप VPN क्लाइंट अपने stub resolver को 127.0.0.1:53 पर भेजते हैं; इसे Android प्रोफ़ाइल में कॉपी करने से स्थानीय मशीन बताने वाला मान आयात होता है, और यहाँ स्थानीय मशीन नहीं है। builder केवल यही स्थिति अस्वीकार करता है: null या loopback पता IllegalArgumentException फेंकता है।

wildcard विपरीत स्थिति है और यहीं अधिकांश पाठ गलत होते हैं। 0.0.0.0 और :: loopback नहीं हैं, इसलिए builder उन्हें स्वीकार करता है; निर्माण के समय कुछ नहीं फेंका जाता। फिर इंटरफ़ेस ऐसी प्रविष्टि के साथ उठता है जो कोई गंतव्य नहीं बताती। इसे पकड़ना आपके वैलिडेटर का काम है: isAnyLocalAddress को अमान्य इनपुट मानें।

कॉन्फ़िगरेशन की सीमा पर एक बार विश्लेषण करें

सस्ता उपाय यह है कि स्ट्रिंग आपके मॉडल में जहाँ प्रवेश करती है वहीं सत्यापित करें:

import android.net.InetAddresses
import java.net.InetAddress

private fun dnsLiteralOrNull(raw: String?): InetAddress? {
    val text = raw?.trim().orEmpty()
    if (text.isEmpty()) return null
    val address = try {
        InetAddresses.parseNumericAddress(text)
    } catch (e: IllegalArgumentException) {
        return null
    }
    return address.takeUnless { it.isAnyLocalAddress || it.isLoopbackAddress }
}

null परिणाम दोषी पाठ वाली फ़ील्ड-स्तरीय त्रुटि बनता है और समर्थन केवल इसी रूप पर कार्य कर सकता है। सफल पथ में ऑब्जेक्ट सीधे builder.addDnsServer(address) में जाता है, और IllegalArgumentException विशेष रूप से पकड़ें, Exception नहीं।

जब इच्छित मान अस्वीकृत हो तो सार्वजनिक resolver से न बदलें; चुपचाप सार्वजनिक DNS डालने वाली प्रोफ़ाइल जुड़ती है, पर आंतरिक नाम हल होना बंद हो जाते हैं।

इंटरफ़ेस बनने के बाद पहुँच की पुष्टि करें

builder की स्वीकृति पहला जाँच-बिंदु है, अंतिम नहीं। इंटरफ़ेस उठने के बाद LinkProperties.getDnsServers() बताता है कि नेटवर्क को वास्तव में क्या मिला, और adb shell dumpsys connectivity बाहर से वही गुण छापता है।

Private DNS अलग नियंत्रण है और इस तर्क से नहीं गुजरता; DoT प्रदाता का होस्टनाम सिस्टम सेटिंग का है, IP फ़ील्ड का नहीं।

प्रोफ़ाइल पार्सर फिर बदलने से पहले कच्ची स्ट्रिंग और उसका स्रोत लॉग करें, पुष्टि करें कि वह होस्टनाम नहीं लिटरल है, loopback या wildcard नहीं है, और विश्लेषित ऑब्जेक्ट आगे भेजें।

संबंधित लेख