addDnsServer Bad Address: ինչ է իրականում ստուգում Android-ը
addDnsServer-ի Bad address սխալը տեղական ձևաչափի ստուգում է, ոչ DNS խափանում։ Վերլուծեք լիտերալը, հեռացրեք loopback-ը և wildcard-ը, ապա ստուգեք հասանելիությունը։
Արժեքը հասել է VpnService.Builder.addDnsServer-ին որպես տող, և builder-ը մերժել է այն այնտեղ, նախքան որևէ բան հաստատվելը։ Այդ մերժումը տեղական է․ ոչ մի resolver չի կանչվել, թունել չկա, և ձախողվել է ձևաչափի ստուգումը, ոչ DNS-ը։
Սա կարևոր է, որովհետև սովորական VPN հավելվածն ունի երեք նման DNS մուտք․ պրոֆիլում գրված արժեք, սերվերի ուղարկած արժեք և համակարգային Private DNS։ Միայն առաջին երկուսը կարող են հասնել այս կանչին։
Նախապատրաստական օբյեկտը ստուգում է արգումենտը անմիջապես
VpnService.Builder թունելը չէ։ Այն նախապատրաստական օբյեկտ է, որը հավաքում է հասցեներ, երթուղիներ և սերվերներ, և միայն establish()-ն է դարձնում այդ ցանկերը կենդանի ParcelFileDescriptor և ինտերֆեյս։ DNS գրառումը ստուգվում է addDnsServer կանչելիս, ոչ հաստատելիս։
Երկու հետևանք կա․ stack trace-ը կարճ է և դիտավորյալ անօգուտ, և անփույթ catch-ը թողնում է թունել առանց resolver-ի։
Builder-ը պահում է ցանկացած ցանկ։ Ստուգումն անցած սերվեր ավելացնելը ապացուցում է միայն, որ տողը կառուցման պահին վերլուծվել է որպես հասցեի լիտերալ։
Ինչ կարող է լինել արգումենտը
Պարամետրը հասցե է, ոչ endpoint։ Builder-ի տեղեկատուն ընդունում է IPv4 կամ IPv6 լիտերալ, և վերլուծությունը միայն թվային է․ android.net.InetAddresses.parseNumericAddress-ը այդ parser-ի հանրային դեմքն է 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-ը կլուծի hostname-ը արգելափակող ցանցային որոնմամբ։
Loopback-ը մերժվում է այստեղ, wildcard-ը ոչ
Desktop 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 մատակարարի hostname-ը պատկանում է համակարգային կարգավորմանը, ոչ IP դաշտին։
Նախքան պրոֆիլի parser-ը կրկին փոխելը, գրանցեք հում տողը և դրա աղբյուրը, հաստատեք, որ այն լիտերալ է և ոչ hostname, որ loopback կամ wildcard չէ, և փոխանցեք վերլուծված օբյեկտը։