VpnService protect() o l'exclusió de l'aplicació: què arregla l'encaminament
Protegiu el socket que transporta el túnel o excloeu tota l'aplicació: les tres condicions que decideixen quin abast necessita la vostra VPN d'Android.
Teniu dues maneres de mantenir els paquets propis del túnel fora del túnel: protegir el socket de transport o treure tota l'aplicació de la VPN. Resolen problemes diferents, i triar la més àmplia perquè és més fàcil de connectar és com les aplicacions perden l'accés a la xarxa local i el DNS dividit sense adonar-se'n. Comenceu pel que ha de quedar fora del túnel i trieu després l'abast més petit que ho aconsegueix.
Què treu protect() del túnel
VpnService.protect() és una crida per socket. Li passeu el socket o el descriptor que transporta el túnel, i Android marca aquest socket perquè el seu trànsit faci servir la xarxa subjacent en lloc de la interfície de la VPN. Tota la resta que envia l'aplicació continua seguint la ruta normal de la VPN.
Aquest abast estret és el punt. Un socket protegit no canvia com el vostre client HTTP resol noms, quina interfície fan servir les vostres crides d'analítica ni què passa amb una segona connexió oberta més tard.
Tres condicions que decideixen l'abast
La primera condició és si només ha d'escapar el transport del túnel. Si la resta del trànsit de l'aplicació ha de continuar passant per la VPN, la protecció del socket és l'abast correcte. La segona condició és si controleu la creació del socket. Una biblioteca que obre el seu propi socket ben endins no us deixa cap lloc per cridar protect() tret que exposi un hook o accepti un socket ja construït. La tercera condició és si l'aplicació ha d'arribar directament a la xarxa local o a un captive portal, que és un requisit diferent de mantenir viu el túnel.
Excloure l'aplicació amb addDisallowedApplication() és l'eina contundent. Treu de la VPN cada connexió que fa l'aplicació, incloent-hi les que volíeu inspeccionar, i en alguns dispositius també canvia com es comporta el descobriment local.
Una tercera opció és lligar el transport a una xarxa triada amb bindProcessToNetwork() o amb un identificador de xarxa a nivell de socket. Això és útil quan el dispositiu té Wi-Fi i mòbil alhora i voleu que el túnel vagi per un enllaç de pujada concret.
Crideu-lo mentre es crea el transport
Crideu protect() al mateix bloc que crea el transport, abans de connect() o send(). Si la crida retorna false, tanqueu el socket i feu fallar la configuració; continuar us deixa un transport els paquets del qual poden tornar a entrar a la vostra pròpia interfície.
La forma és prou petita per tenir-la en un sol lloc:
DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
transport.close();
throw new IOException("transport not protected");
}
transport.connect(serverAddress);Tres detalls mosseguen aquí. Primer,el booleà és l'únic senyal que rebeu, així que registreu la fallada amb prou context per distingir un consentiment revocat d'una crida rebutjada. Segon,protect() cobreix el socket, no el número de descriptor: si tanqueu el socket i més tard reutilitzeu el mateix enter per a un altre socket, el nou socket no queda protegit. Tercer, quan una biblioteca nativa obre el transport, protegiu el descriptor abans que el codi natiu es connecti, o la ruta potser ja està triada.La referència de VpnService és el lloc on confirmar quines sobrecàrregues ofereix realment el vostre nivell d'API objectiu.
Proveu el camí bloquejat, no només el camí feliç
Verifiqueu amb el túnel aixecat, perquè és l'únic estat en què els dos camins es poden diferenciar. Envieu una petició pel transport protegit i una altra des d'un camí de codi que mai no ha cridat protect(), i compareu què veu l'extrem remot: la connexió protegida ha d'arribar amb la xarxa subjacent com a origen, mentre que la no protegida mostra l'adreça pròpia del túnel i mou els comptadors de bytes del túnel. Al dispositiu,ss o netstat mostra la mateixa divisió abans que res surti de la ràdio.
Proveu la revocació per separat, perquè un canvi de consentiment enderroca la interfície sense tocar el codi del socket. Revogueu el consentiment de la VPN o atureu el servei, torneu a construir el transport i confirmeu que protect() encara retorna true i que el transport es reconnecta quan es torna a concedir el consentiment.
Si la petició protegida segueix el túnel igualment, la crida de protecció probablement passa després que el socket ja s'hagi encaminat. Moveu-la més amunt i torneu a provar.
Manteniu la decisió escrita al costat de la configuració del transport: quins sockets estan protegits, quin trànsit de l'aplicació està exclòs i què fa l'aplicació quan falla la protecció. Aquesta línia és el que evita que un refactor posterior ampliï l'exclusió a tota l'aplicació perquè una sola prova fallava. També dona a la persona següent una manera de comprovar la intenció sense fer enginyeria inversa del manifest.