Değer VpnService.Builder.addDnsServer çağrısına dize olarak ulaştı ve builder onu hiçbir şey kurulmadan orada reddetti. Bu ret yereldir: hiçbir resolver ile iletişim kurulmadı, tünel yok ve başarısız olan şey biçim denetimi, DNS değil.

Bu önemli çünkü tipik bir VPN uygulamasının üç benzer DNS girişi vardır: profildeki değer, sunucunun gönderdiği değer ve sistem düzeyinde Private DNS. Bu çağrıya yalnızca ilk ikisi ulaşır.

Hazırlık nesnesi argümanı hemen denetler

VpnService.Builder tünel değildir. O, adresleri, rotaları ve sunucuları toplayan hazırlık nesnesidir ve yalnızca establish() bu listeleri canlı bir ParcelFileDescriptor ve arayüze dönüştürür. DNS girdisi addDnsServer çağrılırken denetlenir.

İki sonuç: yığın izi kısa ve kasıtlı olarak bilgi vermez ve dikkatsiz bir catch tüneli resolver olmadan bırakır.

Builder verilen listeyi korur; denetimi geçen bir sunucu eklemek yalnızca dizenin derleme sırasında adres literali olarak ayrıştırıldığını kanıtlar.

Argüman ne olabilir

Parametre bir adrestir, endpoint değil. Builder başvurusu IPv4 veya IPv6 literali alır ve ayrıştırma yalnızca sayısaldır: android.net.InetAddresses.parseNumericAddress API 29’dan itibaren o ayrıştırıcının kamusal yüzüdür.

Pratik eşleme şöyle 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

Sürüm nasıl doğruladığınızı etkiler, builder’ın ne kabul ettiğini değil. InetAddresses API 29’dan beri var, bu yüzden Android 10’da ayrıştırma tek çağrıdır; addDnsServer(InetAddress) özgün biçim, String daha yeni ekleme. API 29 altında InetAddress.getByName ana bilgisayar adını bloklayan bir ağ aramasıyla çözer.

Loopback burada reddedilir, wildcard reddedilmez

Masaüstü VPN istemcileri kendi stub resolver’larını sık sık 127.0.0.1:53 adresine yönlendirir; bunu Android profiline kopyalamak yerel makineyi tanımlayan bir değer getirir ve burada yerel makine yoktur. Builder yalnızca bu durumu reddeder: null veya loopback adres IllegalArgumentException fırlatır.

Wildcard bunun tersi durumdur ve hatanın çoğu okuması burada yanılır. 0.0.0.0 ve :: loopback değildir, bu yüzden builder onları kabul eder; derleme sırasında hiçbir şey fırlatılmaz. Arayüz ardından hedef belirtmeyen bir girdiyle açılır. Bunu yakalamak doğrulayıcınızın işidir: isAnyLocalAddress değerini geçersiz girdi sayın.

Yapılandırma sınırında bir kez ayrıştırın

Ucuz çözüm, dizenin modelinize girdiği yerde doğrulamaktı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 sonuç, suçlu metni taşıyan alan düzeyinde bir hata olur ve desteğin işlem yapabileceği tek biçim budur. Başarı yolunda nesne doğrudan builder.addDnsServer(address) içine girer ve IllegalArgumentException öğesini özellikle yakalayın, Exception değil.

İstenen değer reddedildiğinde genel bir resolver ile değiştirmeyin; sessizce genel DNS koyan profil bağlanır ama iç adlar çözülemez.

Arayüz oluştuktan sonra erişilebilirliği doğrulayın

Builder’ın kabulü ilk denetim noktasıdır, son değil. Arayüz açıldıktan sonra LinkProperties.getDnsServers() ağın gerçekte ne aldığını bildirir ve adb shell dumpsys connectivity aynı özellikleri dışarıdan yazdırır.

Private DNS ayrı bir denetimdir ve bu argümandan geçmez; DoT sağlayıcısının ana bilgisayar adı sistem ayarına aittir, IP alanına değil.

Profil ayrıştırıcısını yeniden değiştirmeden önce ham dizeyi ve kaynağını kaydedin, onun ana bilgisayar adı değil literal olduğunu, loopback veya wildcard olmadığını doğrulayın ve ayrıştırılmış nesneyi iletin.

İlgili makaleler