VPN-i tuvastamine Androidis: miks tun0 ja ping eksitavad
Liidese nimi on kokkulepe; ping tõestab, et üks host oli kättesaadav. Küsige NetworkCapabilitiesilt, mida võrk deklareerib, ja reageerige seejärel tagasikutsetele.
Kontroll, mida enamik Androidi rakendusi VPN-i tuvastamiseks kasutab, on liidese nimi ja see ei ole kunagi olnud VPN-i kontroll. See teatab, et mingi liides eksisteerib arendaja oodatud stringiga, mis ühes seadmes tähendab OpenVPN-i ja teises midagi täiesti seostamatut. Ping on sama tüüpi tõend: see tõestab, et üks sihtkoht vastas, mitte et tunnel paketi kohale viis. Mida platvorm tegelikult välja pakub, on hulk võrke ja transporti, mida igaüks neist deklareerib, ja see on teine küsimus teise vastusega.
Müüt: kui tun0 on olemas, on VPN üleval
Androidi VPN-rakendused on VpnService'i implementatsioonid, mis annavad süsteemile tun-faili deskriptori. Liidese nimi tuleb sellelt, kes selle lõi: OpenVPN loob tavaliselt tun0, PPTP ja L2TP loovad ppp0, WireGuard nimetab oma liidese tunneli järgi ja IPsec-tunnelil ei pruugi üldse tavapärast nime olla. Seega /proc/net/dev lugemine või NetworkInterface'i objektide loendamine otsib kokkulepet, mitte fakti.
Mõlemad veasuunad on elus. Seadmes võib olla tun0 kohaliku tunneli jaoks, millel pole kasutaja VPN-iga mingit seost, ja täiesti terve WireGuard-tunnel võib olla nähtamatu kontrollile, mis teab ainult stringi tun0. Valepositiivsed ja valenegatiivsed tulemused samas funktsioonis muudavad selle diagnostikavahendina kasutuskõlbmatuks, sest tugipiletile, mis ütleb „VPN-i ei tuvastatud“, ei saa kummaski suunas usaldada.
Juurdepääs on ka kitsenenud. Uuemad Androidi versioonid piiravad, mida tavaline rakendus saab /proc-ist lugeda, nii et nimepõhine kontroll, mis töötas Androidis 8, võib hilisemas seadmes tagastada tühja loendi ilma igasuguse koodimuudatuseta. Kui kontroll ebaõnnestub vaikselt avatud suunas, järeldab rakendus, et riistvaras, kus tunnel on korras, VPN-i pole.
Müüt: õnnestunud ping tähendab, et tunnel edastab liiklust
Ping sisehostile õnnestub alati, kui selle hostini on marsruut olemas. Jaotatud tunneliga võib host olla Wi-Fi kaudu kättesaadav, samal ajal kui tunnel on üleval ja teeb midagi hoopis muud. Rakendusepõhise VPN-iga võib pingi tegev rakendus olla tunnelist välja jäetud, nii et ICMP kaja väljub sellest väljapoole ja tuleb tagasi, mis tõestab vastupidist sellele, mida kontroll eeldab.
ICMP-d ja TCP-d filtreeritakse samuti eraldi. Tunnel, mis lubab kajapäringuid, kuid jätab kõrvale TCP-pordi, mida teenus vajab, läbib pingi testi ja ebaõnnestub rakenduses, ning see on tulemüürireegel teisel pool, mitte seadme omadus. Õnnestunud pingi ainus aus lugemine on see, et see sihtkoht vastas sellele protokollile praegusel hetkel.
Mida platvorm tegelikult teatab
Alates API-st 21 tagastab ConnectivityManager.getAllNetworks() iga võrgu, mida süsteem teab, ja getNetworkCapabilities(network) teatab, mida see võrk deklareerib. VPN-võrk kannab TRANSPORT_VPN-i. Kõigi võrkude läbimine vastab ühele küsimusele: kas selles seadmes on praegu mõni VPN üleval?
Teine, kitsam küsimus nõuab teist kutset. getActiveNetwork() tagastab võrgu, mida rakenduse liiklus praegu kasutaks, ja sama võimekuse kontrollimine sellel vastab: kas minu liiklus läheks VPN-i kaudu? Need kaks võivad lahkneda ja lahknevus on informatiivne. VPN, mis on üleval, kuid mitte aktiivne, viitab rakendusepõhisele marsruutimisele või jaotatud tunneli konfiguratsioonile, mitte puuduvale tunnelile.
Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Kaitske mõlemat kutset. Võimekused võivad olla null võrgu puhul, mis kadus loendamise ja pärimise vahel, ja aktiivne võrk võib olla null üleandmise ajal. NET_CAPABILITY_NOT_VPN on kasulik vastassuunas, et kinnitada, et võrk kindlasti ei ole tunnel, mitte ainult et sellel puudub VPN-transpordi lipp.
NetworkInfo oli sama müüt API-ga
ConnectivityManager.TYPE_VPN koos NetworkInfoga on selle kontrolli teine põlvkond ja see on põhjusega aegunud. Kaasaegsetes versioonides võib VPN-i jaoks tagastatud objekt olla null, samal ajal kui tunnel on ühendatud, nii et kood, mis käsitleb null-i kui „VPN puudub“, teatab vaikselt valenegatiivse tulemuse seadmetes, kus vastus on kõige olulisem.
Ka saade, mis seda tavaliselt saadab, pole parem. CONNECTIVITY_CHANGE-il on dokumenteeritud ajalugu VPN-üleminekute vahelejätmise kohta, sealhulgas versioonid, kus see käivitus ainult siis, kui VPN loodi või katkes, mitte iga võrgu muudatuse korral. Sellele ehitatud kuulaja tekitab olekumuutuja, mis triivib platvormi vaatest eemale kuni järgmise täieliku ühenduse taastamiseni.
Reageerige tagasikutsetele, mitte küsitlemisele
Registreerige NetworkRequest, mis nõuab TRANSPORT_VPN-i ja eemaldab NET_CAPABILITY_NOT_VPN-i, seejärel kutsuge registerNetworkCallback selle taotlusega. Siis ütlevad onAvailable ja onLost, millal deklaratsioon muutub, ja see on sündmus, mis teid tegelikult huvitab. Tühistage tagasikutse registreerimine, kui komponent peatub, muidu hoiab rakendus viidet surnud ekraanile ja tagasikutse jätkab käivitumist.
NetworkRequest req = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(req, callback);
See nõuab ACCESS_NETWORK_STATE-i, mis on tavaline luba: ilma rootita, ilma kasutaja nõusoleku dialoogita, ilma VPN-loata. Taimeril küsitlemine on alternatiiv, mille juurde inimesed tagasi pöörduvad, ja see kulutab akut, jättes samal ajal vahele intervallist lühema ülemineku, mis on täpselt see üleminek, millest kasutaja teatab.
Mida vastus teile siiski öelda ei saa
Ükski neist API-dest ei teata, kas tunnel on terve, millises serveris see lõpeb või kas teatud rakenduse liiklus on selle sees. VPN-võrk võib eksisteerida ilma kasutatavate marsruutideta ja rakendusepõhine tunnel võib välistada rakenduse, mis küsimuse esitab.
Seega kirjutage üles, mida kontroll nägi, mitte ainult tõeväärtus. Logige võrgu identifikaator, deklareeritud transport ja see, kas see võrk oli aktiivne. Aruanne, mis ütleb „VPN-i ei tuvastatud“, on kasutu, samas kui aruanne, mis ütleb, et võrk deklareeris TRANSPORT_VPN-i, kuid getActiveNetwork() tagastas Wi-Fi, osutab otse marsruutimisele või välistusnimekirjale, ja see on parandus, mille kallal saab keegi töötada.