VpnService protect() eller app-udelukkelse: hvad retter routingen
Beskyt den socket, der bærer tunnelen, eller udeluk hele appen: de tre betingelser, der afgør, hvilket omfang din Android-VPN har brug for.
Du har to måder at holde tunnelens egne pakker uden for tunnelen: beskyt transportsocketten, eller tag hele appen ud af VPN'en. De løser forskellige problemer, og at vælge den bredere, fordi den er lettere at koble op, er netop måden, apps mister adgang til det lokale netværk og delt DNS uden at opdage det. Start med det, der skal blive uden for tunnelen, og vælg så det mindste omfang, der opnår det.
Hvad protect() fjerner fra tunnelen
VpnService.protect() er et kald for hver socket. Du giver den den socket eller deskriptor, der bærer tunnelen, og Android markerer den socket, så dens trafik bruger det underliggende netværk i stedet for VPN-grænsefladen. Alt andet, appen sender, følger stadig den normale VPN-rute.
Netop dette snævre omfang er pointen. En beskyttet socket ændrer ikke, hvordan din HTTP-klient opløser navne, hvilken grænseflade dine analysekald bruger, eller hvad der sker med en anden forbindelse, der åbnes senere.
Tre betingelser, der afgør omfanget
Den første betingelse er, om kun tunneltransporten skal slippe ud. Hvis appens øvrige trafik stadig skal gå gennem VPN'en, er socketbeskyttelse det rigtige omfang. Den anden betingelse er, om du styrer oprettelsen af socketten. Et bibliotek, der åbner sin egen socket dybt inde, giver dig ikke et sted at kalde protect(), medmindre det udstiller et hook eller accepterer en færdigbygget socket. Den tredje betingelse er, om appen skal nå det lokale netværk eller en captive portal direkte, hvilket er et andet krav end at holde tunnelen i live.
At udelukke appen med addDisallowedApplication() er det grove værktøj. Det fjerner hver forbindelse, appen laver, fra VPN'en, inklusive dem du ville inspicere, og på nogle enheder ændrer det også, hvordan lokal opdagelse opfører sig.
En tredje mulighed er at binde transporten til et valgt netværk med bindProcessToNetwork() eller et netværkshåndtag på socketniveau. Det er nyttigt, når enheden har Wi-Fi og mobil på samme tid, og du vil have tunnelen til at køre over en bestemt uplink.
Kald det, mens transporten oprettes
Kald protect() i samme blok, som opretter transporten, før connect() eller send(). Hvis kaldet returnerer false, så luk socketten og lad opsætningen fejle; at fortsætte efterlader dig med en transport, hvis pakker kan komme tilbage i din egen grænseflade.
Formen er lille nok til at holde på ét sted:
DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
transport.close();
throw new IOException("transport not protected");
}
transport.connect(serverAddress);Tre detaljer bider her. For det første,boolean er det eneste signal, du får, så log fejlen med nok kontekst til at skelne en tilbagekaldt samtykke fra et afvist kald. For det andet,protect() dækker socketten, ikke deskriptornummeret: hvis du lukker socketten og senere genbruger det samme heltal til en anden socket, er den nye socket ikke beskyttet. For det tredje, når et native bibliotek åbner transporten, skal du beskytte deskriptoren, før den native kode forbinder, ellers kan ruten allerede være valgt.VpnService-referencen er stedet, hvor du bekræfter, hvilke overbelastninger dit mål-API-niveau faktisk tilbyder.
Test den blokerede vej, ikke kun den glade
Verificer med tunnelen oppe, fordi det er den eneste tilstand, hvor de to veje kan adskille sig. Send én anmodning gennem den beskyttede transport og én fra en kodesti, der aldrig kaldte protect(), og sammenlign så, hvad den fjerne ende ser: den beskyttede forbindelse bør ankomme med det underliggende netværk som kilde, mens den ubeskyttede viser tunnelens egen adresse og flytter tunnelens byte-tællere. På enheden viser ss eller netstat den samme opdeling, før noget forlader radioen.
Test tilbagekaldelse separat, fordi en ændring af samtykke river grænsefladen ned uden at røre din socketkode. Tilbagekald VPN-samtykket eller stop tjenesten, genopbyg transporten, og bekræft, at protect() stadig returnerer true, og at transporten forbinder igen, når samtykket gives igen.
Hvis den beskyttede anmodning alligevel følger tunnelen, sker beskyttelseskaldet sandsynligvis, efter at socketten allerede er rutet. Flyt det tidligere og test igen.
Hold beslutningen nedskrevet ved siden af transportopsætningen: hvilke sockets er beskyttet, hvilken apptrafik er udelukket, og hvad appen gør, når beskyttelsen fejler. Netop den linje forhindrer en senere refaktorering i at udvide udelukkelsen til hele appen, fordi en enkelt test fejlede. Den giver også den næste person en måde at tjekke hensigten på uden at reverse-engineere manifestet.