addDnsServer Bad Address: mit ellenőriz valójában az Android
Az addDnsServer Bad address hibája helyi formátumellenőrzés, nem DNS-kiesés. Elemezze a literált, dobja el a loopbacket és a wildcardot, majd tesztelje az elérhetőséget.
Az érték stringként érkezett a VpnService.Builder.addDnsServer-ba, és a builder ott elutasította, mielőtt bármi létrejött volna. Az elutasítás helyi: egyetlen resolver sem kapott kérést, nincs alagút, és ami meghiúsult, az formátumellenőrzés, nem DNS-kiesés.
Ez azért számít, mert egy tipikus VPN-alkalmazásnak három hasonló DNS-bemenete van: a profilba írt érték, a kiszolgáló által küldött érték és a rendszerszintű Private DNS. Csak az első kettő jut el ehhez a híváshoz.
Az előkészítő objektum azonnal ellenőrzi az argumentumot
A VpnService.Builder nem az alagút. Ez egy előkészítő objektum, amely címeket, útvonalakat és kiszolgálókat gyűjt, és csak a establish() alakítja ezeket élő ParcelFileDescriptor-vé és interfésszé. A DNS-bejegyzés a addDnsServer hívásakor ellenőrződik.
Két dolog következik: a veremnyom rövid és szándékosan semmitmondó, és egy hanyag catch resolver nélküli alagutat hagy.
A builder megtart minden listát, amit kap; egy ellenőrzést átlépő kiszolgáló hozzáadása csak azt bizonyítja, hogy a string cím-literálként elemezhető volt.
Mi lehet az argumentum
A paraméter cím, nem endpoint. A Builder-referencia IPv4 vagy IPv6 literált fogad, és az elemzés csak numerikus: a android.net.InetAddresses.parseNumericAddress ennek a parsernek a nyilvános arca API 29 óta.
A gyakorlati leképezés így néz ki:
| 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 |
A verzió az ellenőrzés módját befolyásolja, nem azt, hogy a builder mit fogad el. A InetAddresses API 29 óta elérhető, így Android 10-en az elemzés egy hívás; a addDnsServer(InetAddress) az eredeti forma, a String az újabb kiegészítés. API 29 alatt a InetAddress.getByName blokkoló hálózati kereséssel oldja fel a hosztnevet.
A loopback itt elutasított, a wildcard nem
Az asztali VPN-kliensek stub resolverüket 127.0.0.1:53-ra irányítják; ezt Android-profilba másolva a helyi gépet leíró érték kerül be, itt viszont nincs helyi gép. A builder csak ezt az esetet utasítja el: null vagy loopback cím IllegalArgumentException-t dob.
A wildcard a fordított eset, és itt téved a legtöbb olvasat. A 0.0.0.0 és a :: nem loopback, ezért a builder elfogadja őket; építéskor semmi nem dobódik. Az interfész aztán cél nélküli bejegyzéssel áll fel. Ennek elkapása a validátor dolga: a isAnyLocalAddress-öt tekintse érvénytelen bemenetnek.
Egyszer elemezze a konfiguráció határán
Az olcsó megoldás ott ellenőrizni, ahol a string a modellbe lép:
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 }
}
A null eredmény mezőszintű hiba lesz a hibás szöveggel, és a támogatás csak ezt tudja kezelni. Siker esetén az objektum egyenesen a builder.addDnsServer(address)-ba kerül, és a IllegalArgumentException-at fogja meg célzottan, ne a Exception-at.
Ne helyettesítse nyilvános resolverrel, ha a kívánt értéket elutasítják; a csendben nyilvános DNS-t beállító profil csatlakozik, de a belső nevek nem oldódnak fel.
Az interfész után igazolja az elérhetőséget
A builder elfogadása az első ellenőrzési pont, nem az utolsó. Az interfész felállása után a LinkProperties.getDnsServers() megmutatja, mit kapott a hálózat, a adb shell dumpsys connectivity pedig kívülről írja ki ugyanezeket.
A Private DNS külön vezérlő, nem ezen az argumentumon megy át; a DoT-szolgáltató hosztneve a rendszerbeállításhoz tartozik, nem IP-mezőhöz.
Mielőtt újra módosítja a profil parsert, rögzítse a nyers stringet és forrását, igazolja, hogy literál és nem hosztnév, hogy nem loopback és nem wildcard, majd adja tovább az elemzett objektumot.