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 ispad.

To je važno jer tipična VPN aplikacija ima tri slična DNS ulaza: vrijednost upisanu ili uvezenu u profil, vrijednost koju server pošalje tokom konfiguracije i Private DNS na nivou sistema. Samo prva dva mogu stići 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, ne pri uspostavljanju.

Slijede dvije posljedice. Stack trace je kratak i namjerno neinformativan: vrijednost je došla kao običan string i okvir ne može znati dolazi li iz QR uvoza ili servera. A nepažljiv catch oko cijele gradnje ostavlja tunel s adresama i rutama, ali bez resolvera.

Builder čuva svaku listu koju mu date. 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. Ime hosta ovdje nije spora adresa nego string koji se ne može raščlaniti.

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 to kako provjeravate, ne na to šta builder prima. InetAddresses je dostupan od API 29, pa je na Android 10 i novijem raščlanjivanje jedan poziv. addDnsServer(InetAddress) je izvorna forma, String noviji dodatak. Ispod API 29 InetAddress.getByName će rado raščlaniti ime hosta blokirajućom mrežnom pretragom i pretvoriti grešku u kucanju u spor start.

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). Hvatajte IllegalArgumentException konkretno, a ne Exception: široko hvatanje guta i greške null pokazivača.

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. 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, a ne polju IP.

Prije nego opet promijenite parser profila, radite ovim redom: 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