Читайте Android DNS правильно: за мережею, а не в resolv.conf
Ваш застосунок Android розв’язує імена через resolver для кожної мережі, а не через resolv.conf, який ви щойно прочитали. Спершу перевірте LinkProperties, прив’язки сокетів і Private DNS.
Ви отримуєте shell на пристрої, читаєте /etc/resolv.conf або викопуєте старе значення net.dns1, і отримуєте одну адресу. Застосунок перед вами розв’язує імена зовсім через інше. У поточній версії Android resolver існує для кожної мережі: система тримає політику для кожного Network і застосовує її до сокета, який надіслав запит. Файл і стара властивість — залишки, і жодне з них не описує шлях, який ваш запит справді пройшов.
Ця прогалина проявляється під час упровадження split-tunnel і Private DNS, де два застосунки на одному телефоні можуть законно бачити різні відповіді. Цей посібник починається з хибного або відсутнього розв’язання і йде назад до конфігурації, якій воно належить.
Resolver належить мережі, а не пристрою
Розв’язання імен в Android 9 і новіших іде через системний resolver, який спирається на Network, до якої прив’язано сокет. Кожна мережа несе власні LinkProperties, і ці властивості містять список DNS-серверів, домени пошуку та стан Private DNS лише для цієї мережі. Ніщо в цій моделі не відповідає єдиному списку на весь пристрій, який можна прочитати з файлу.
VPN робить розділення очевидним. Коли тунель піднімається, застосунок VPN надає DNS-сервери, зареєстровані у власній мережі VPN, тож застосунки, спрямовані через нього, розв’язують імена через ці сервери, а виключені застосунки далі використовують мережу під ними. Один пристрій, два шляхи відповіді, жодного протиріччя.
Прочитайте знімок зсередини ураженого застосунку
Запитайте сам застосунок. Метод, який має Context і звичайний дозвіл android.permission.ACCESS_NETWORK_STATE, може прочитати знімок, не торкаючись системних властивостей:
val cm = getSystemService(ConnectivityManager::class.java) ?: return
val network = cm.activeNetwork
val links = network?.let { cm.getLinkProperties(it) }
val dnsServers = links?.dnsServers
val privateDnsActive = links?.isPrivateDnsActive
val privateDnsName = links?.privateDnsServerNameactiveNetwork — це мережа за замовчуванням для вашого процесу, а не єдина мережа на пристрої, і вона може бути null, коли у виклику немає придатної мережі за замовчуванням. Заблокована VPN за замовчуванням — звичайна причина. isPrivateDnsActive повідомляє, чи запити в цій мережі йдуть через TLS до іменованого resolver, що змінює те, що покаже проста проба UDP 53. Поверхня API описана в LinkProperties, і кожне значення в ній — знімок конфігурації, а не доказ, що конкретний запит дійшов до конкретного сервера.
Коли мережа за замовчуванням — не та, що виконує роботу
Знімок відповідає лише за мережу за замовчуванням. Застосунок може прив’язати свій процес або один сокет до іншої мережі, і тоді розв’язання йде за цим вибором, поки ваша діагностика все ще повідомляє про мережу за замовчуванням. Браузер із власним DNS-over-HTTPS resolver взагалі не звертається до системного списку, тож його відповіді нічого не кажуть про решту пристрою. Застосунок робочого профілю працює під окремим користувачем зі своєю політикою. Перевірте прив’язку, перш ніж звинувачувати сервери.
Інтеграція з VPN — друга половина. Отримати рядок dhcp-option DNS у профілі OpenVPN і зареєструвати цю адресу в мережі VPN Android — це два окремі кроки, і відсутній маршрут до сервера збоїть так само тихо, як неправильна адреса. З машини розробника adb shell dumpsys connectivity друкує ті самі властивості з’єднання за мережею, коли збірка це дозволяє, що підтверджує список без написання коду.
Порівняйте конфігурацію з одним розв’язанням, яке вам дозволено
Виберіть ім’я, яке має розв’язуватися в приватному просторі імен, і одне публічне ім’я, і запишіть обидва результати поряд зі знімком: мережа, налаштовані сервери, стан Private DNS і точний текст помилки. Прямий запит до іменованого сервера доводить, що цей сервер вам відповідає; він не доводить, що звичайне розв’язання застосунку вибрало його. Відповідь із кешу — не свіжий тест, тому очистіть кеш або використайте ім’я, яке сьогодні ніхто не запитував.
Якщо resolver обслуговує лише клієнтів з певної мережі доступу, те, що він бачить після виходу з VPN, може вирішити результат, і це питання політики для того, хто ним керує, а не доказ, що оператори блокують UDP 53. Там, де Private DNS активний, некваліфіковане ім’я хоста може бути відхилене за задумом, поки той самий сервер нормально відповідає на публічні імена. Читання властивостей каже вам, який режим діє, тож ви перестаєте вгадувати фільтри пакетів.
Виправте шар, якому належить невідповідність
Працюйте вгору, а не редагуйте глобальний стан. Якщо застосунок прив’язаний до неправильної мережі, виправте прив’язку. Якщо профіль надає неправильну опцію DNS, виправте профіль. Якщо Private DNS суворий у мережі, чий resolver не може обслуговувати вашу приватну зону, це рішення керування пристроєм, а не причина вимикати функцію всюди.
Проштовхування публічних resolver-ів у DHCP-хук або системну властивість — це виправлення, яке постійно спричиняє інциденти: приватні імена перестають розв’язуватися, зміна переживає сесію VPN, і наступна людина на пристрої знаходить застарілу адресу без власника. Тримайте зміну всередині VPN, застосунку або шару керування, якому належить невідповідність, а потім перечитайте знімок, щоб підтвердити, що список серверів справді змінився.
Наприкінці ви хочете один запит застосунку, прив’язаний до однієї мережі, списку resolver-ів і стану Private DNS. Щойно ви можете показати цей ланцюг, знайома адреса у файлі перестає бути доказом чогось.