A comprobación que a maioría das aplicacións de Android usan para detectar unha VPN é un nome de interface, e nunca foi unha comprobación de VPN. Informa de que existe algunha interface cunha cadea que o desenvolvedor esperaba, o que nun dispositivo significa OpenVPN e noutro significa algo sen relación ningunha. Un ping é o mesmo tipo de proba: demostra que un destino respondeu, non que un túnel transportase o paquete. O que a plataforma expón realmente é un conxunto de redes e os transportes que cada unha declara, e esa é outra pregunta con outra resposta.

Mito: se existe tun0, hai unha VPN activa

As aplicacións de VPN para Android son implementacións de VpnService que entregan ao sistema un descritor de ficheiro tun. O nome da interface procede de quen a creou: OpenVPN adoita producir tun0, PPTP e L2TP producen ppp0, WireGuard nomea a súa interface segundo o túnel, e un túnel IPsec pode non presentar ningún nome convencional. Por tanto, ler /proc/net/dev ou enumerar obxectos NetworkInterface busca unha convención, non un feito.

As dúas direccións de erro están vivas. Un dispositivo pode ter tun0 para un túnel local que non ten nada que ver coa VPN do usuario, e un túnel WireGuard perfectamente san pode ser invisible para unha comprobación que só coñece a cadea tun0. Os falsos positivos e os falsos negativos na mesma función fan que sexa inservible como diagnóstico, porque un ticket de soporte que di «non se detectou ningunha VPN» non é fiable en ningunha dirección.

O acceso tamén se estreitou. As versións máis recentes de Android restrinxen o que unha aplicación normal pode ler de /proc, así que unha comprobación baseada no nome que funcionaba en Android 8 pode devolver unha lista baleira nun dispositivo posterior sen ningún cambio de código. Cando a comprobación falla en silencio cara ao lado aberto, a aplicación conclúe que non hai VPN nun hardware onde o túnel está ben.

Mito: un ping correcto significa que o túnel transporta tráfico

Un ping a un host interno ten éxito sempre que exista unha ruta cara a ese host. Cun túnel dividido, o host pode ser accesible por Wi-Fi mentres o túnel está activo e fai algo completamente distinto. Cunha VPN por aplicación, a aplicación que fai o ping pode quedar excluída do túnel, así que o eco ICMP sae fóra del e volve, o que demostra o contrario do que a comprobación supón.

ICMP e TCP tamén se filtran por separado. Un túnel que permite solicitudes de eco pero descarta o porto TCP que necesita un servizo pasará a proba de ping e fallará na aplicación, e iso é unha regra de devasas no outro extremo, non unha propiedade do dispositivo. A única lectura honesta dun ping correcto é que este destino respondeu a este protocolo neste momento.

Que informa realmente a plataforma

Desde a API 21, ConnectivityManager.getAllNetworks() devolve todas as redes que o sistema coñece, e getNetworkCapabilities(network) informa do que declara esa rede. Unha rede VPN leva TRANSPORT_VPN. Percorrer todas as redes responde a unha pregunta: hai algunha VPN activa agora mesmo neste dispositivo?

Unha segunda pregunta, máis estreita, necesita outra chamada. getActiveNetwork() devolve a rede que o tráfico da aplicación usaría agora, e comprobar a mesma capacidade nela responde: pasaría o meu tráfico por unha VPN? As dúas poden discrepar, e a discrepancia é informativa. Unha VPN activa pero non actual apunta a encamiñamento por aplicación ou a unha configuración de túnel dividido, non a un túnel ausente.

Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
    && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);

Protexa ambas as chamadas. As capacidades poden ser null para unha rede que desapareceu entre a listaxe e a consulta, e a rede activa pode ser null durante unha transferencia. NET_CAPABILITY_NOT_VPN é útil na outra dirección, para confirmar que unha rede non é en absoluto un túnel en lugar de só carecer do indicador de transporte VPN.

NetworkInfo era o mesmo mito cunha API

ConnectivityManager.TYPE_VPN con NetworkInfo é a segunda xeración desta comprobación, e está obsoleta por unha razón. Nas versións modernas o obxecto devolto para unha VPN pode ser null mentres o túnel está conectado, así que o código que trata null como «sen VPN» informa en silencio dun falso negativo nos dispositivos onde a resposta máis importa.

A emisión que adoita acompañala tampouco é mellor. CONNECTIVITY_CHANGE ten unha historia documentada de transicións de VPN perdidas, incluídas compilacións nas que só se disparaba cando unha VPN se conectaba ou desconectaba en lugar de en cada cambio de rede. Un receptor construído sobre ela produce unha variable de estado que se afasta da visión da plataforma ata a seguinte reconexión completa.

Reaccione ás devolucións de chamada en lugar de sondar

Rexistre unha NetworkRequest que esixa TRANSPORT_VPN e elimine NET_CAPABILITY_NOT_VPN, e logo chame a registerNetworkCallback con esa solicitude. onAvailable e onLost dinlle entón cando cambia a declaración, que é o evento que realmente lle importa. Anule o rexistro da devolución de chamada cando o compoñente se deteña, ou a aplicación manterá unha referencia a unha pantalla morta e a devolución de chamada seguirá disparándose.

NetworkRequest req = new NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();
cm.registerNetworkCallback(req, callback);

Isto require ACCESS_NETWORK_STATE, que é un permiso normal: sen root, sen diálogo de consentimento do usuario, sen permiso de VPN. Sondar cun temporizador é a alternativa á que a xente recorre, e consome batería mentres segue perdendo unha transición máis curta que o intervalo, que é exactamente a transición que un usuario reporta.

Que segue sen poder dicirlle a resposta

Ningunha destas API informa de se o túnel está san, en que servidor termina ou se o tráfico dunha aplicación concreta está dentro. Unha rede VPN pode existir sen rutas utilizables, e un túnel por aplicación pode excluír a aplicación que fai a pregunta.

Así que rexistre o que viu a comprobación, non só o booleano. Rexistre o identificador de rede, os transportes declarados e se esa rede estaba activa. Un informe que di «non se detectou ningunha VPN» é inutilizable, mentres que un que di que unha rede declarou TRANSPORT_VPN pero getActiveNetwork() devolveu Wi-Fi apunta directamente ao encamiñamento ou a unha lista de exclusións, e iso é unha corrección sobre a que alguén pode traballar.

Artigos relacionados