Valoarea a ajuns la VpnService.Builder.addDnsServer ca șir și builderul a refuzat-o acolo înainte de a se stabili ceva. Refuzul este local: niciun resolver nu a fost contactat, nu există tunel, iar ceea ce a eșuat este o verificare de format, nu un DNS.

Acest lucru contează pentru că o aplicație VPN obișnuită are trei intrări DNS asemănătoare: o valoare din profil, o valoare trimisă de server și Private DNS la nivel de sistem. Doar primele două ajung la acest apel.

Un obiect de pregătire verifică argumentul imediat

VpnService.Builder nu este tunelul. Este un obiect de pregătire care adună adrese, rute și servere, iar doar establish() transformă acele liste într-un ParcelFileDescriptor viu și într-o interfață. Intrarea DNS este verificată la apelul addDnsServer.

Două consecințe: urma este scurtă și intenționat neinformativă, iar un catch neglijent lasă un tunel fără resolver.

Builderul păstrează orice listă îi dați; adăugarea unui server care trece verificarea dovedește doar că șirul a fost analizat ca literal de adresă la construcție.

Ce poate fi argumentul

Parametrul este o adresă, nu un endpoint. Referința Builder acceptă un literal IPv4 sau IPv6, iar analiza este doar numerică: android.net.InetAddresses.parseNumericAddress este fața publică a acelui parser din API 29.

Maparea practică arată astfel:

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

Versiunea influențează modul în care validați, nu ce acceptă builderul. InetAddresses este disponibil din API 29, deci pe Android 10 analiza este un singur apel; addDnsServer(InetAddress) este forma originală, String adăugarea mai nouă. Sub API 29, InetAddress.getByName va rezolva un nume de gazdă printr-o căutare de rețea blocantă.

Loopback este refuzat aici, un wildcard nu

Clienții VPN de desktop își îndreaptă adesea stub resolverul spre 127.0.0.1:53; copierea în profilul Android introduce o valoare care descrie mașina locală, iar aici nu există mașină locală. Builderul refuză doar acest caz: o adresă null sau loopback aruncă IllegalArgumentException.

Un wildcard este cazul opus și aici greșesc majoritatea lecturilor. 0.0.0.0 și :: nu sunt loopback, deci builderul le acceptă; nimic nu este aruncat la construcție. Interfața urcă apoi cu o intrare care nu numește nicio destinație. Prinderea acestui lucru este treaba validatorului: tratați isAnyLocalAddress ca intrare invalidă.

Analizați o singură dată la granița configurației

Remedierea ieftină este să validați acolo unde șirul intră în modelul dumneavoastră:

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 }
}

Un rezultat null devine o eroare la nivel de câmp cu textul vinovat și este singura formă pe care suportul o poate trata. Pe calea de succes obiectul intră direct în builder.addDnsServer(address), iar IllegalArgumentException se prinde specific, nu Exception.

Nu înlocuiți cu un resolver public când valoarea dorită este refuzată; un profil care pune în silență un DNS public se conectează, dar numele interne nu mai sunt rezolvate.

Confirmați accesibilitatea după ce interfața există

Acceptarea de către builder este primul punct de control, nu ultimul. După ce interfața urcă, LinkProperties.getDnsServers() raportează ce a primit rețeaua efectiv, iar adb shell dumpsys connectivity tipărește aceleași proprietăți din exterior.

Private DNS este un control separat și nu trece prin acest argument; numele de gazdă al unui furnizor DoT aparține setării de sistem, nu unui câmp IP.

Înainte de a schimba din nou parserul profilului, înregistrați șirul brut și sursa lui, confirmați că este un literal și nu un nume de gazdă, că nu este loopback sau wildcard, și transmiteți obiectul analizat.

Articole similare