Vrijednost je stigla u VpnService.Builder.addDnsServer kao string i builder ju je tamo odbio prije nego što je išta uspostavljeno. To odbijanje je lokalno: nijedan resolver nije kontaktiran, tunela nema, a ono što je palo je provjera formata, ne DNS.

To je važno jer tipična VPN aplikacija ima tri slična DNS ulaza: vrijednost u profilu, vrijednost koju server pošalje i Private DNS na nivou sistema. Samo prva dva stižu do ovog poziva.

Pripremni objekat provjerava argument odmah

VpnService.Builder nije tunel. To je pripremni objekat koji skuplja adrese, rute i servere, a samo establish() pretvara te liste u živi ParcelFileDescriptor i interfejs. DNS unos se provjerava pri pozivu addDnsServer.

Dvije posljedice: trag je kratak i namjerno neinformativan, a nepažljiv catch ostavlja tunel bez resolvera.

Builder čuva svaku listu; dodavanje servera koji prođe provjeru dokazuje samo da je string raščlanjen kao adresni literal pri gradnji.

Šta argument smije biti

Parametar je adresa, ne endpoint. Referenca Builder prima literal IPv4 ili IPv6, a raščlanjivanje je samo numeričko: android.net.InetAddresses.parseNumericAddress je javno lice tog parsera od API 29.

Praktično mapiranje izgleda ovako:

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

Verzija utiče na način provjere, ne na ono što builder prima. InetAddresses je dostupan od API 29, pa je na Androidu 10 raščlanjivanje jedan poziv; addDnsServer(InetAddress) je izvorna forma, String noviji dodatak. Ispod API 29 InetAddress.getByName će raščlaniti ime hosta blokirajućom mrežnom pretragom.

Loopback se ovdje odbija, wildcard ne

Desktop VPN klijenti često usmjeravaju svoj stub resolver na 127.0.0.1:53; kopiranje toga u Android profil unosi vrijednost koja opisuje lokalnu mašinu, a lokalne mašine ovdje nema. Builder odbija samo taj slučaj: null ili loopback adresa baca IllegalArgumentException.

Wildcard je suprotan slučaj i tu se većina čitanja ove greške zabuni. 0.0.0.0 i :: nisu loopback, pa ih builder prihvata; pri gradnji se ništa ne baca. Interfejs se onda diže s unosom koji ne imenuje odredište. Hvatanje toga je posao vašeg validatora: tretirajte isAnyLocalAddress kao nevažeći ulaz.

Raščlanite jednom na granici konfiguracije

Jeftino rješenje je validirati tamo gdje string ulazi u vaš model:

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 rezultat postaje greška na nivou polja s krivim tekstom i to je jedini oblik na koji podrška može djelovati. Na putu uspjeha objekat ide pravo u builder.addDnsServer(address), a IllegalArgumentException hvatajte konkretno, ne Exception.

Ne zamjenjujte javnim resolverom kad je željena vrijednost odbijena; profil koji tiho ubaci javni DNS se povezuje, ali interna imena prestaju da se razrješavaju.

Potvrdite dostupnost nakon što interfejs postoji

Prihvatanje od buildera je prva kontrolna tačka, ne posljednja. Nakon dizanja interfejsa LinkProperties.getDnsServers() pokazuje šta je mreža stvarno primila, a adb shell dumpsys connectivity ispisuje ista svojstva spolja.

Private DNS je odvojena kontrola i ne prolazi kroz ovaj argument; ime hosta DoT provajdera pripada sistemskom podešavanju, ne polju IP.

Prije nego opet promijenite parser profila, zabilježite sirovi string i njegov izvor, potvrdite da je literal a ne ime hosta, da nije loopback ni wildcard, i proslijedite raščlanjeni objekat.

Povezani članci