Արժեքը հասել է 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-ից։

Գործնական քարտեզագրումը այսպիսին է․

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-ը կլուծի 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 չէ, և փոխանցեք վերլուծված օբյեկտը։

Առնչվող հոդվածներ