Aveți două moduri de a ține pachetele tunelului însuși în afara tunelului: să protejați socketul de transport sau să scoateți întreaga aplicație din VPN. Ele rezolvă probleme diferite, iar alegerea celei mai largi pentru că e mai ușor de conectat este exact felul în care aplicațiile pierd accesul la rețeaua locală și DNS-ul despicat fără să observe. Începeți de la ce trebuie să rămână în afara tunelului, apoi alegeți cea mai mică anvergură care obține asta.

Ce elimină protect() din tunel

VpnService.protect() este un apel pentru fiecare socket. Îi dați socketul sau descriptorul care poartă tunelul, iar Android marchează acel socket astfel încât traficul lui să folosească rețeaua de dedesubt în locul interfeței VPN. Orice altceva trimite aplicația urmează în continuare ruta VPN normală.

Exact această anvergură îngustă este ideea. Un socket protejat nu schimbă felul în care clientul dvs. HTTP rezolvă nume, ce interfață folosesc apelurile de analiză sau ce se întâmplă cu o a doua conexiune deschisă mai târziu.

Trei condiții care decid anvergura

Prima condiție este dacă doar transportul tunelului trebuie să iasă. Dacă restul traficului aplicației trebuie să treacă în continuare prin VPN, protecția socketului este anvergura corectă. A doua condiție este dacă controlați crearea socketului. O bibliotecă ce își deschide propriul socket adânc în interior nu vă lasă unde să apelați protect(), decât dacă expune un hook sau acceptă un socket deja construit. A treia condiție este dacă aplicația trebuie să ajungă direct la rețeaua locală sau la un captive portal, ceea ce este o cerință diferită de a ține tunelul viu.

Excluderea aplicației cu addDisallowedApplication() este unealta brută. Scoate din VPN fiecare conexiune pe care o face aplicația, inclusiv cele pe care voiați să le inspectați, iar pe unele dispozitive schimbă și comportamentul descoperirii locale.

O a treia opțiune este să legați transportul de o rețea aleasă cu bindProcessToNetwork() sau cu un mâner de rețea la nivel de socket. Este util când dispozitivul are Wi-Fi și celular simultan și doriți ca tunelul să meargă pe o anumită legătură de urcare.

Apelați-l în timp ce transportul este creat

Apelați protect() în același bloc care creează transportul, înainte de connect() sau send(). Dacă apelul returnează false, închideți socketul și lăsați configurarea să eșueze; continuarea vă lasă cu un transport ale cărui pachete pot intra din nou în propria interfață.

Forma este destul de mică pentru a sta într-un singur loc:

DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
    transport.close();
    throw new IOException("transport not protected");
}
transport.connect(serverAddress);

Trei detalii mușcă aici. Întâi,booleanul este singurul semnal pe care îl primiți, așa că înregistrați eșecul cu suficient context pentru a distinge un consimțământ revocat de un apel refuzat. Al doilea,protect() acoperă socketul, nu numărul descriptorului: dacă închideți socketul și mai târziu refolosiți același întreg pentru alt socket, noul socket nu este protejat. Al treilea, când o bibliotecă nativă deschide transportul, protejați descriptorul înainte ca codul nativ să se conecteze, altfel ruta poate fi deja aleasă.Referința VpnService este locul unde confirmați ce supraîncărcări oferă de fapt nivelul dvs. țintă de API.

Testați calea blocată, nu doar pe cea fericită

Verificați cu tunelul ridicat, pentru că este singura stare în care cele două căi pot diferi. Trimiteți o cerere prin transportul protejat și una dintr-o cale de cod care nu a apelat niciodată protect(), apoi comparați ce vede capătul îndepărtat: conexiunea protejată ar trebui să sosească cu rețeaua de dedesubt ca sursă, în timp ce cea neprotejată arată adresa tunelului însuși și mișcă contoarele de octeți ale tunelului. Pe dispozitiv,ss sau netstat arată aceeași despărțire înainte ca ceva să părăsească radioul.

Testați revocarea separat, pentru că o schimbare de consimțământ dărâmă interfața fără să atingă codul socketului. Revocați consimțământul VPN sau opriți serviciul, reconstruiți transportul și confirmați că protect() încă returnează true și că transportul se reconectează imediat ce consimțământul este acordat din nou.

Dacă cererea protejată urmează totuși tunelul, apelul de protecție are loc probabil după ce socketul a fost deja rutat. Mutați-l mai devreme și testați din nou.

Țineți decizia scrisă lângă configurarea transportului: ce socketuri sunt protejate, ce trafic al aplicației este exclus și ce face aplicația când protecția eșuează. Exact acea linie împiedică o refactorizare ulterioară să extindă excluderea la întreaga aplicație pentru că un singur test eșua. Ea îi dă și următoarei persoane o modalitate de a verifica intenția fără inginerie inversă a manifestului.

Articole similare