VpnService protect() ili izuzimanje aplikacije: šta ispravlja rutiranje
Zaštitite socket koji nosi tunel ili izuzmite cijelu aplikaciju: tri uslova koja određuju koji obim je potreban vašem Android VPN-u.
Imate dva načina da zadržite vlastite pakete tunela izvan tunela: zaštitite transportni socket ili izbacite cijelu aplikaciju iz VPN-a. Oni rješavaju različite probleme, a biranje šireg zato što ga je lakše povezati je način na koji aplikacije izgube pristup lokalnoj mreži i podijeljeni DNS a da to ne primijete. Pođite od onoga što mora ostati izvan tunela, pa odaberite najmanji obim koji to postiže.
Šta protect() uklanja iz tunela
VpnService.protect() je poziv po socketu. Predate mu socket ili deskriptor koji nosi tunel, a Android označi taj socket tako da njegov saobraćaj koristi donju mrežu umjesto VPN interfejsa. Sve ostalo što aplikacija pošalje i dalje slijedi uobičajenu VPN rutu.
Upravo taj uski obim je poenta. Zaštićeni socket ne mijenja kako vaš HTTP klijent razrješava imena, koji interfejs koriste vaši analitički pozivi, ni šta se dogodi s drugom vezom otvorenom kasnije.
Tri uslova koja određuju obim
Prvi uslov je mora li pobjeći samo transport tunela. Ako ostatak saobraćaja aplikacije i dalje treba ići kroz VPN, zaštita socketa je ispravan obim. Drugi uslov je kontrolišete li stvaranje socketa. Biblioteka koja otvara vlastiti socket duboko unutra ne ostavlja vam mjesto da pozovete protect() osim ako izloži hook ili prihvati već izgrađen socket. Treći uslov je mora li aplikacija direktno doći do lokalne mreže ili captive portala, što je drukčiji zahtjev od održavanja tunela živim.
Izuzimanje aplikacije pomoću addDisallowedApplication() je tupi alat. Ono uklanja iz VPN-a svaku vezu koju aplikacija napravi, uključujući one koje ste htjeli posmatrati, a na nekim uređajima mijenja i ponašanje lokalnog otkrivanja.
Treća opcija je da transport vežete za odabranu mrežu pomoću bindProcessToNetwork() ili mrežne ručke na nivou socketa. To je korisno kada uređaj ima Wi-Fi i mobilnu mrežu istovremeno i želite da tunel ide određenim uzlaznim kanalom.
Pozovite ga dok se transport stvara
Pozovite protect() u istom bloku koji stvara transport, prije connect() ili send(). Ako poziv vrati false, zatvorite socket i oborite postavljanje; nastavak vas ostavlja s transportom čiji paketi mogu ponovo ući u vaš vlastiti interfejs.
Oblik je dovoljno mali da stoji na jednom mjestu:
DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
transport.close();
throw new IOException("transport not protected");
}
transport.connect(serverAddress);Ovdje ujedaju tri detalja. Prvo,boolean je jedini signal koji dobijate, pa zabilježite neuspjeh s dovoljno konteksta da razlikujete povučenu saglasnost od odbijenog poziva. Drugo,protect() pokriva socket, a ne broj deskriptora: ako zatvorite socket i kasnije ponovo upotrijebite isti cijeli broj za drugi socket, novi socket nije zaštićen. Treće, kada native biblioteka otvori transport, zaštitite deskriptor prije nego se native kod poveže, inače ruta možda već je odabrana.Referenca za VpnService je mjesto gdje potvrđujete koja preopterećenja vaš ciljni API nivo zaista pruža.
Testirajte blokiranu putanju, ne samo sretnu
Provjerite s podignutim tunelom, jer je to jedino stanje u kojem se dvije putanje mogu razlikovati. Pošaljite jedan zahtjev kroz zaštićeni transport i jedan iz koda koji nikad nije pozvao protect(), pa uporedite šta vidi daleki kraj: zaštićena veza treba doći s donjom mrežom kao izvorom, dok nezaštićena pokazuje vlastitu adresu tunela i pomjera njegove brojače bajtova. Na uređaju ss ili netstat pokazuje istu podjelu prije nego išta napusti radio.
Testirajte povlačenje odvojeno, jer promjena saglasnosti ruši interfejs ne dirajući vaš kod za socket. Povucite saglasnost za VPN ili zaustavite servis, ponovo izgradite transport i potvrdite da protect() još vraća true i da se transport ponovo povezuje kad se saglasnost opet da.
Ako zaštićeni zahtjev ipak slijedi tunel, poziv zaštite vjerovatno se događa nakon što je socket već rutiran. Pomjerite ga ranije i testirajte ponovo.
Držite odluku zapisanu uz postavljanje transporta: koji su sockete zaštićeni, koji je saobraćaj aplikacije izuzet, i šta aplikacija radi kada zaštita ne uspije. Upravo taj red sprječava kasniji refaktor da proširi izuzimanje na cijelu aplikaciju zato što je jedan test padao. On takođe daje sljedećoj osobi način da provjeri namjeru bez obrnutog inženjeringa manifesta.