Reikšmė pasiekė VpnService.Builder.addDnsServer kaip eilutė, ir builder ją ten atmetė, kol dar niekas nebuvo sukurta. Šis atmetimas yra vietinis: joks resolver nebuvo iškviestas, tunelio nėra, o nepavyko formato patikra, ne DNS.

Tai svarbu, nes įprasta VPN programėlė turi tris panašius DNS įėjimus: reikšmę profilyje, serverio siunčiamą reikšmę ir Private DNS sistemos lygiu. Tik pirmieji du pasiekia šį kvietimą.

Paruošiamasis objektas argumentą tikrina iškart

VpnService.Builder nėra tunelis. Tai paruošiamasis objektas, renkantis adresus, maršrutus ir serverius, ir tik establish() paverčia tuos sąrašus gyvu ParcelFileDescriptor ir sąsaja. DNS įrašas tikrinamas kviečiant addDnsServer.

Dvi pasekmės: iškvietimo pėdsakas trumpas ir tyčia neinformatyvus, o nerūpestingas catch palieka tunelį be resolver.

Builder išsaugo bet kurį sąrašą; serverio, kuris praeina patikrą, pridėjimas tik įrodo, kad eilutė konstravimo metu buvo išnagrinėta kaip adreso literalas.

Kas gali būti argumentas

Parametras yra adresas, ne endpoint. Builder nuoroda priima IPv4 arba IPv6 literalą, o analizė yra tik skaitinė: android.net.InetAddresses.parseNumericAddress nuo API 29 yra viešasis to parserio veidas.

Praktinis atitikimas atrodo taip:

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

Versija veikia tai, kaip jūs tikrinate, o ne tai, ką priima builder. InetAddresses prieinamas nuo API 29, todėl Android 10 analizė yra vienas kvietimas; addDnsServer(InetAddress) yra pirminė forma, String naujesnis papildymas. Žemiau API 29 InetAddress.getByName išspręs hostname blokuojančia tinklo paieška.

Loopback čia atmetamas, wildcard ne

Darbalaukio VPN klientai dažnai nukreipia savo stub resolver į 127.0.0.1:53; nukopijavus tai į Android profilį įvedama reikšmė, apibūdinanti vietinę mašiną, o vietinės mašinos čia nėra. Builder atmeta tik šį atvejį: null arba loopback adresas meta IllegalArgumentException.

Wildcard yra priešingas atvejis ir būtent čia dauguma šios klaidos skaitymų klysta. 0.0.0.0 ir :: nėra loopback, todėl builder juos priima; konstravimo metu nieko nemetama. Tada sąsaja pakyla su įrašu, kuris nenurodo jokios paskirties. Tai pagauti yra jūsų validatoriaus darbas: laikykite isAnyLocalAddress neteisingu įėjimu.

Analizuokite vieną kartą konfigūracijos riboje

Pigiausia priemonė yra tikrinti ten, kur eilutė patenka į jūsų modelį:

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 rezultatas tampa lauko lygio klaida su kaltu tekstu, ir tai vienintelė forma, kuria gali veikti palaikymas. Sėkmės kelyje objektas patenka tiesiai į builder.addDnsServer(address), o IllegalArgumentException gaudykite konkrečiai, ne Exception.

Nekeiskite viešuoju resolver, kai norima reikšmė atmetama; profilis, tyliai įdedantis viešą DNS, prisijungia, bet vidiniai vardai nustoja spręstis.

Patvirtinkite pasiekiamumą, kai sąsaja jau yra

Builder priėmimas yra pirmas kontrolinis taškas, ne paskutinis. Kai sąsaja pakyla, LinkProperties.getDnsServers() rodo, ką tinklas iš tikrųjų gavo, o adb shell dumpsys connectivity išspausdina tas pačias savybes iš išorės.

Private DNS yra atskira kontrolė ir neina per šį argumentą; DoT teikėjo hostname priklauso sistemos nustatymui, ne IP laukui.

Prieš vėl keisdami profilio parserį, užregistruokite neapdorotą eilutę ir jos šaltinį, patvirtinkite, kad tai literalas, o ne hostname, kad tai ne loopback ir ne wildcard, ir perduokite išnagrinėtą objektą.

Susiję straipsniai