addDnsServer Bad Address: што всушност проверува Android
Грешката Bad address од addDnsServer е локална проверка на формат, а не DNS пад. Анализирајте го литералот, отстранете loopback и wildcard, потоа тестирајте достапност.
Вредноста стигна до 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.
Практичното пресликување изгледа вака:
| Input | Result | Reason |
|---|---|---|
10.8.0.1 | accepted | IPv4 literal |
2606:4700:4700::1111 | accepted | IPv6 literal |
dns.example.com | rejected | hostname; numeric parsing does not resolve it |
10.8.0.1:53 | rejected | port belongs to your own config model |
https://dns.example.com/dns-query | rejected | DoH URL, a different configuration path |
127.0.0.1 | rejected | loopback is not a remote resolver |
0.0.0.0 or :: | accepted by the builder | parses 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, и предадете го анализираниот објект.