addDnsServer Bad Address: čo Android skutočne overuje
Chyba Bad address z addDnsServer je lokálna kontrola formátu, nie výpadok DNS. Rozparsujte literál, odstráňte loopback a wildcard a potom otestujte dostupnosť.
Hodnota dorazila do VpnService.Builder.addDnsServer ako reťazec a builder ju tam odmietol skôr, než sa čokoľvek ustavilo. Odmietnutie je lokálne: žiadny resolver nebol kontaktovaný, tunel neexistuje a zlyhala kontrola formátu, nie DNS.
To je dôležité, lebo typická VPN aplikácia má tri podobné vstupy DNS: hodnotu v profile, hodnotu odoslanú serverom a Private DNS na úrovni systému. K tomuto volaniu sa dostanú len prvé dva.
Prípravný objekt skontroluje argument okamžite
VpnService.Builder nie je tunel. Je to prípravný objekt, ktorý zhromažďuje adresy, trasy a servery, a len establish() mení tieto zoznamy na živý ParcelFileDescriptor a rozhranie. Záznam DNS sa kontroluje pri volaní addDnsServer.
Dva dôsledky: stopa je krátka a zámerne neinformatívna a nedbalý catch nechá tunel bez resolvera.
Builder si ponechá každý zoznam; pridanie servera, ktorý prejde kontrolou, dokazuje len to, že reťazec bol pri zostavení rozparsovaný ako literál adresy.
Čím môže byť argument
Parameter je adresa, nie endpoint. Referencia Builder prijíma literál IPv4 alebo IPv6 a parsovanie je len číselné: android.net.InetAddresses.parseNumericAddress je verejná tvár tohto parsera od API 29.
Praktické mapovanie vyzerá takto:
| 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 |
Verzia ovplyvňuje, ako validujete, nie to, čo builder prijme. InetAddresses je dostupný od API 29, takže na Androide 10 je parsovanie jedno volanie; addDnsServer(InetAddress) je pôvodná forma, String novší doplnok. Pod API 29 InetAddress.getByName vyrieši názov hostiteľa blokujúcim sieťovým dopytom.
Loopback sa tu odmieta, wildcard nie
Desktopoví VPN klienti často smerujú svoj stub resolver na 127.0.0.1:53; kopírovanie do profilu Android vnesie hodnotu opisujúcu lokálny stroj a lokálny stroj tu nie je. Builder odmieta len tento prípad: null alebo loopback adresa vyhodí IllegalArgumentException.
Wildcard je opačný prípad a práve tu sa väčšina čítaní tejto chyby mýli. 0.0.0.0 a :: nie sú loopback, takže builder ich prijme; pri zostavení sa nič nevyhodí. Rozhranie potom naskočí so záznamom, ktorý neuvádza cieľ. Zachytiť to je práca vášho validátora: berte isAnyLocalAddress ako neplatný vstup.
Parsujte raz na hranici konfigurácie
Lacnou opravou je validovať tam, kde reťazec vstupuje do vášho modelu:
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 výsledok sa stane chybou na úrovni poľa s vinným textom a to je jediná forma, na ktorú môže podpora reagovať. Na úspešnej ceste ide objekt priamo do builder.addDnsServer(address) a IllegalArgumentException chytajte konkrétne, nie Exception.
Nenahrádzajte verejným resolverom, keď je zamýšľaná hodnota odmietnutá; profil, ktorý ticho dosadí verejný DNS, sa pripojí, ale interné názvy prestanú fungovať.
Potvrďte dostupnosť, keď rozhranie existuje
Prijatie builderom je prvý kontrolný bod, nie posledný. Keď rozhranie naskočí, LinkProperties.getDnsServers() ukáže, čo sieť skutočne dostala, a adb shell dumpsys connectivity vypíše rovnaké vlastnosti zvonku.
Private DNS je samostatné nastavenie a neprechádza týmto argumentom; názov hostiteľa DoT poskytovateľa patrí do systémového nastavenia, nie do poľa IP.
Pred ďalšou zmenou parsera profilu zalogujte surový reťazec a jeho zdroj, potvrďte, že ide o literál a nie názov hostiteľa, že nie je loopback ani wildcard, a odovzdajte rozparsovaný objekt.