Wêrom't dyn Android VpnService-tunnel ophâldt mei it rûtelein fan ferkear
Fjouwer oannames oer VpnService dy't yn produksje brekke: ien tunnel tagelyk, berik fan rûtes, listen per app en de folchoarder fan ôfsluten, mei in kontrôle foar elk.
Fjouwer oannames oer Android VpnService hawwe my ienris it grutste part fan in wike koste, en gjinien gie oer it netwurkprotokol. De app sei ferbûn, it kaaiikoan siet yn 'e statusbalke, en it ferkear gie noch altyd oer de normale rûte nei bûten. Ik bleau oannimmen dat de tunnel fan my wie oant ik him stopje, dat de rûtes dy't ik tafoege de rûtes wiene dy't ik krige, en dat in oprop nei stopSelf it ynterface mei him delhelje soe. De tsjinst droech him presys sa as dokumintearre. Myn mentale model wie it brutsen part.
Myt 1: de tunnel bliuwt oerein oantsto him stopest
Android hâldt ien VPN-ynterface tagelyk. Set in twadde op en it platfoarm set him net yn 'e wachtrige: it deaktivearret dy fan dy en ropt onRevoke() op dyn tsjinst. As dyn koade allinnich by in knopdruk ôfbrekt, lit in oername in deskriptor en in lêstried efter, en de folgjende start erft beide.
De VpnService-referinsje seit dúdlik dat prepare() null weromjout as de app al tastimming hat, en in Intent as de brûker dy noch goedkrije moat. Dy tastimming komt allinnich oan as do de bedoeling troch startActivityForResult() leverest. Sla de oerdracht oer en establish() jout null werom, wat lêzen wurdt as in stil mislearjen ynstee fan in tastimmingsprobleem.
Always-on VPN foeget in eigner ta dy'tsto net kontrolearrest. It systeem kin de tunnel nei in opstart of nei in crash werom helje, en it giet in VPN-app foar dy't de brûker mei de hân start hat. Bou onRevoke() sa dat it deselde boarnen slút as dyn normale ôfsluting, en behannelje it as in primêre stoppewei ynstee fan in rânesaak. Brûkers wikselje fan VPN-app; dyn koade moat dat oerlibje.
Myt 2: addRoute fangt presys watsto opliste hast
Rûtes beslute hokker pakketten dyn deskriptor berikke, en in flater yn it berik makket it dwylsinnige gefal wêryn in part fan it ferkear troch de tunnel giet en in part net. addRoute("0.0.0.0", 0) nimt hiel IPv4, wat betsjut dat elk pakket no by dy oankomt en trochstjoerd of weilitten wurde moat. In /32 foar ien inkelde host nimt hast neat, en al it oare draacht him as wie dyn VPN nea ynstallearre.
Famyljes wurde apart teld. In rûte foar allinnich IPv4 lit IPv6 op it ûnderlizzende netwurk, sadat in dûbelstapel-host noch bûten de tunnel oplosse en ferbine kin wylst er fan binnen folslein ferbûn liket.
DNS folget deselde logika. Tsjinners tafoege mei addDnsServer() geane nei apps dy't de resolver fan de tunnel brûke, wylst in app mei syn eigen fersifere DNS-einpunt of Private DNS set op in hostnamme se folslein negearret. As nammen foar ien app mislearje en foar in oare wurkje, kontrolearje dan it resolverspoar foar'tsto ien inkelde rûte oanrekest.
Myt 3: regels per app binne in skeakel dy'tsto beide kanten op draaie kinst
addDisallowedApplication() en addAllowedApplication() binne twa modi, net twa filters. De bouwer akseptearret ien model per tunnel, dus in app dy't alles behalve trije pakketten wol brûkt de ferbeane list en lit de tastiene list leech. De twa slute elkaar út, en minsken ûntdekke it by it draaien neidat it ynstellingsskerm al beide skeakels oanbean hat.
Pakkettsichtberens byt ek. Op Android 11 en letter kinst allinnich pakketten neame dy'tsto sjen kinst, dus in útslutingslist boud út in query dy't dyn app net draaie mei sil stil yngongen misse. Fergelykje de list mei wat de pakketbehearder echt weromjout foar'tsto har fertroust.
Wurkprofilen ferdiele it publyk wer. In tunnel start binnen it profyl dekt dat profyl en syn apps, wylst de persoanlike kant syn eigen ferbining hâldt. Dat is in berikbeslissing dy't ien kear nommen wurdt, en dy heart yn 'e interface, net yn in stipekaart.
Myt 4: stopSelf einiget de tunnel
Dat docht er net. It ynterface libbet yn 'e deskriptor dy't establish() weromjout, en it tsjinstobjekt kin stjerre wylst dy deskriptor iepen bliuwt. Slút earst de ParcelFileDescriptor, lit de referinsje falle, join of ûnderbrek de lês- en skriuwtrieden, en rop dan stopForeground(true) en stopSelf() yn dy folchoarder.
In fergetten deskriptor is de gewoane reden dat in tsjinst syn eigen stopfersyk liket te oerlibjen, en it ferklearret in ferâldere rûte dy't langer libbet as de app dy't him tafoege.
De omkearde flater is like gewoan: de deskriptor slute wylst in lêstried healwei it lêzen is, en dan dy tried in útsûndering ôfhanje litte dêr't nimmen wat foar skreaun hat. Hâld de ôfslutingssekwinsje op ien plak en lit sawol de brûkersaksje as onRevoke() dêr yn roppe.
Wat te kontrolearjen as de tunnel dea liket
Beskerm langlibjende sockets mei protect() foar't de tunnel omheech komt. Sûnder dat wurdt it eigen ferfier fan de tunnel wer yn de tunnel rûtelein en de ferbining stûket op in wize dy't presys op in tsjinnerflater liket. Test tsjin in adres dat allinnich efter it fiere netwurk bestiet, net tsjin in iepenbiere side dy't út 'e cache of fan it ûnderlizzende paad antwurdet.
Log it resultaat fan establish(), de rûtes, de útsletten pakketten en de reden fan ôfsluten. Dy fjouwer rigels skiede in tastimmingsprobleem fan in berikprobleem en fan in libbenssyklusprobleem sûnder pakketopname.