addDnsServer Bad Address: สิ่งที่ Android ตรวจสอบจริง ๆ
ข้อผิดพลาด Bad address จาก addDnsServer เป็นการตรวจรูปแบบภายใน ไม่ใช่ DNS ล่ม วิเคราะห์ลิเทอรัล ตัด loopback และ wildcard แล้วทดสอบการเข้าถึง
ค่ามาถึง VpnService.Builder.addDnsServer เป็นสตริงและ builder ปฏิเสธที่นั่นก่อนจะมีสิ่งใดถูกสร้าง การปฏิเสธนี้เป็นภายใน: ไม่มี resolver ใดถูกติดต่อ ไม่มี tunnel และสิ่งที่ล้มเหลวคือการตรวจรูปแบบ ไม่ใช่ DNS
เรื่องนี้สำคัญเพราะแอป VPN ทั่วไปมีอินพุต DNS สามอย่างที่คล้ายกัน: ค่าในโปรไฟล์ ค่าที่เซิร์ฟเวอร์ส่งมา และ Private DNS ในระดับระบบ มีเพียงสองอย่างแรกที่มาถึงการเรียกนี้
ออบเจ็กต์เตรียมการตรวจอาร์กิวเมนต์ทันที
VpnService.Builder ไม่ใช่ tunnel มันคือ ออบเจ็กต์เตรียมการที่รวบรวมที่อยู่ เส้นทาง และเซิร์ฟเวอร์ และมีเพียง establish() ที่เปลี่ยนรายการเหล่านั้นเป็น ParcelFileDescriptor ที่มีชีวิตและอินเทอร์เฟซ รายการ DNS ถูกตรวจเมื่อเรียก addDnsServer
ผลลัพธ์สองอย่าง: stack trace สั้นและจงใจไม่มีข้อมูล และ catch ที่ไม่ระวังทำให้เหลือ tunnel ที่ไม่มี resolver
builder เก็บทุกรายการที่ให้ไว้ การเพิ่มเซิร์ฟเวอร์ที่ผ่านการตรวจพิสูจน์เพียงว่าสตริงถูกวิเคราะห์เป็นที่อยู่ลิเทอรัลตอนสร้าง
อาร์กิวเมนต์เป็นอะไรได้
พารามิเตอร์คือที่อยู่ ไม่ใช่ endpoint เอกสาร Builder รับลิเทอรัล IPv4 หรือ IPv6 และการวิเคราะห์เป็นตัวเลขเท่านั้น: android.net.InetAddresses.parseNumericAddress คือหน้าสาธารณะของ parser นั้นตั้งแต่ 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 จึงรับไว้ ไม่มีอะไรถูกโยนตอนสร้าง อินเทอร์เฟซจะขึ้นพร้อมรายการที่ไม่ระบุปลายทาง การจับสิ่งนั้นเป็นงานของ validator: ถือว่า 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 และส่งออบเจ็กต์ที่วิเคราะห์แล้วต่อ