Lire le DNS Android correctement : par réseau, pas dans resolv.conf
Votre app Android résout via un résolveur propre à chaque réseau, pas via le resolv.conf que vous venez de lire. Inspectez d’abord LinkProperties, les liaisons de socket et le DNS privé.
Vous obtenez un shell sur l’appareil, vous lisez /etc/resolv.conf ou vous dénichez une ancienne valeur net.dns1, et vous obtenez une adresse. L’app en face de vous résout par un tout autre chemin. Sur une version récente d’Android, le résolveur est propre à chaque réseau : le système détient une politique pour chaque Network et l’applique au socket qui a émis la requête. Le fichier et l’ancienne propriété sont des vestiges, et ni l’un ni l’autre ne décrit le chemin qu’a réellement pris votre requête.
Cet écart apparaît lors des déploiements de split tunnel et de DNS privé, où deux apps du même téléphone peuvent légitimement voir des réponses différentes. Cette analyse part de la résolution erronée ou absente et remonte vers la configuration qui en est responsable.
Le résolveur appartient à un réseau, pas à l’appareil
La résolution de noms sous Android 9 et versions ultérieures passe par un résolveur système qui s’appuie sur le Network auquel un socket est lié. Chaque réseau porte ses propres LinkProperties, et ces propriétés contiennent la liste des serveurs DNS, les domaines de recherche et l’état du DNS privé pour ce réseau seul. Rien dans ce modèle ne correspond à une liste unique à l’échelle de l’appareil que vous pourriez lire dans un fichier.
Un VPN rend la séparation évidente. Quand le tunnel monte, l’app VPN fournit des serveurs DNS enregistrés sur le réseau propre du VPN, si bien que les apps routées par lui résolvent auprès de ces serveurs tandis que les apps exclues continuent d’utiliser le réseau sous-jacent. Un appareil, deux chemins de réponse, aucune contradiction.
Lire l’instantané depuis l’app concernée
Interrogez l’app elle-même. Une méthode disposant d’un Context et de la permission normale android.permission.ACCESS_NETWORK_STATE peut lire l’instantané sans toucher aux propriétés système :
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 est le réseau par défaut de votre processus, pas le seul réseau de l’appareil, et il peut être null quand l’appelant n’a pas de défaut utilisable. Un VPN par défaut bloqué en est la raison habituelle. isPrivateDnsActive vous indique si les requêtes sur ce réseau passent par TLS vers un résolveur nommé, ce qui change ce qu’une simple sonde UDP 53 vous montrera. La surface d’API est documentée sous LinkProperties, et chaque valeur qu’elle contient est un instantané de configuration et non la preuve qu’une requête donnée a atteint un serveur donné.
Quand le réseau par défaut n’est pas celui qui travaille
L’instantané ne répond que pour le réseau par défaut. Une app peut lier son processus ou un socket unique à un autre réseau, et la résolution suit alors ce choix tandis que votre diagnostic continue de signaler le défaut. Un navigateur qui embarque son propre résolveur DNS-over-HTTPS ne consulte jamais la liste système, donc ses réponses ne disent rien du reste de l’appareil. Une app de profil professionnel s’exécute sous un autre utilisateur avec sa propre politique. Vérifiez la liaison avant d’accuser les serveurs.
L’intégration VPN est l’autre moitié du sujet. Recevoir une ligne dhcp-option DNS dans un profil OpenVPN et enregistrer cette adresse sur le réseau VPN Android sont deux étapes distinctes, et une route manquante vers le serveur échoue aussi discrètement qu’une mauvaise adresse. Depuis une machine de développement, adb shell dumpsys connectivity affiche les mêmes propriétés de lien par réseau quand la compilation le permet, ce qui confirme la liste sans écrire de code.
Comparer la configuration avec une résolution que vous avez le droit de faire
Choisissez un nom qui doit résoudre dans l’espace privé et un nom public, puis notez les deux résultats à côté de l’instantané : réseau, serveurs configurés, état du DNS privé et texte exact de l’erreur. Une requête directe vers un serveur nommé prouve que ce serveur vous répond ; elle ne prouve pas que la résolution applicative ordinaire l’a choisi. Une réponse en cache n’est pas un test frais : videz le cache ou utilisez un nom que personne n’a interrogé aujourd’hui.
Si le résolveur ne sert que les clients d’un réseau d’accès particulier, ce qu’il voit après la sortie VPN peut décider de l’issue, et c’est une question de politique pour celui qui l’exploite, non la preuve que les opérateurs bloquent l’UDP 53. Là où le DNS privé est actif, un nom d’hôte non qualifié peut être refusé par conception alors que le même serveur répond normalement aux noms publics. Lire les propriétés vous dit quel mode est en vigueur, et vous cessez de deviner des filtres de paquets.
Corriger la couche qui possède l’écart
Remontez au lieu de modifier l’état global. Si l’app est liée au mauvais réseau, corrigez la liaison. Si le profil fournit la mauvaise option DNS, corrigez le profil. Si le DNS privé est strict sur un réseau dont le résolveur ne peut pas servir votre zone privée, c’est une décision d’administration d’appareil, pas une raison de désactiver la fonctionnalité partout.
Forcer des résolveurs publics dans un hook DHCP ou une propriété système est la réparation qui ne cesse de provoquer des incidents : les noms privés cessent de résoudre, le changement survit à la session VPN, et la personne suivante sur l’appareil trouve une adresse périmée sans propriétaire. Gardez la modification dans le VPN, l’app ou la couche de gestion qui possède l’écart, puis relisez l’instantané pour confirmer que la liste des serveurs a réellement changé.
Ce que vous voulez à la fin, c’est une requête d’app reliée à un réseau, une liste de résolveurs et un état de DNS privé. Une fois cette chaîne démontrée, une adresse familière dans un fichier ne prouve plus rien.