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:

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

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.

Súvisiace články