Вредноста стигна до VpnService.Builder.addDnsServer како низа и builder ја одби таму пред ништо да биде воспоставено. Тоа одбивање е локално: ниту еден resolver не е контактиран, нема тунел и она што падна е проверка на формат, не DNS.

Ова е важно зашто типична VPN апликација има три слични DNS влеза: вредност во профилот, вредност што ја праќа серверот и Private DNS на системско ниво. Само првите два стигнуваат до овој повик.

Подготвителен објект веднаш го проверува аргументот

VpnService.Builder не е тунелот. Тоа е подготвителен објект што собира адреси, рути и сервери, и само establish() ги претвора тие списоци во жив ParcelFileDescriptor и интерфејс. DNS записот се проверува при повик на addDnsServer.

Две последици: трагата е кратка и намерно неинформативна, а невнимателен catch остава тунел без resolver.

Builder го чува секој список; додавање сервер што ја поминува проверката докажува само дека низата била анализирана како адресен литерал при градбата.

Што може да биде аргументот

Параметарот е адреса, не endpoint. Референцата за Builder прима литерал IPv4 или IPv6, а анализата е само бројчена: android.net.InetAddresses.parseNumericAddress е јавното лице на тој парсер од API 29.

Практичното пресликување изгледа вака:

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

Верзијата влијае на тоа како проверувате, не на тоа што прима builder. InetAddresses е достапен од API 29, па на Android 10 анализата е еден повик; addDnsServer(InetAddress) е оригиналната форма, String е новиот додаток. Под API 29 InetAddress.getByName решава име на хост со блокирачко мрежно пребарување.

Loopback се одбива овде, wildcard не

Десктоп VPN клиентите често го насочуваат својот stub resolver на 127.0.0.1:53; копирањето во Android профил внесува вредност што ја опишува локалната машина, а локална машина овде нема. Builder го одбива само овој случај: null или loopback адреса фрла IllegalArgumentException.

Wildcard е спротивен случај и тука повеќето читања на оваа грешка погрешно заклучуваат. 0.0.0.0 и :: не се loopback, па builder ги прифаќа; при градбата ништо не се фрла. Интерфејсот потоа се крева со запис што не именува одредиште. Да се фати тоа е работа на вашиот валидатор: третирајте isAnyLocalAddress како невалиден влез.

Анализирајте еднаш на границата на конфигурацијата

Евтиното решение е да проверувате таму каде низата влегува во вашиот модел:

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 резултатот станува грешка на ниво на поле со виновниот текст и тоа е единствената форма на која поддршката може да делува. На патот на успехот објектот оди директно во builder.addDnsServer(address), а IllegalArgumentException фатете го конкретно, не Exception.

Не заменувајте со јавен resolver кога саканата вредност е одбиена; профил што тивко става јавен DNS се поврзува, но внатрешните имиња престануваат да се решаваат.

Потврдете достапност откако ќе постои интерфејсот

Прифаќањето од builder е прва контролна точка, не последна. Откако интерфејсот ќе се крене, LinkProperties.getDnsServers() покажува што мрежата навистина примила, а adb shell dumpsys connectivity ги печати истите својства однадвор.

Private DNS е одделна контрола и не поминува низ овој аргумент; името на хост на DoT провајдер припаѓа на системското поставување, не на IP поле.

Пред повторно да го смените парсерот на профилот, запишете ја суровата низа и нејзиниот извор, потврдете дека е литерал, а не име на хост, дека не е loopback ниту wildcard, и предадете го анализираниот објект.

Поврзани статии