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:

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

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.

Kapcsolódó cikkek