addDnsServer Bad Address: რას ამოწმებს სინამდვილეში Android
addDnsServer-ის Bad address შეცდომა ლოკალური ფორმატის შემოწმებაა და არა DNS გათიშვა. გააანალიზეთ ლიტერალი, მოაშორეთ loopback და wildcard, შემდეგ შეამოწმეთ ხელმისაწვდომობა.
მნიშვნელობა 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-დან ამ პარსერის საჯარო სახეა.
პრაქტიკული დაბმა ასე გამოიყურება:
| Input | Result | Reason |
|---|---|---|
10.8.0.1 | accepted | IPv4 literal |
2606:4700:4700::1111 | accepted | IPv6 literal |
dns.example.com | rejected | hostname; numeric parsing does not resolve it |
10.8.0.1:53 | rejected | port belongs to your own config model |
https://dns.example.com/dns-query | rejected | DoH URL, a different configuration path |
127.0.0.1 | rejected | loopback is not a remote resolver |
0.0.0.0 or :: | accepted by the builder | parses 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 არის, და გადასცით გაანალიზებული ობიექტი.