मान VpnService.Builder.addDnsServer मा स्ट्रिङको रूपमा पुग्यो र builder ले केही पनि स्थापना हुनुअघि त्यहीँ अस्वीकार गर्यो। यो अस्वीकृति स्थानीय हो: कुनै resolver सँग सम्पर्क भएन, tunnel छैन, र असफल भएको ढाँचा जाँच हो, DNS होइन।

यो महत्त्वपूर्ण छ किनकि सामान्य 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 होइन भनी पुष्टि गर्नुहोस्, र विश्लेषित वस्तु अगाडि पठाउनुहोस्।

सम्बन्धित लेखहरू