Утга 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-өөс хойш тэр parser-ийн нийтийн нүүр.

Практик харгалзаа ийм байна:

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 талбарт биш.

Профайлын parser-ыг дахин өөрчлөхөөс өмнө түүхий мөрийг болон эх сурвалжийг нь бүртгэж, хост нэр биш литерал, loopback ч wildcard ч биш гэдгийг баталж, задлан шинжилсэн объектыг дамжуул.

Холбоотой нийтлэлүүд