Razdeljeno tuneliranje v aplikaciji VpnService se zlomi na nekaj konkretnih mestih, popravilo pa je za vsako drugačno. Odjemalec prikaže le en neuspešen cilj, zato je prva naloga ugotoviti, na katero mejo zadeneš: poti, ki jih je prejel builder, proxy, ki ga ne moreš overiti, resolver, ki si ga samo naznanil, ali seznam aplikacij, ki ga platforma uporabi za vsakega uporabnika. Prav zato imam preverjanje v štirih korakih. Gre po spodnjem vrstnem redu in se ustavi pri prvi napaki.

Potrdi, kaj je builder predal platformi

VpnService.Builder je seznam deklaracij. establish() vrne ParcelFileDescriptor za navidezni vmesnik, platforma pa v ta deskriptor pošilja le promet, ki ustreza predponam iz addAddress in addRoute; addDnsServer pove odjemalcem, kateri resolver naj vprašajo. Cilj zunaj vseh dodanih predpon nikoli ne doseže tvoje aplikacije, kar koli obljublja vmesnik. To eno dejstvo pojasni večino poročil o pokvarjenem razdeljenem tuneliranju še pred spremembo kode.

Zabeleži sezname naslovov, poti in DNS, ki jih je sestavil builder, nato jih primerjaj s cilji, ki ne uspejo. Manjkajoča /32 za upravljalni gostitelj je veliko pogostejša od pokvarjenega tunela. Če je seznam pravilen, nadaljuj; če ni, je preostanek kontrolnega seznama zapravljen trud.

excludeRoute obstaja šele od API 33

Izključevanje po cilju je zahteva, ki najpogosteje postane nit za podporo. builder.excludeRoute(IpPrefix) je prišel v API 33; prej je edini podprti približek dodajanje poti za vse razen izključene predpone, kar pomeni, da moraš komplementarne predpone izračunati sam. Klica nista zrcalni sliki: eden odšteva od nabora poti vmesnika, drugi gradi nabor od začetka, starejša naprava pa bo neznan klic vrgla ali tiho prezrla. Razveji po Build.VERSION.SDK_INT in obe poti preizkusi na resnični strojni opremi.

Pri izračunu komplementov pazi na poravnavo predpon. Izključitev /24 znotraj poti /16 potrebuje pokrivne predpone, ki res opisujejo preostanek, ne ene popačene /8. Zabeleži končni seznam in preveri, da se izključeni gostitelj še vedno razrešuje po poti zunaj tunela.

Poverilnic proxyja ni mogoče izraziti

setHttpProxy(ProxyInfo) usmeri vmesnik na proxy HTTP ali SOCKS, URL PAC pa lahko za vsak cilj izbira med njima. Niti ProxyInfo niti oblika PAC ne prenašata uporabniškega imena in gesla, zato proxy, ki zahteva overjanje, ostane zunaj API-ja platforme. Aplikacije, ki ga potrebujejo, običajno vgradijo sklad uporabniškega prostora, tun2socks ali podoben pogon in same odgovorijo na izziv 407.

Ta izbira ima posledice. Sklad uporabniškega prostora vidi le promet, ki ga usmeriš vanj, zato mora živeti znotraj iste meje deskriptorja in podeduje isto zahtevo protect() za svojo lastno vtičnico navzgor. Če protect() izpustiš, tunel ujame svoj lastni transport, kar se pokaže kot rokovanje, ki se po prvem paketu ustavi. Preizkusi proti proxyju, ki zahteva poverilnice, ker odprt proxy preide po poti platforme in ne dokaže ničesar.

Seznami po aplikacijah in DNS so tudi deklaracije

Builder ima addAllowedApplication in addDisallowedApplication, in se medsebojno izključujeta: klic obeh vrže izjemo. Kakor koli, seznam se uporabi za vsakega uporabnika pri nastavitvi vmesnika, zato aplikacijo v delovnem profilu upravlja tunel delovnega profila in ne tvoj. Česar noben API ne da, je nadzor nad odločitvijo druge aplikacije: paketa ne moreš od zunaj potisniti v svoj tunel in ne moreš zapreti ali prekonfigurirati že aktivnega VPN.

DNS sledi istemu vzorcu. addDnsServer naznani naslov resolverja; tvoji aplikaciji ne dovoli odgovarjati na poizvedbe. Če želiš imena razreševati sam, poženi resolver in njegov naslov usmeri v tunel. Aplikacije, ki uporabljajo DoT ali DoH na fiksno ime gostitelja, lahko obidejo naznanjeni naslov, zato razreševanje imen obravnavaj kot najboljši trud, razen če prestrežeš tudi pot poizvedbe.

Manifest, always-on in preizkus v štirih korakih

Upravičenost se deklarira v manifestu: BIND_VPN_SERVICE, filter namena android.net.VpnService in storitev v ospredju, če mora tunel preživeti v ozadju. Always-on VPN dodatno zahteva čist onRevoke, ker ga sistem pokliče, ko uporabnik spremeni nastavitve ali druga aplikacija prevzame nadzor.

Izvedi preverjanja po vrsti in se ustavi pri prvi napaki. Najprej izpiši sezname naslovov, poti in DNS, predane builderju. Nato potrdi, da je establish() vrnil deskriptor in da je transportna vtičnica zaščitena. Tretjič, zahtevaj en cilj znotraj vključene predpone in enega znotraj izključene. Četrtič, preveri, kateri resolver je v resnici odgovoril, tako da vprašaš ime gostitelja, ki ga lahko razreši le tvoj resolver.

Dokončano rokovanje ne dokaže nobenega od poznejših korakov. Vsako preverjanje izolira eno plast, zato uspeh v drugem koraku ne pove nič o četrtem.

Referenca VpnService.Builder našteje celotno površino builderja. Vse, kar potrebuješ onkraj tega, je drug API, druga aplikacija ali tvoj lastni sklad uporabniškega prostora, in to je oblikovalska odločitev, ne hrošč razdeljenega tunela.

Sorodni članki