Tunelové pakety můžete držet mimo tunel dvěma způsoby: chránit transportní socket, nebo vytáhnout celou aplikaci z VPN. Řeší různé problémy a volba širšího proto, že se snáz zapojí, je přesně to, jak aplikace ztratí přístup k lokální síti a rozdělený DNS, aniž by si toho všimly. Začněte tím, co musí zůstat mimo tunel, a pak zvolte nejmenší rozsah, který to splní.

Co protect() z tunelu odstraňuje

VpnService.protect() je volání pro jeden socket. Předáte mu socket nebo deskriptor, který nese tunel, a Android tento socket označí, aby jeho provoz používal podkladovou síť místo rozhraní VPN. Všechno ostatní, co aplikace odešle, dál sleduje obvyklou trasu VPN.

Právě ten úzký rozsah je smyslem. Chráněný socket nemění, jak váš HTTP klient překládá názvy, které rozhraní používají vaše analytické požadavky, ani co se stane s druhým připojením otevřeným později.

Tři podmínky, které určují rozsah

První podmínka je, zda musí uniknout jen transport tunelu. Pokud ostatní provoz aplikace má dál procházet VPN, ochrana socketu je správný rozsah. Druhá podmínka je, zda řídíte vytváření socketu. Knihovna, která otevírá vlastní socket hluboko uvnitř, vám nedá místo, kde zavolat protect(), ledaže zpřístupní hook nebo přijme už hotový socket. Třetí podmínka je, zda aplikace musí přímo dosáhnout lokální sítě nebo captive portálu, což je jiný požadavek než udržet tunel živý.

Vyloučení aplikace pomocí addDisallowedApplication() je tupý nástroj. Odstraní z VPN každé spojení, které aplikace naváže, včetně těch, která jste chtěli sledovat, a na některých zařízeních mění i chování lokálního objevování.

Třetí možností je připoutat transport k vybrané síti pomocí bindProcessToNetwork() nebo síťového handle na úrovni socketu. To se hodí, když má zařízení současně Wi-Fi i mobilní síť a chcete, aby tunel jel po konkrétním vzestupném kanálu.

Volejte ho ve chvíli, kdy transport vzniká

Zavolejte protect() ve stejném bloku, který transport vytváří, před connect() nebo send(). Pokud volání vrátí false, socket zavřete a nastavení nechte selhat; pokračování vám nechá transport, jehož pakety mohou znovu vejít do vašeho vlastního rozhraní.

Tvar je dost malý na to, aby zůstal na jednom místě:

DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
    transport.close();
    throw new IOException("transport not protected");
}
transport.connect(serverAddress);

Tady koušou tři detaily. Za prvé,boolean je jediný signál, který dostanete, takže selhání zalogujte s dostatkem kontextu, abyste rozeznali odvolaný souhlas od odmítnutého volání. Za druhé,protect() pokrývá socket, ne číslo deskriptoru: pokud socket zavřete a později stejné celé číslo použijete pro jiný socket, nový socket chráněný není. Za třetí, když transport otevře nativní knihovna, chraňte deskriptor dřív, než se nativní kód připojí, jinak už může být trasa zvolená.Referenční stránka VpnService je místo, kde ověříte, které přetížené varianty vaše cílové API skutečně poskytuje.

Testujte zablokovanou cestu, ne jen tu šťastnou

Ověřujte se zvednutým tunelem, protože to je jediný stav, ve kterém se obě cesty mohou lišit. Pošlete jeden požadavek chráněným transportem a jeden z cesty kódu, která nikdy nezavolala protect(), a pak porovnejte, co vidí vzdálený konec: chráněné spojení má dorazit s podkladovou sítí jako zdrojem, zatímco nechráněné ukazuje vlastní adresu tunelu a hýbe jeho bajtovými počítadly. Na zařízení ss nebo netstat ukáže stejné rozdělení dřív, než cokoli opustí rádio.

Odvolání testujte zvlášť, protože změna souhlasu zboří rozhraní, aniž by se dotkla vašeho kódu pro socket. Odvolejte souhlas s VPN nebo zastavte službu, znovu sestavte transport a potvrďte, že protect() pořád vrací true a že se transport znovu připojí, jakmile je souhlas znovu udělen.

Pokud chráněný požadavek tunel stejně sleduje, volání ochrany pravděpodobně probíhá až poté, co byl socket už nasměrován. Přesuňte ho dřív a otestujte to znovu.

Rozhodnutí držte zapsané vedle nastavení transportu: které sockety jsou chráněné, který provoz aplikace je vyloučený a co aplikace dělá, když ochrana selže. Právě tento řádek brání pozdějšímu refaktoru rozšířit vyloučení na celou aplikaci proto, že jeden test selhával. Zároveň dává dalšímu člověku způsob, jak zkontrolovat záměr bez reverzního inženýrství manifestu.

Související články