Giá trị đến VpnService.Builder.addDnsServer dưới dạng chuỗi và builder từ chối ngay tại đó trước khi bất cứ thứ gì được thiết lập. Việc từ chối này là cục bộ: không resolver nào được liên hệ, không có tunnel, và thứ thất bại là kiểm tra định dạng, không phải DNS.

Điều này quan trọng vì một ứng dụng VPN điển hình có ba đầu vào DNS giống nhau: giá trị trong hồ sơ, giá trị máy chủ gửi và Private DNS ở cấp hệ thống. Chỉ hai đầu tiên đến được lời gọi này.

Đối tượng chuẩn bị kiểm tra đối số ngay lập tức

VpnService.Builder không phải tunnel. Đó là đối tượng chuẩn bị tập hợp địa chỉ, tuyến và máy chủ, và chỉ establish() biến các danh sách đó thành ParcelFileDescriptor sống và giao diện. Mục DNS được kiểm tra khi gọi addDnsServer.

Hai hệ quả: dấu vết ngắn và cố ý không có thông tin, và catch cẩu thả để lại tunnel không có resolver.

Builder giữ mọi danh sách được đưa; thêm một máy chủ vượt qua kiểm tra chỉ chứng minh chuỗi đã được phân tích như literal địa chỉ lúc dựng.

Đối số có thể là gì

Tham số là địa chỉ, không phải endpoint. Tài liệu Builder nhận literal IPv4 hoặc IPv6, và phân tích chỉ là số: android.net.InetAddresses.parseNumericAddress là bộ mặt công khai của parser đó từ API 29.

Ánh xạ thực tế như sau:

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

Phiên bản ảnh hưởng đến cách bạn xác thực, không phải thứ builder chấp nhận. InetAddresses có từ API 29, nên trên Android 10 phân tích là một lời gọi; addDnsServer(InetAddress) là dạng gốc, String là phần bổ sung mới hơn. Dưới API 29 InetAddress.getByName sẽ phân giải tên máy chủ bằng truy vấn mạng chặn.

Loopback bị từ chối ở đây, wildcard thì không

Ứng dụng VPN trên máy tính thường trỏ stub resolver của họ tới 127.0.0.1:53; sao chép điều đó vào hồ sơ Android đưa vào một giá trị mô tả máy cục bộ, và ở đây không có máy cục bộ. Builder chỉ từ chối trường hợp này: địa chỉ null hoặc loopback ném IllegalArgumentException.

Wildcard là trường hợp ngược lại và đây là nơi phần lớn cách đọc sai. 0.0.0.0 và :: không phải loopback, nên builder chấp nhận chúng; không có gì bị ném lúc dựng. Giao diện sau đó lên với một mục không nêu đích đến. Bắt được điều đó là việc của validator: coi isAnyLocalAddress là đầu vào không hợp lệ.

Phân tích một lần tại ranh giới cấu hình

Cách sửa rẻ là xác thực nơi chuỗi đi vào mô hình của bạn:

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 }
}

Kết quả null trở thành lỗi cấp trường mang văn bản sai, và đó là dạng duy nhất hỗ trợ có thể xử lý. Trên đường thành công, đối tượng đi thẳng vào builder.addDnsServer(address), và bắt IllegalArgumentException cụ thể, không phải Exception.

Đừng thay bằng resolver công cộng khi giá trị mong muốn bị từ chối; hồ sơ âm thầm đổi sang DNS công cộng vẫn kết nối, nhưng tên nội bộ không còn phân giải.

Xác nhận khả năng kết nối sau khi giao diện tồn tại

Chấp nhận của builder là điểm kiểm tra đầu tiên, không phải cuối cùng. Khi giao diện lên, LinkProperties.getDnsServers() báo cáo điều mạng thực sự nhận, và adb shell dumpsys connectivity in các thuộc tính đó từ bên ngoài.

Private DNS là điều khiển riêng và không đi qua đối số này; tên máy chủ của nhà cung cấp DoT thuộc cài đặt hệ thống, không phải trường IP.

Trước khi đổi parser hồ sơ lần nữa, ghi lại chuỗi thô và nguồn của nó, xác nhận đó là literal chứ không phải tên máy chủ, không phải loopback hay wildcard, và chuyển đối tượng đã phân tích.

Bài viết liên quan