addDnsServer Bad Address: mida Android tegelikult kontrollib
addDnsServeri Bad address viga on kohalik vormikontroll, mitte DNS katkestus. Analüüsi literaali, eemalda loopback ja wildcard, seejärel testi kättesaadavust.
Väärtus jõudis VpnService.Builder.addDnsServer-ni stringina ja builder lükkas selle seal tagasi enne, kui midagi loodi. See tagasilükkamine on kohalik: ühtegi resolverit ei kutsutud, tunnelit pole ja ebaõnnestus vormikontroll, mitte DNS.
See loeb, sest tüüpilisel VPN-rakendusel on kolm sarnast DNS-sisendit: profiili trükitud või imporditud väärtus, serveri saadetud väärtus ja Private DNS süsteemi tasemel. Ainult kaks esimest jõuavad sellesse kutsesse.
Ettevalmistusobjekt kontrollib argumenti kohe
VpnService.Builder ei ole tunnel. See on ettevalmistusobjekt, mis kogub aadresse, marsruute ja servereid, ja ainult establish() muudab need loendid elavaks ParcelFileDescriptor-ks ja liideseks. DNS-kirje kontrollitakse addDnsServer kutsumisel, mitte loomisel.
Järgneb kaks asja. Stack trace on lühike ja meelega mitteinformatiivne: väärtus saabus lihtsa stringina. Ja hooletu catch kogu ehituse ümber jätab tunneli, millel on aadressid ja marsruudid, aga resolver puudub.
Builder hoiab iga loendit, mille talle annad. Kontrolli läbiva serveri lisamine tõestab ainult, et string analüüsiti ehitamise ajal aadressi literaalina.
Mida argument olla võib
Parameeter on aadress, mitte endpoint. Builderi viide võtab IPv4 või IPv6 literaali ja analüüs on ainult numbriline: android.net.InetAddresses.parseNumericAddress on selle parseri avalik nägu alates API 29. Hostinimi pole siin aeglane aadress, vaid string, mida ei saa analüüsida.
Praktiline vastendus näeb välja nii:
| Input | Result | Reason |
|---|---|---|
10.8.0.1 | accepted | IPv4 literal |
2606:4700:4700::1111 | accepted | IPv6 literal |
dns.example.com | rejected | hostname; numeric parsing does not resolve it |
10.8.0.1:53 | rejected | port belongs to your own config model |
https://dns.example.com/dns-query | rejected | DoH URL, a different configuration path |
127.0.0.1 | rejected | loopback is not a remote resolver |
0.0.0.0 or :: | accepted by the builder | parses fine, but names no destination; only your own validator rejects it |
Versioon mõjutab seda, kuidas valideerid, mitte seda, mida builder aktsepteerib. InetAddresses on saadaval alates API 29, seega Android 10 ja uuemal on analüüs üks kutse. addDnsServer(InetAddress) on algne vorm, String uuem lisand. Alla API 29 lahendab InetAddress.getByName hostinime blokeeriva võrguotsinguga ja muudab trükivea aeglaseks käivituseks.
Loopback lükatakse siin tagasi, wildcard mitte
Töölaua VPN-kliendid suunavad oma stub resolveri sageli 127.0.0.1:53-le. Selle Android-profiili kopeerimine impordib väärtuse, mis kirjeldab kohalikku masinat, ja kohalikku masinat siin pole. Builder lükkab tagasi ainult selle juhtumi: null- või loopback-aadress viskab IllegalArgumentException.
Wildcard on vastupidine juhtum ja siin läheb enamik selle vea lugemisi valesti. 0.0.0.0 ja :: ei ole loopback, seega builder aktsepteerib neid; ehitamisel ei visata midagi. Liides tõuseb kirjega, mis ei nimeta sihtkohta. Selle püüdmine on sinu valideerija töö: käsitle isAnyLocalAddress kehtetu sisendina.
Analüüsi üks kord konfiguratsiooni piiril
Odav lahendus on valideerida seal, kus string sinu mudelisse siseneb:
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-tulemusest saab väljatasandi viga, mis kannab süüdlase teksti, ja see on ainus vorm, millega tugi saab tegutseda. Õnnestumise teel läheb objekt otse builder.addDnsServer(address)-sse. Püüa IllegalArgumentException konkreetselt, mitte Exception: lai püüdmine neelab ka nullosuti vead.
Ära asenda avaliku resolveriga, kui soovitud väärtus tagasi lükatakse. Profiil, mis vaikselt avaliku DNS-i paneb, ühendub endiselt, aga sisemised nimed lakkavad lahendamast.
Kinnita kättesaadavus, kui liides on olemas
Builderi heakskiit on esimene kontrollpunkt, mitte viimane. Kui liides on püsti, näitab LinkProperties.getDnsServers(), mida võrk tegelikult sai. adb shell dumpsys connectivity trükib samad omadused väljastpoolt.
Private DNS on eraldi juhtimine ja ei käi selle argumendi kaudu. DoT-pakkuja hostinimi kuulub süsteemiseadesse, mitte IP-väljale.
Enne kui profiili parserit uuesti muudad, tööta selles järjekorras: logi toores string ja selle allikas, kinnita, et see on literaal ja mitte hostinimi, et see pole loopback ega wildcard, ja anna analüüsitud objekt edasi.