Engadiches un dominio á lista de permitidos, a consulta chega á túa aplicación, o comparador devolve permitido e a páxina segue sen cargar nunca. Nese punto a maioría da xente comeza a reescribir o comparador e a engadir comparacións con comodíns. Pola miña experiencia, o comparador adoita ser correcto e o fallo está unha capa máis abaixo: na resposta DNS que sintetizaches, na ruta que instalaches ou no socket que usaches para reenviar a consulta. A lista é a parte fácil; o camiño do paquete é onde se rompen as listas de permitidos.

O comparador raramente é o culpable

O primeiro que hai que separar é bloqueado de roto. Un nome bloqueado debería fallar rápido e de forma visible, porque o navegador recibe unha resposta que di que o nome non existe e detense. Un nome roto colga, reintenta por IPv6, pasa a outro transporte e déixache cun indicador xirando. Se ves un indicador xirando, a túa lista de permitidos probablemente responde algo co que o cliente non pode facer nada.

O rexistro A baleiro é o erro clásico. Devolver NOERROR con cero respostas parece unha resposta válida, así que o resolutor stub acéptaa, o navegador volve tentalo, proba AAAA, proba outro resolutor e rendese moito máis tarde. Un código de resposta 3, NXDOMAIN, coa sección de pregunta repetida e o identificador de transacción copiado, remata a conversa de inmediato. Úsao para cada nome que rexeites a propósito.

IPv6 merece a mesma decisión e non un valor por defecto. Se respondes NXDOMAIN para A e deixas pasar AAAA, o cliente pode alcanzar o nome bloqueado por IPv6 e o filtro ten unha fuga. Algunhas implementacións responden ás consultas AAAA bloqueadas cun enderezo de loopback para que a conexión falle de inmediato en vez de caer nunha alternativa. Escolle unha política, aplícaa aos dous tipos de rexistro e rexistra ambos.

Unha ruta que o captura todo posúeo todo

Un deseño só de DNS nunca engade addRoute("0.0.0.0", 0), así que só as consultas entran na interface TUN e todo o demais segue usando a rede subxacente. Esa é a forma segura, e tamén explica por que algunhas listas de permitidos parecen funcionar en Wi-Fi e fallan na rede móbil.

No momento en que existe unha ruta por defecto, cada fluxo é teu. Un paquete TCP que chega á interface e nin se reenvía nin se responde queda na cola ata que o cliente esgota o tempo, e non hai ningún erro que rexistrar porque nada fallou. Vin a alguén pasar unha tarde cunha lista de permitidos rota que en realidade era un reenviador que faltaba para o porto 443. Rexistra o protocolo e o destino de cada paquete que decidas descartar.

O socket do resolutor ten que saír do túnel

O reenvío é onde está a seguinte trampa. Os resolutores do operador adoitan vivir nun enderezo privado que o túnel non alcanza, así que a consulta que reenvías non vai a ningún sitio e a aplicación informa dun tempo de espera de lectura. Envía as consultas aprobadas a un resolutor que saibas que é accesible, e pasa primeiro ese socket a VpnService.protect(). Un socket sen protexer devolve a consulta ao teu propio túnel, onde volve chegar á aplicación e xira ata que salta o tempo de espera.

O fallo parece idéntico a unha caída de rede, e por iso sobrevive tanto tempo nos informes de erros. Mantén contadores de consultas enviadas e respostas recibidas: se o primeiro número sobe e o segundo queda en cero, estás mirando o socket, non a lista.

O tamaño e as sumas de verificación fallan sen mensaxe

Os descartes silenciosos tamén veñen do tamaño. Unha resposta DNS cun conxunto grande de respostas, ou unha aperta de mans TCP con opcións xenerosas, pode superar unha MTU de ruta que nunca fixaches explicitamente; o fragmento desaparece augas arriba e o cliente ou ben reintenta máis pequeno ou deixa de tentar. Fixar a MTU da interface en 1280 é unha escolla conservadora que sobrevive á maioría das rutas móbiles, e ser explícito é mellor que herdar un valor por defecto que ninguén mediu.

Se constrúes os paquetes ti mesmo, a suma de verificación é o outro asasino silencioso. Unha suma UDP calculada sobre unha pseudo-cabeceira que omite a lonxitude UDP ou o campo de protocolo produce un paquete que no teu lado parece ben formado e que o receptor descarta sen rastro. Compara a túa pseudo-cabeceira coa especificación campo por campo, e imprime a suma calculada para un fluxo coñecido mentres depuras.

O que un VpnService non fará por ti

Paga a pena coñecer dous límites antes de deseñar arredor deles. Ningunha aplicación normal pode instalar un proxy ou un ficheiro PAC de todo o sistema fóra do Wi-Fi, e esa parte non cambiou. O camiño por aplicación si: antes da API 29 VpnService.Builder non podía levar ningún proxy, e desde a API 29 setHttpProxy(ProxyInfo.buildPacProxy(uri)) entrega á plataforma un script PAC para avaliar para a rede VPN. Avalía as regras no teu propio código cando apuntes a API máis antigas ou cando o modelo PAC non poida expresar a regra. UDP tampouco é de balde: os navegadores só envían TCP a través dun proxy SOCKS, e a maioría dos clientes SOCKS5 nunca implementan UDP ASSOCIATE, así que un deseño baseado en proxy descarta ou filtra UDP mentres TCP parece san.

Ese segundo límite é a razón pola que un protocolo baseado en UDP pode funcionar nunha rede normal e fallar no momento en que a túa VPN se conecta. Se se espera que o túnel leve UDP, o reenviador ten que implementar o camiño de reenvío para iso, non só o camiño TCP cun tipo de socket distinto.

Comproba nesta orde

Percorre a lista en orde e detente na primeira etapa que falle, porque toda comprobación posterior é ruído ata que a anterior aguante.

  1. Enumera as rutas instaladas e confirma se os paquetes que non son DNS entran na interface TUN.
  2. Rexistra o nome da consulta, a decisión e o código de resposta para un nome bloqueado e un aprobado.
  3. Confirma que o socket do resolutor está protexido e que o resolutor escollido responde desde dentro do túnel.
  4. Confirma a MTU da interface e a suma de verificación dunha resposta que construíches a man.
  5. Repite a mesma páxina en Wi-Fi e en rede móbil, porque os resolutores do operador e a MTU de ruta difiren.

Se as cinco se cumpren e a páxina segue colgada, o seguinte sospeitoso é o propio reenviador: compara unha petición capturada cunha resposta capturada e busca un reconto de bytes que falta ou un socket medio pechado. A referencia de VpnService.Builder cobre os contratos de ruta, DNS e MTU, e protect() é unha chamada VpnService aparte e non unha opción do builder; unha interface conectada só significa que o primeiro contrato se cumpriu.

Artigos relacionados