Consigues una shell en el dispositivo, lees /etc/resolv.conf o desentierras un valor antiguo de net.dns1, y obtienes una dirección. La app que tienes delante resuelve por algo completamente distinto. En una versión actual de Android el resolutor es por red: el sistema mantiene una política para cada Network y la aplica al socket que hizo la petición. El archivo y la propiedad heredada son restos, y ninguno de los dos describe el camino que realmente siguió tu consulta.

Esa brecha aparece durante los despliegues de split tunnel y DNS privado, donde dos apps del mismo teléfono pueden ver respuestas distintas de forma legítima. Este recorrido empieza en la consulta errónea o ausente y retrocede hasta la configuración a la que pertenece.

El resolutor pertenece a una red, no al dispositivo

La resolución de nombres en Android 9 y posteriores pasa por un resolutor del sistema que se basa en la Network a la que está vinculado un socket. Cada red lleva sus propias LinkProperties, y esas propiedades contienen la lista de servidores DNS, los dominios de búsqueda y el estado del DNS privado solo para esa red. Nada de ese modelo se corresponde con una lista única para todo el dispositivo que puedas leer de un archivo.

Una VPN hace obvia la separación. Cuando sube el túnel, la app de VPN aporta servidores DNS registrados en la red propia de la VPN, así que las apps enrutadas por ella resuelven contra esos servidores mientras las apps excluidas siguen usando la red subyacente. Un dispositivo, dos caminos de respuesta, ninguna contradicción.

Leer la instantánea desde dentro de la app afectada

Pregunta a la propia app. Un método que tenga un Context y el permiso normal android.permission.ACCESS_NETWORK_STATE puede leer la instantánea sin tocar propiedades del sistema:

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?.privateDnsServerName

activeNetwork es la red predeterminada de tu proceso, no la única del dispositivo, y puede ser null cuando el llamador no tiene una predeterminada utilizable. Una VPN predeterminada bloqueada es la razón habitual. isPrivateDnsActive te dice si las consultas de esa red van por TLS a un resolutor con nombre, lo que cambia lo que mostrará una sonda UDP 53 simple. La superficie de API está documentada en LinkProperties, y cada valor que contiene es una instantánea de configuración, no la prueba de que una consulta concreta llegara a un servidor concreto.

Cuando la red predeterminada no es la que trabaja

La instantánea solo responde por la red predeterminada. Una app puede vincular su proceso o un solo socket a otra red, y entonces la consulta sigue esa elección mientras tu diagnóstico sigue informando de la predeterminada. Un navegador que trae su propio resolutor DNS-over-HTTPS nunca consulta la lista del sistema, así que sus respuestas no dicen nada del resto del dispositivo. Una app de perfil de trabajo se ejecuta bajo un usuario aparte con su propia política. Comprueba el enlace antes de culpar a los servidores.

La integración con VPN es la otra mitad. Recibir una línea dhcp-option DNS en un perfil de OpenVPN y registrar esa dirección en la red VPN de Android son dos pasos distintos, y una ruta ausente hacia el servidor falla con la misma discreción que una dirección equivocada. Desde una máquina de desarrollo, adb shell dumpsys connectivity imprime las mismas propiedades de enlace por red cuando la compilación lo permite, lo que confirma la lista sin escribir código.

Comparar la configuración con una consulta que puedas hacer

Elige un nombre que deba resolverse dentro del espacio privado y otro público, y anota ambos resultados junto a la instantánea: red, servidores configurados, estado del DNS privado y el texto exacto del error. Una consulta directa a un servidor con nombre demuestra que ese servidor te responde; no demuestra que la resolución normal de la aplicación lo eligiera. Una respuesta en caché no es una prueba nueva, así que vacía la caché o usa un nombre que nadie haya consultado hoy.

Si el resolutor solo atiende a clientes de una red de acceso concreta, lo que ve tras la salida de la VPN puede decidir el resultado, y eso es una cuestión de política para quien lo opera y no la prueba de que los operadores bloqueen el UDP 53. Donde el DNS privado está activo, un nombre de host sin cualificar puede rechazarse por diseño mientras el mismo servidor responde con normalidad a nombres públicos. Leer las propiedades te dice qué modo está vigente, así dejas de adivinar filtros de paquetes.

Corregir la capa a la que pertenece la discrepancia

Trabaja hacia arriba en lugar de editar el estado global. Si la app está vinculada a la red equivocada, corrige el enlace. Si el perfil trae la opción DNS equivocada, corrige el perfil. Si el DNS privado es estricto en una red cuyo resolutor no puede servir tu zona privada, eso es una decisión de gestión del dispositivo y no una razón para desactivar la función en todas partes.

Forzar resolutores públicos en un hook de DHCP o en una propiedad del sistema es la reparación que no deja de causar incidentes: los nombres privados dejan de resolverse, el cambio sobrevive a la sesión de VPN y la siguiente persona en el dispositivo encuentra una dirección obsoleta sin dueño. Mantén el cambio dentro de la VPN, la app o la capa de gestión a la que pertenece la discrepancia, y luego vuelve a leer la instantánea para confirmar que la lista de servidores cambió de verdad.

Lo que quieres al final es una petición de app ligada a una red, una lista de resolutores y un estado de DNS privado. Una vez que puedas mostrar esa cadena, una dirección conocida en un archivo deja de ser prueba de nada.

Artículos relacionados