addDnsServer Bad Address: hva Android faktisk validerer
Bad address-feilen fra addDnsServer er en lokal formatkontroll, ikke et DNS-utfall. Pars literalen, forkast loopback og wildcard, og test deretter rekkevidde.
Verdien nådde VpnService.Builder.addDnsServer som en streng, og builderen avviste den der før noe ble etablert. Avvisningen er lokal: ingen resolver er kontaktet, det finnes ingen tunnel, og det som feilet er en formatkontroll, ikke et DNS-utfall.
Dette betyr noe fordi en typisk VPN-app har tre like DNS-innganger: en verdi skrevet eller importert i profilen, en verdi serveren sender under konfigurasjon, og Private DNS på systemnivå. Bare de to første når dette kallet.
Et forberedelsesobjekt sjekker argumentet med en gang
VpnService.Builder er ikke tunnelen. Det er et forberedelsesobjekt som samler adresser, ruter og servere, og bare establish() gjør disse listene til et levende ParcelFileDescriptor og et grensesnitt. DNS-oppføringen sjekkes når du kaller addDnsServer.
To konsekvenser: sporet er kort og med vilje intetsigende, og en slurvete catch rundt hele bygget etterlater en tunnel uten resolver.
Builderen beholder enhver liste du gir den; å legge til en server som består kontrollen beviser bare at strengen ble parset som adresseliteral ved byggetid.
Hva argumentet kan være
Parameteren er en adresse, ikke et endpoint. Builder-referansen tar et IPv4- eller IPv6-literal, og parsingen er bare numerisk: android.net.InetAddresses.parseNumericAddress er det offentlige ansiktet til den parseren fra API 29.
Den praktiske mappingen ser slik ut:
| 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 |
Versjonen påvirker hvordan du validerer, ikke hva builderen godtar. InetAddresses er tilgjengelig fra API 29, så på Android 10 er parsingen ett kall; addDnsServer(InetAddress) er den opprinnelige formen, String den nyere tilleggelsen. Under API 29 løser InetAddress.getByName et vertsnavn med et blokkerende nettsøk.
Loopback avvises her, wildcard ikke
Desktop-VPN-klienter peker ofte sin egen stub resolver mot 127.0.0.1:53; kopieres det til en Android-profil, importeres en verdi som beskriver den lokale maskinen, og her finnes ingen lokal maskin. Builderen avviser bare dette tilfellet: en null- eller loopback-adresse kaster IllegalArgumentException.
Et wildcard er det motsatte tilfellet, og her går de fleste lesningene galt. 0.0.0.0 og :: er ikke loopback, så builderen godtar dem; intet kastes ved byggetid. Grensesnittet kommer så opp med en oppføring som ikke navngir noe mål. Å fange det er din validators jobb: behandle isAnyLocalAddress som ugyldig inndata.
Pars én gang ved konfigurasjonsgrensen
Den billige løsningen er å validere der strengen kommer inn i modellen din:
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 }
}
Et null-resultat blir en feltfeil med den skyldige teksten, og det er den eneste formen support kan handle på. På suksessveien går objektet rett i builder.addDnsServer(address), og fang IllegalArgumentException spesifikt, ikke Exception.
Ikke bytt ut med en offentlig resolver når den tiltenkte verdien avvises; en profil som stille setter inn en offentlig DNS kobler fortsatt til, men interne navn slutter å løses.
Bekreft rekkevidde når grensesnittet finnes
Builderens aksept er det første kontrollpunktet, ikke det siste. Når grensesnittet er oppe, viser LinkProperties.getDnsServers() hva nettverket faktisk mottok, og adb shell dumpsys connectivity skriver ut de samme egenskapene utenfra.
Private DNS er en separat kontroll og går ikke gjennom dette argumentet; vertsnavnet til en DoT-leverandør hører til systeminnstillingen, ikke et IP-felt.
Før du endrer profilparseren igjen, logg den rå strengen og kilden, bekreft at det er et literal og ikke et vertsnavn, at det verken er loopback eller wildcard, og send det parsede objektet videre.