Ei, VpnService’i rakendus ei saa telefoni VPN-i sisse lülitada
Rakendus omab üht tunnelit ja selle elutsüklit, mitte telefoni VPN-i lülitit. Nõusoleku saab tagasi võtta, korraga töötab ainult üks VPN ja paketid tuleb teil ise parsida.
Vale eeldus on, et VpnService’i rakendus keerab süsteemi lülitit ja sealt alates on telefon kaitstud. Tegelikult on see kitsam: rakendus küsib nõusolekut, ehitab liidese, loeb ja kirjutab pakette failideskriptori kaudu ning annab liidese tagasi, kui see peatub. Korraga saab elus olla ainult üks selline tunnel ja süsteem võib selle küsimata ära võtta. Suurem osa segasest käitumisest, mille inimesed protokolli süüks panevad, tuleb just sellest omandimudelist.
Käsitlege iga elutsükli üleminekut disaini osana, mitte erijuhuna.
Müüt: rakendus lülitab telefoni VPN-i sisse
Voog algab VpnService.prepare()-ga, mis tagastab Intent’i, mille kasutaja peab vastu võtma, ja jätkub establish()-ga, mis võib hiljem siiski ebaõnnestuda põhjustel, mida rakendus ei kontrolli. Teise tunneli loomine deaktiveerib esimese liidese ja asendab süsteemiteavituse. Kusagil selles järjestuses ei oma rakendus kogu seadme lülitit; ta omab talle antud deskriptorit ja pakette, mis sellele maanduvad. Isegi püsiv teavitus kuulub süsteemile, mitte rakenduse disainile.
Kuna liidest antakse, mitte ei haarata, võib teine klient koha igal hetkel hõivata. Rakendus saab sellest teada tagasikutse kaudu, mitte tagastusväärtuse kaudu.
Rakenduse sundpeatumine on teavituste paneelil nähtav samal põhjusel: süsteem teatab, mida ta andis, mitte seda, mida rakendus otsustas näidata.
Müüt: nõusolek antakse üks kord
Kui süsteem annab tunneli teisele kliendile, saadab ta tühistamistehingu sellele binderile, mille baasklass tagastab kohast onBind(). Rakendus, mis asendab selle binderi omaga, peab tühistamistehingu ise edasi saatma, muidu ei käivitu VpnService.onRevoke() kunagi ja teenus jätkab kirjutamist deskriptorisse, mida enam ei ole. Nõusoleku võib tagasi võtta ka kasutaja ning süsteem võib teenuse peatada taaskäivituse või alati sisse lülitatud ühenduse taasloomise ajal.
Sellega tegelemine on konkreetne töö, mitte logirida: lõpetage deskriptori lugemine, sulgege see, tühjendage ühendatud lipp ja öelge kasutajale, mis olekus ta on. Seejärel otsustage, kas rakendus peaks uuesti looma, uuesti küsima või ootama, sest alati sisse lülitatud seadistuses võib süsteem teenuse ise tagasi tuua.
Rakendus, mis käsitleb tühistamist erandina, mitte olekuna, teatab ühendatusest, samal ajal kui iga tema kirjutatud pakett visatakse kõrvale, mis on privaatsusvahendi jaoks halvim võimalik tõrkerežiim.
Müüt: liides filtreerib liiklust teie eest
Kohale jõuab töötlemata IP. Püüdmine või filtreerimine tähendab IP, TCP, UDP ja IPv6 päiste parsimist, voogude jälgimist ja vastuste tagasi deskriptorisse kirjutamist, mis teeb püüdmisrakendusest väikese võrgupinu, mitte süsteemse vahendi. protect() on olemas selleks, et rakenduse enda transpordisokkel mööduks tema tunnelist, mitte ei püüaks iseennast, ja see tagastab tõeväärtuse, mida tasub kontrollida igal soklil, mis luuakse pärast tunneli püstitamist.
Ehitaja lubatud ja keelatud rakenduste loendid otsustavad, kelle liiklus liidesesse siseneb. Need ei anna juurdepääsu kasulikule koormusele väljaspool IP-pakette, mida liides kannab, ja ükski API ei paljasta teise kliendi tunnelit ega loetle süsteemi VPN-profiile. Võrguvõimalused võivad öelda, et võrk on VPN-transport, aga mitte seda, kelle tunnel see on.
Seetõttu saavad teiste rakenduste oleku kohta käivad küsimused vastuolulisi vastuseid: teave lihtsalt ei ole teie protsessile kättesaadavaks tehtud.
Mida elutsükkel tegelikult nõuab
Jälgige olekuid, mida te kontrollite: ettevalmistatud, loodud, tühistatud, peatatud. Taasühendus peaks olema lammutamine ja seejärel loomine, ühe omanikuga deskriptorile ja transpordisoklile, sest kaks komponenti, mis võidu loomise nimel võistlevad, kaklevad ainsa vaba koha pärast. Taustakäivitused on tänapäevases Androidis piiratud, seega alglaadimise saade ei ole garantii; kasutage esiplaaniteenust koos selle teavitusega seal, kus API tase seda lubab.
Logige üleminekut, mitte ainult lõppolekut, ja hoidke olekumasin piisavalt väike, et testida iga rada. Olek, milleni te testis ei jõua, on olek, millega te kohtute põllul ilma logita.
Testige elutsüklit seadmes
Emulaatorid suudavad ühenduvust võltsida, kuid mitte nõusoleku ja tühistamise rada veenvalt, seega käivitage need päris seadmes, kuhu on paigaldatud teine VPN-klient, kes koha ära varastab.
adb shell dumpsys connectivity | grep -i -A3 vpn
adb shell dumpsys activity services | grep -i -A2 vpnservice
adb logcat -s VpnService:V AndroidRuntime:E
adb shell am force-stop com.example.vpnapp
Käivitage tunnel, kinnitage teavitus ja liides, seejärel ühendage teine klient ja jälgige, kas tühistamise tagasikutse käivitub ja kas olek puhastub. Peatage rakendus sunniviisiliselt ja kontrollige, kas miski teatab endiselt elusast deskriptorist.
Korrake jada pärast alati sisse lülitatud taaskäivitust, sest see rada erineb kasutaja algatatud ühendusest. Te ei otsi rohelist ikooni, vaid tõendit, et rakendus lõpetas deskriptori kasutamise, mida ta enam ei omanud.