მნიშვნელობა 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-დან ამ პარსერის საჯარო სახეა.

პრაქტიკული დაბმა ასე გამოიყურება:

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 ველს.

სანამ პროფილის პარსერს კვლავ შეცვლით, ჩაწერეთ ნედლი სტრიქონი და მისი წყარო, დაადასტურეთ, რომ ეს ლიტერალია და არა ჰოსტსახელი, რომ არც loopback და არც wildcard არის, და გადასცით გაანალიზებული ობიექტი.

დაკავშირებული სტატიები