Miks teie Android VpnService'i tunnel lakkab liiklust marsruutimast
Neli VpnService'i uskumust, mis tootmises katki lähevad: üks tunnel korraga, marsruutide ulatus, rakendusepõhised loendid ja sulgemise järjekord, igaühe jaoks kontroll.
Neli oletust Android VpnService'i kohta maksid mulle kord suurema osa nädalast, ja ükski neist ei puudutanud võrguprotokolli. Rakendus teatas, et on ühendatud, võtmeikoon seisis olekuribal, ja liiklus läks ikkagi tavalise marsruudi kaudu välja. Ma eeldasin jätkuvalt, et tunnel on minu, kuni ma selle peatän, et marsruudid, mille lisasin, on marsruudid, mille saan, ja et stopSelf'i kutsumine võtab liidese endaga kaasa. Teenus käitus täpselt nii, nagu dokumenteeritud. Katki oli minu mõttemudel.
Müüt 1: tunnel püsib püsti, kuni te selle peatate
Android hoiab korraga ühte VPN-liidest. Looge teine ja platvorm ei pane seda järjekorda, vaid deaktiveerib teie oma ja kutsub teie teenuses onRevoke(). Kui teie kood lammutab ainult nupuvajutusel, jätab ülevõtmine maha deskriptori ja lugemiskeerme, ja järgmine käivitus pärib mõlemad.
VpnService'i viide ütleb selgesõnaliselt, et prepare() tagastab null, kui rakendusel on juba nõusolek, ja Intent, kui kasutaja peab selle alles kinnitama. See nõusolek jõuab kohale ainult siis, kui toimetate kavatsuse kohale startActivityForResult() kaudu. Jätke üleandmine vahele ja establish() tagastab null, mida loetakse vaikseks ebaõnnestumiseks, mitte õiguste probleemiks.
Always-on VPN lisab omaniku, mida te ei kontrolli. Süsteem võib tunneli pärast alglaadimist või pärast krahhi tagasi tuua, ja see tõrjub välja VPN-rakenduse, mille kasutaja käivitas käsitsi. Ehitage onRevoke() nii, et see sulgeb samad ressursid nagu teie tavaline sulgemine, ja käsitlege seda peamise peatamistee, mitte äärmusliku juhtumina. Kasutajad vahetavad VPN-rakendusi; teie kood peab selle üle elama.
Müüt 2: addRoute püüab täpselt selle, mille te loetlesite
Marsruudid otsustavad, millised paketid teie deskriptorini jõuavad, ja ulatuse viga tekitab hullustava juhtumi, kus osa liiklusest läheb tunnelist läbi ja osa mitte. addRoute("0.0.0.0", 0) võtab kogu IPv4, mis tähendab, et iga pakett jõuab nüüd teieni ja tuleb edasi saata või maha visata. Ühe hosti jaoks /32 ei võta peaaegu midagi, ja kõik muu käitub nii, nagu teie VPN-i poleks kunagi paigaldatud.
Perekonnad loetakse eraldi. Ainult IPv4 marsruut jätab IPv6 alusvõrku, nii et kahe pinu host saab endiselt tunnelist väljas lahendada ja ühenduda, nähes seespool täiesti ühendatud.
DNS järgib sama loogikat. addDnsServer() abil lisatud serverid lähevad rakendustele, mis kasutavad tunneli lahendajat, samas kui rakendus, millel on oma krüpteeritud DNS-lõpp-punkt või hostinimele seatud Private DNS, eirab neid täielikult. Kui nimed ebaõnnestuvad ühes rakenduses ja töötavad teises, kontrollige lahendaja teed enne, kui puudutate ühtki marsruuti.
Müüt 3: rakendusepõhised reeglid on lüliti, mida saab keerata kummale poole
addDisallowedApplication() ja addAllowedApplication() on kaks režiimi, mitte kaks filtrit. Koostaja võtab tunnelisse ühe mudeli, seega rakendus, mis tahab kõike peale kolme paketi, kasutab keelatud loendit ja jätab lubatud loendi tühjaks. Need kaks välistavad teineteist ja inimesed saavad sellest teada käitusajal pärast seda, kui seadete ekraan on juba pakkunud mõlemat lülitit.
Pakettide nähtavus hammustab samuti. Android 11 ja uuemates saate nimetada ainult pakette, mida näete, seega välistusloend, mis on ehitatud päringust, mida teie rakendusel pole lubatud käitada, jätab kirjed vaikselt vahele. Võrrelge loendit sellega, mida paketihaldur tegelikult tagastab, enne kui seda usaldate.
Tööprofiilid jagavad publiku uuesti. Profiili sees käivitatud tunnel katab selle profiili ja selle rakendused, samas kui isiklik pool hoiab oma ühenduvust. See on korra tehtud ulatusotsus ja see kuulub liidesesse, mitte tugipiletisse.
Müüt 4: stopSelf lõpetab tunneli
Ei lõpeta. Liides elab deskriptoris, mille establish() tagastab, ja teenuseobjekt võib surra, samal ajal kui see deskriptor jääb avatuks. Sulgege kõigepealt ParcelFileDescriptor, vabastage viide, liituge lugemis- ja kirjutamiskeermedega või katkestage need, seejärel kutsuge stopForeground(true) ja stopSelf() selles järjekorras.
Unustatud deskriptor on tavaline põhjus, miks teenus näib omaenda peatamispalve üle elavat, ja see selgitab aegunud marsruuti, mis elab kauem kui selle lisanud rakendus.
Vastupidine viga on sama tavaline: deskriptori sulgemine, kui lugemiskeerm on lugemise keskel, ja siis selle keerme jätmine tegelema erandiga, mille jaoks keegi koodi ei kirjutanud. Hoidke sulgemisjärjekord ühes kohas ja laske nii kasutaja toimingul kui ka onRevoke() sinna kutsuda.
Mida kontrollida, kui tunnel näib surnud
Kaitske pikaajalisi sokleid protect() abil enne, kui tunnel üles tuleb. Ilma selleta suunatakse tunneli enda transport tagasi tunnelisse ja ühendus takerdub nii, et see näeb välja täpselt nagu serveririke. Testige aadressi vastu, mis eksisteerib ainult kaugvõrgu taga, mitte avaliku saidi vastu, mis vastab vahemälust või alusrajalt.
Logige establish() tulemus, marsruudid, välistatud paketid ja sulgemise põhjus. Need neli rida eristavad nõusolekuprobleemi ulatusprobleemist ja elutsükliprobleemist ilma paketipüügita.