Vier aannames oor Android VpnService het my een keer die grootste deel van 'n week gekos, en nie een daarvan het oor die draadprotokol gegaan nie. Die app het gesê dis gekoppel, die sleutelikoon het in die statusbalk gesit, en die verkeer het steeds oor die normale roete uitgegaan. Ek het aanhou aanneem die tonnel is myne totdat ek dit stop, dat die roetes wat ek bygevoeg het die roetes is wat ek kry, en dat 'n oproep na stopSelf die koppelvlak saam met hom sou aftakel. Die diens het presies gewerk soos gedokumenteer. My geestesmodel was die gebreekte deel.

Mite 1: die tonnel bly staan tot jy dit stop

Android hou een VPN-koppelvlak op 'n slag. Stel 'n tweede een op en die platform tou dit nie in nie; dit deaktiveer joune en roep onRevoke() op jou diens. As jou kode net op 'n knoppiedruk aftakel, laat 'n oorname 'n descriptor en 'n leserdraad agter, en die volgende begin erf albei.

Die VpnService-verwysing is duidelik dat prepare() null teruggee wanneer die app reeds toestemming het, en 'n Intent wanneer die gebruiker nog moet goedkeur. Daardie goedkeuring kom net deur as jy die intent via startActivityForResult() aflewer. Slaan die oorhandiging oor en establish() gee null terug, wat as 'n stil mislukking eerder as 'n toestemmingsprobleem gelees word.

Always-on VPN voeg 'n eienaar by wat jy nie beheer nie. Die stelsel kan die tonnel ná 'n herlaai of ná 'n ineenstorting terugbring, en dit voorkom 'n VPN-app wat die gebruiker met die hand begin het. Bou onRevoke() so dat dit dieselfde hulpbronne sluit as jou normale afsluiting, en behandel dit as 'n primêre stoppad eerder as 'n randgeval. Gebruikers wissel VPN-apps; jou kode moet dit oorleef.

Mite 2: addRoute vang presies wat jy gelys het

Roetes bepaal watter pakkette jou descriptor bereik, en 'n omvangsfoout gee die waansinnige geval waar sommige verkeer deur die tonnel gaan en sommige nie. addRoute("0.0.0.0", 0) neem alle IPv4, wat beteken elke pakket kom nou by jou aan en moet aanstuur of laat val word. 'n /32 vir 'n enkele gasheer neem byna niks, en alles anders gedra hom asof jou VPN nooit geïnstalleer is nie.

Families word apart getel. 'n Slegs-IPv4-roete laat IPv6 op die onderliggende netwerk, so 'n tweestapel-gasheer kan steeds buite die tonnel oplos en verbind terwyl dit heeltemal gekoppel lyk binne-in.

DNS volg dieselfde logika. Bedieners wat met addDnsServer() bygevoeg is, gaan na apps wat die tonnel-oplosser gebruik, terwyl 'n app met sy eie geënkripteerde DNS-eindpunt of Private DNS wat op 'n gasheernaam gestel is, hulle heeltemal ignoreer. Wanneer name vir een app misluk en vir 'n ander werk, kyk na die oplosserpad voordat jy aan 'n enkele roete raak.

Mite 3: per-app-reëls is 'n skakelaar wat jy enige kant toe kan flip

addDisallowedApplication() en addAllowedApplication() is twee modusse, nie twee filters nie. Die bouer aanvaar een model per tonnel, so 'n app wat alles behalwe drie pakkette wil hê, gebruik die verbode lys en laat die toegelate lys leeg. Die twee sluit mekaar uit, en mense vind dit uit tydens looptyd nadat die instellingsskerm albei skakelaars reeds aangebied het.

Pakket sigbaarheid byt ook. Op Android 11 en later kan jy net pakkette noem wat jy kan sien, so 'n uitsluitingslys wat uit 'n navraag gebou is wat jou app nie mag uitvoer nie, sal stilweg inskrywings mis. Vergelyk die lys met wat die pakketbestuurder werklik teruggee voordat jy dit vertrou.

Werkprofiele verdeel die gehoor weer. 'n Tonnel wat binne die profiel begin is, dek daardie profiel en sy apps, terwyl die persoonlike kant sy eie verbinding behou. Dit is 'n omvangsbesluit wat een keer geneem word, en dit hoort in die UI eerder as in 'n ondersteuningskaartjie.

Mite 4: stopSelf beëindig die tonnel

Dit doen nie. Die koppelvlak leef in die descriptor wat deur establish() teruggegee word, en die diensobjek kan sterf terwyl daardie descriptor oop bly. Sluit eers die ParcelFileDescriptor, laat vaar die verwysing, sluit by die leser- en skrywerdrade aan of onderbreek hulle, en roep dan stopForeground(true) en stopSelf() in daardie orde.

'n Vergete descriptor is die gewone rede waarom 'n diens blyk om sy eie stopversoek te oorleef, en dit verduidelik 'n verouderde roete wat die app wat dit bygevoeg het, oorleef.

Die omgekeerde fout is net so algemeen: die descriptor sluit terwyl 'n leserdraad middelesing is, en dan daardie draad los om 'n uitsondering te hanteer waarvoor niemand geskryf het nie. Hou die afsluitreeks op een plek en laat beide die gebruikersaksie en onRevoke() daarnatoe roep.

Wat om te kontroleer wanneer die tonnel dood lyk

Beskerm langdurige sokkette met protect() voordat die tonnel opkom. Sonder dit word die tonnel se eie vervoer terug in die tonnel in gestuur en die verbinding stol op 'n manier wat presies soos 'n bedienerfout lyk. Toets teen 'n adres wat net agter die afgeleë netwerk bestaan, nie 'n publieke werf wat uit die kas of uit die onderliggende pad antwoord nie.

Teken die resultaat van establish() aan, die roetes, die uitgeslote pakkette en die afsluitrede. Daardie vier reëls skei 'n toestemmingsprobleem van 'n omvangsprobleem van 'n lewensiklusprobleem sonder 'n pakketopname.

Verwante artikels