Die waarde het VpnService.Builder.addDnsServer as ’n string bereik, en die bouer het dit daar verwerp voordat enigiets opgestel is. Daardie verwerping is plaaslik: geen resolver is gekontak nie, geen tonnel bestaan nie, en wat gefaal het is ’n formaattoets, nie ’n DNS-storing nie. Om die spoor as ’n kragbestuurbotsing of ’n bedienerprobleem te lees, stuur jou na die verkeerde laag. Die raam noem die argument wat geweier is, nie die instelling wat dit geproduseer het nie.

Dit maak saak omdat ’n tipiese VPN-program drie DNS-invoere het wat dieselfde lyk: ’n waarde wat in die profiel getik of ingevoer is, ’n waarde wat die bediener tydens konfigurasie stuur, en Private DNS wat op stelselvlak gestel is. Net die eerste twee kan hierdie oproep bereik, en gewoonlik kom die slegte string uit net een van hulle.

’n Voorbereidingsobjek kontroleer die argument dadelik

VpnService.Builder is nie die tonnel nie. Dit is ’n voorbereidingsobjek wat adresse, roetes en bedieners versamel, en net establish() maak daardie lyste ’n lewende ParcelFileDescriptor en ’n koppelvlak. Die DNS-inskrywing word gekontroleer wanneer jy addDnsServer roep, nie wanneer jy vestig nie; daarom word die uitsondering etlike reëls bokant die kode wat jy ontfout het, gegooi.

Twee gevolge volg. Die stapelspoor is kort en opsetlik onbehulpsaam: die waarde het as ’n gewone string aangekom, en die raamwerk kan nie weet of dit uit ’n QR-invoer, ’n bedienerlading of ’n instelling-oorskrywing kom nie. En omdat alles anders in die bouer die mislukte oproep oorleef, laat ’n sorgelose catch rondom die hele bou jou met ’n tonnel wat adresse en roetes het maar glad geen resolver nie.

Die bouer hou ook enige lys wat jy gee. Om ’n bediener by te voeg wat die toets slaag, bewys net dat die string tydens bou as ’n adresletterlike ontleed is, en die wildcard-geval hieronder wys dit bewys nie eers dat die waarde bruikbaar is nie.

Wat die argument mag wees

Die parameter is ’n adres, nie ’n eindpunt nie. Android se Builder-verwysing neem ’n IPv4- of IPv6-letterlike, en die ontleding daaronder is slegs numeries: android.net.InetAddresses.parseNumericAddress is die publieke gesig van daardie ontleder vanaf API 29, en die raamwerk se eie oproep beland by dieselfde roetine. ’n Hostnaam is hier nie ’n stadige adres nie; dit is ’n onontleedbare string, en daardie ontledingsfout verskyn as ’n bad-address-uitsondering.

Die praktiese kartering lyk so:

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

Weergawe maak saak vir hoe jy valideer, nie vir wat die bouer aanvaar nie. InetAddresses is beskikbaar vanaf API 29, so op Android 10 en later is die letterlike ontleding een oproep. Die oorlaai maak ook saak: addDnsServer(InetAddress) is die oorspronklike vorm, terwyl die String-oorlaai die nuwer toevoeging is, so ’n geplakte string-voorbeeld sal nie teen ’n ouer SDK kompileer nie. Onder API 29 sal die ooglopende plaasvervanger, InetAddress.getByName, met graagte ’n hostnaam met ’n blokkerende netwerkopsoek oplos, wat ’n tikfout in ’n stadige begin verander eerder as ’n duidelike fout. As jy nog na daardie weergawes lewer, doen ’n eksplisiete letterlike toets voordat jy die ouer API aanraak.

Loopback word hier geweier, ’n wildcard nie

Rekenaar-VPN-kliënte wys dikwels hul eie stub-resolver na 127.0.0.1:53 en laat die tonnelkliënt navrae aanstuur. Om daardie reëling in ’n Android-profiel te kopieer, voer ’n waarde in wat die plaaslike masjien beskryf, en daar is geen plaaslike masjien hier om dit aan te gee nie: die tonnel se resolver moet aan die ander kant van die koppelvlak bereikbaar wees. Dit is die een geval wat die bouer self weier: ’n nul- of loopback-adres gooi IllegalArgumentException, soos die verwysingskontrak sê.

’n Wildcard is die teenoorgestelde geval, en dit is waar die meeste lesings van hierdie uitsondering verkeerd gaan. 0.0.0.0 en :: is nie loopback nie, so die bouer aanvaar hulle en voeg hulle soos enige ander letterlike by; niks gooi tydens bou nie. Die koppelvlak kom dan op met ’n resolver-inskrywing wat geen bestemming noem nie, en die fout skuif van ’n uitsondering na navrae wat stilweg nêrens heen gaan. Om dit te vang is jou valideerder se werk, nie die raamwerk s’n nie: behandel isAnyLocalAddress as ongeldige invoer in die hulpfunksie hieronder en wys dit as ’n veldvlakfout.

Ontleed een keer by die konfigurasiegrens

Die goedkoop oplossing is om te valideer waar die string jou model binnekom en die ontlede objek verder te stuur, sodat die waarde wat die bouer sien presies die waarde is wat jy gevalideer het:

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

’n Nul-resultaat word dan ’n veldvlakfout wat die aanstootlike teks dra, en dit is die enigste vorm waarop ’n ondersteuningsgesprek kan handel. Op die suksespad gaan die teruggekeerde objek reguit in builder.addDnsServer(address), sodat die waarde wat jy gevalideer het die waarde is wat verskeep. Vang IllegalArgumentException spesifiek eerder as Exception: ’n breë vang op konfigurasiekode sluk ook die null-wyser- en toestandfoute wat jy in ’n ontfoutbou wil sien.

Weerstaan die terugval om ’n publieke resolver te vervang wanneer die bedoelde waarde verwerp word. ’n Profiel wat stilweg ’n publieke DNS-diens inruil, verbind steeds, maar interne name hou op oplos en elke navraag verlaat deur ’n resolver wat die gebruiker nie gekies het nie. ’n Sigbare fout kos een ondersteuningsantwoord; die stil vervanging kos die vertroue dat die tonnel navrae hou waar die profiel sê.

Bevestig bereikbaarheid nadat die koppelvlak bestaan

Aanvaarding deur die bouer is die eerste kontrolepunt, nie die laaste nie. Sodra die koppelvlak op is, rapporteer LinkProperties.getDnsServers() wat die netwerk werklik ontvang het, en dit is waar ’n wanverhouding wys: ’n leë lys, minder inskrywings as wat jy bygevoeg het, of ’n familie wat jou roetes nie dra nie. adb shell dumpsys connectivity druk dieselfde per-netwerk eienskappe wanneer jy van buite die program wil kyk.

Private DNS is ’n aparte beheer en reis nie deur hierdie argument nie. ’n DoT-verskaffer-hostnaam hoort by die stelselinstelling, of by jou eie geënkripteerde resolver-pad; dit is nie ’n waarde om in ’n IP-veld te plak nie, en om ’n Private DNS-instelling reg te maak sal nie ’n uitsondering wat deur die bouer gegooi is, skoonmaak nie.

Voordat jy die profielontleder weer verander, werk hierdie volgorde: teken die rou string en sy bron aan, bevestig dat dit ’n letterlike is eerder as ’n hostnaam of URL, bevestig dat dit nie loopback of ’n wildcard is nie, stuur die ontlede objek eerder as om die teks weer te ontleed, en vergelyk eers daarna die gevestigde koppelvlak met die waarde wat jy bedoel het.

Verwante artikels