Tunelul divizat într-o aplicație VpnService se rupe în câteva locuri concrete, iar repararea diferă pentru fiecare. Clientul afișează o singură destinație care eșuează, așa că prima sarcină este să afli ce graniță lovești: rutele primite de builder, proxy-ul pe care nu-l poți autentifica, resolver-ul pe care doar l-ai anunțat sau lista de aplicații pe care platforma o aplică per utilizator. Tocmai de aceea țin o verificare în patru pași. Merge în ordinea de mai jos și se oprește la prima eroare.

Confirmă ce a predat builder-ul platformei

VpnService.Builder este o listă de declarații. establish() returnează un ParcelFileDescriptor pentru o interfață virtuală, iar platforma trimite în acel descriptor doar traficul care corespunde prefixelor din addAddress și addRoute; addDnsServer spune clienților ce resolver să întrebe. O destinație din afara tuturor prefixelor adăugate nu ajunge niciodată în aplicația ta, orice ar promite interfața. Acest singur fapt explică majoritatea raportărilor de tunel divizat stricat înainte de orice modificare de cod.

Înregistrează în jurnal listele de adrese, rute și DNS pe care le-a compus builder-ul, apoi compară-le cu destinațiile care eșuează. Un /32 lipsă pentru un host de administrare este mult mai frecvent decât un tunel stricat. Dacă lista este corectă, mergi mai departe; dacă nu, restul listei de verificare este efort irosit.

excludeRoute există doar de la API 33

Excluderea pe bază de destinație este cererea care ajunge cel mai des într-un fir de suport. builder.excludeRoute(IpPrefix) a apărut în API 33; înainte, singura aproximare acceptată este să adaugi rute pentru tot mai puțin prefixul exclus, ceea ce înseamnă să calculezi singur prefixele complementare. Cele două apeluri nu sunt imagini în oglindă: unul scade din setul de rute al interfeței, celălalt construiește setul de la zero, iar un dispozitiv mai vechi va arunca sau va ignora în tăcere un apel pe care nu îl recunoaște. Ramifică după Build.VERSION.SDK_INT și testează ambele căi pe hardware real.

Când calculezi complemente, urmărește alinierea prefixelor. O excludere /24 într-o rută /16 are nevoie de prefixele de acoperire care descriu într-adevăr restul, nu de un /8 malformat. Înregistrează lista finală și verifică faptul că hostul exclus se rezolvă în continuare pe o cale din afara tunelului.

Credențialele proxy nu pot fi exprimate

setHttpProxy(ProxyInfo) îndreaptă interfața spre un proxy HTTP sau SOCKS, iar un URL PAC poate alege între ele per destinație. Nici ProxyInfo, nici formatul PAC nu transportă nume de utilizator și parolă, așa că un proxy care cere autentificare rămâne în afara API-ului platformei. Aplicațiile care au nevoie de el integrează de obicei un stivă de spațiu utilizator, tun2socks sau un motor similar și răspund ele însele provocării 407.

Această alegere are consecințe. O stivă de spațiu utilizator vede doar traficul pe care îl rutezi spre ea, așa că trebuie să trăiască în aceeași limită a descriptorului și moștenește aceeași cerință protect() pentru propriul socket de uplink. Dacă sari peste protect(), tunelul își capturează propriul transport, ceea ce apare ca un handshake care se blochează după primul pachet. Testează cu un proxy care cere credențiale, pentru că un proxy deschis trece pe calea platformei și nu demonstrează nimic.

Listele per aplicație și DNS sunt tot declarații

Builder are addAllowedApplication și addDisallowedApplication, iar acestea se exclud reciproc: apelarea ambelor aruncă o excepție. Oricum, lista se aplică per utilizator la configurarea interfeței, așa că o aplicație din profilul de lucru este guvernată de tunelul profilului de lucru, nu de al tău. Ce nu oferă niciun API este controlul asupra deciziei altei aplicații: nu poți împinge un pachet în tunelul tău din exterior și nu poți închide sau reconfigura un VPN deja activ.

DNS urmează același model. addDnsServer anunță o adresă de resolver; nu îi permite aplicației tale să răspundă la interogări. Dacă vrei să rezolvi numele singur, rulează un resolver și rutează adresa lui în tunel. Aplicațiile care folosesc DoT sau DoH către un nume de host fix pot ocoli adresa anunțată, așa că tratează rezolvarea numelor ca efort optim dacă nu interceptezi și calea interogării.

Manifest, always-on și testul în patru pași

Eligibilitatea se declară în manifest: BIND_VPN_SERVICE, filtrul de intent android.net.VpnService și un serviciu de prim-plan dacă tunelul trebuie să supraviețuiască în fundal. Always-on VPN cere în plus un onRevoke curat, pentru că sistemul îl apelează când utilizatorul schimbă setările sau o altă aplicație preia controlul.

Rulează verificările în ordine și oprește-te la prima eroare. Întâi, afișează listele de adrese, rute și DNS date builder-ului. În al doilea rând, confirmă că establish() a returnat un descriptor și că socketul de transport este protejat. În al treilea rând, cere o destinație într-un prefix inclus și una într-un prefix exclus. În al patrulea rând, verifică ce resolver a răspuns efectiv, interogând un nume de host pe care doar resolver-ul tău îl poate rezolva.

Un handshake finalizat nu demonstrează niciunul dintre pașii următori. Fiecare verificare izolează un singur strat, așa că o reușită la pasul al doilea nu spune nimic despre al patrulea.

Referința VpnService.Builder enumeră toată suprafața builder-ului. Tot ce ai nevoie dincolo de ea este alt API, altă aplicație sau propria stivă de spațiu utilizator, iar asta este o decizie de proiectare, nu un defect al tunelului divizat.

Articole similare