Dəyər VpnService.Builder.addDnsServer metoduna sətir kimi çatdı və builder onu heç nə qurulmadan orada rədd etdi. Bu rədd yerlidir: heç bir resolverlə əlaqə saxlanmayıb, tunel yoxdur və uğursuz olan DNS kəsintisi deyil, format yoxlamasıdır.

Bu vacibdir, çünki tipik VPN tətbiqində oxşar görünən üç DNS girişi var: profildə yazılan və ya idxal edilən dəyər, serverin konfiqurasiya zamanı göndərdiyi dəyər və sistem səviyyəsində qurulan Private DNS. Yalnız ilk ikisi bu çağırışa çata bilər.

Hazırlıq obyekti arqumenti dərhal yoxlayır

VpnService.Builder tunel deyil. O, ünvanları, marşrutları və serverləri toplayan hazırlıq obyektidir və yalnız establish() həmin siyahıları canlı ParcelFileDescriptor və interfeysə çevirir. DNS girişi addDnsServer çağırılanda yoxlanılır, qurulanda deyil.

İki nəticə var. Stek izi qısa və qəsdən faydasızdır: dəyər adi sətir kimi gəlib və framework onun QR idxalından, server yükündən yoxsa ayarlardan gəldiyini bilə bilməz. Bütün quruluşa qoyulan səhlənkar catch isə ünvanları və marşrutları olan, amma resolveri olmayan tunel buraxır.

Builder verdiyiniz hər siyahını saxlayır. Yoxlamadan keçən server əlavə etmək yalnız sətrin qurulma zamanı ünvan literaı kimi təhlil olunduğunu sübut edir.

Arqument nə ola bilər

Parametr ünvandır, endpoint deyil. Builder sənədi IPv4 və ya IPv6 literaı qəbul edir və arxasındakı təhlil yalnız rəqəmsaldır: android.net.InetAddresses.parseNumericAddress API 29-dan etibarən həmin parserin açıq üzüdür. Hostname burada yavaş ünvan deyil, təhlil oluna bilməyən sətirdir.

Praktik uyğunluq belə görünür:

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

Versiya builderin nə qəbul etdiyinə deyil, necə yoxladığınıza təsir edir. InetAddresses API 29-dan mövcuddur, ona görə Android 10 və sonrasında literal təhlili bir çağırışdır. addDnsServer(InetAddress) ilkin formadır, String isə daha yeni əlavədir. API 29-dan aşağıda InetAddress.getByName hostname-i bloklayan şəbəkə axtarışı ilə həll edir və yazı səhvini yavaş başlamaya çevirir.

Loopback burada rədd edilir, wildcard yox

Masaüstü VPN müştəriləri çox vaxt öz stub resolverlərini 127.0.0.1:53 ünvanına yönəldir. Bunu Android profilinə köçürmək yerli maşını təsvir edən dəyər idxal edir və burada yerli maşın yoxdur. Builder yalnız bu halda rədd edir: null və ya loopback ünvan IllegalArgumentException atır.

Wildcard əks haldır və bu xətanın əksər oxunuşları burada səhv edir. 0.0.0.0 və :: loopback deyil, ona görə builder onları qəbul edir; qurulma zamanı heç nə atılmır. Sonra interfeys heç bir təyinat göstərməyən girişlə qalxır. Bunu tutmaq validatorunuzun işidir: isAnyLocalAddress dəyərini etibarsız giriş sayın.

Konfiqurasiya sərhədində bir dəfə təhlil edin

Ucuz düzəliş sətrin modelinizə girdiyi yerdə yoxlamaqdır:

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 nəticə təqsirkar mətni daşıyan sahə səviyyəli xəta olur və dəstək söhbətinin işləyə biləcəyi yeganə forma budur. Uğur yolunda obyekt birbaşa builder.addDnsServer(address) metoduna gedir. IllegalArgumentException xüsusi tutun, Exception yox: geniş tutma null göstərici xətalarını da udur.

Nəzərdə tutulan dəyər rədd edildikdə açıq resolverlə əvəz etməyə qarşı durun. Səssizcə açıq DNS xidməti qoyan profil bağlanır, amma daxili adlar həll olunmur.

İnterfeys yarandıqdan sonra əlçatanlığı təsdiqləyin

Builderin qəbulu ilk yoxlama nöqtəsidir, son deyil. İnterfeys qalxdıqdan sonra LinkProperties.getDnsServers() şəbəkənin həqiqətən nə aldığını göstərir. adb shell dumpsys connectivity eyni xassələri tətbiqdən kənardan çap edir.

Private DNS ayrı idarəetmədir və bu arqumentdən keçmir. DoT provayderinin hostname-i sistem ayarına aiddir, IP sahəsinə yapışdırılan dəyər deyil.

Profil parserini yenidən dəyişməzdən əvvəl bu sıranı işlədin: xam sətri və mənbəyini qeyd edin, onun hostname deyil literal olduğunu, loopback və wildcard olmadığını təsdiqləyin, sonra təhlil olunmuş obyekti ötürün.

Əlaqəli məqalələr