Verdien nådde VpnService.Builder.addDnsServer som en streng, og builderen avviste den der før noe var etablert. Avvisningen er lokal: ingen resolver er kontaktet, det finnes ingen tunnel, og det som sviktet er en formatkontroll, ikke DNS.

Dette betyr noe fordi en vanlig VPN-app har tre like DNS-innganger: en verdi i profilen, en verdi serveren sender under konfigurasjon, og Private DNS på systemnivå. Bare de to første når dette kallet.

Et forberedelsesobjekt sjekker argumentet umiddelbart

VpnService.Builder er ikke tunnelen. Det er et forberedelsesobjekt som samler adresser, ruter og servere, og bare establish() gjør listene om til et levende ParcelFileDescriptor og et grensesnitt. DNS-oppføringen sjekkes når du kaller addDnsServer.

To følger: sporet er kort og med vilje lite informativt, og en uforsiktig catch rundt hele bygget etterlater en tunnel uten resolver.

Builderen beholder hvilken som helst liste du gir den; å legge til en server som passerer kontrollen beviser bare at strengen ble parset som adresseliteral ved byggetid.

Hva argumentet kan være

Parameteren er en adresse, ikke et endpoint. Builder-referansen tar en IPv4- eller IPv6-literal, og parsingen er bare numerisk: android.net.InetAddresses.parseNumericAddress er det offentlige ansiktet til den parseren fra API 29.

Den praktiske koblingen ser slik ut:

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

Versjonen påvirker hvordan du validerer, ikke hva builderen godtar. InetAddresses er tilgjengelig fra API 29, så på Android 10 er parsingen ett kall; addDnsServer(InetAddress) er den opprinnelige formen, String den nyere tilføyelsen. Under API 29 løser InetAddress.getByName et vertsnavn med et blokkerende nettsøk.

Loopback avvises her, wildcard ikke

Desktop-VPN-klienter peker ofte stub resolveren sin mot 127.0.0.1:53; kopieres det inn i en Android-profil, importeres en verdi som beskriver den lokale maskinen, og her finnes ingen lokal maskin. Builderen avviser bare dette tilfellet: en null- eller loopback-adresse kaster IllegalArgumentException.

Et wildcard er det motsatte tilfellet, og her tar de fleste tolkningene av denne feilen feil. 0.0.0.0 og :: er ikke loopback, så builderen godtar dem; ingenting kastes ved byggetid. Grensesnittet kommer så opp med en oppføring som ikke navngir noe mål. Å fange det er validatorens jobb: behandle isAnyLocalAddress som ugyldig inndata.

Parse én gang ved konfigurasjonsgrensen

Den billige løsningen er å validere der strengen kommer inn i modellen din:

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

Et null-resultat blir en feltfeil med den skyldige teksten, og det er den eneste formen støtte kan handle på. På suksessveien går objektet rett inn i builder.addDnsServer(address), og fang IllegalArgumentException spesifikt, ikke Exception.

Ikke bytt til en offentlig resolver når den tiltenkte verdien avvises; en profil som i det stille setter inn en offentlig DNS kobler fortsatt til, men interne navn slutter å løses opp.

Bekreft rekkevidde når grensesnittet finnes

Builderens aksept er det første kontrollpunktet, ikke det siste. Når grensesnittet er oppe, viser LinkProperties.getDnsServers() hva nettverket faktisk mottok, og adb shell dumpsys connectivity skriver ut de samme egenskapene utenfra.

Private DNS er en egen kontroll og går ikke gjennom dette argumentet; vertsnavnet til en DoT-leverandør hører til systeminnstillingen, ikke et IP-felt.

Før du endrer profilparseren igjen, logg den rå strengen og kilden, bekreft at det er en literal og ikke et vertsnavn, at det verken er loopback eller wildcard, og send det parsede objektet videre.

Relaterte artikler