Ai adăugat un domeniu în lista de permisiuni, interogarea ajunge în aplicația ta, comparatorul returnează permis, iar pagina tot nu se încarcă niciodată. În acel moment majoritatea încep să rescrie comparatorul și să adauge comparații cu metacaractere. Din experiența mea comparatorul este de obicei corect, iar defecțiunea se află un nivel mai jos: în răspunsul DNS pe care l-ai sintetizat, în ruta pe care ai instalat-o sau în socketul pe care l-ai folosit pentru a transmite interogarea. Lista este partea ușoară; calea pachetului este locul unde listele de permisiuni se strică.

Comparatorul este rareori vinovatul

Primul lucru de separat este blocat de stricat. Un nume blocat ar trebui să eșueze rapid și vizibil, pentru că browserul primește un răspuns că numele nu există și se oprește. Un nume stricat se blochează, reîncearcă prin IPv6, trece la alt transport și te lasă cu un indicator care se învârte. Dacă vezi un indicator care se învârte, lista ta de permisiuni răspunde probabil cu ceva cu care clientul nu poate face nimic.

Înregistrarea A goală este greșeala clasică. A returna NOERROR cu zero răspunsuri pare un răspuns valid, așa că resolverul stub îl acceptă, browserul reîncearcă, încearcă AAAA, încearcă alt resolver și renunță mult mai târziu. Un cod de răspuns 3, adică NXDOMAIN, cu secțiunea de întrebare repetată și ID-ul tranzacției copiat, încheie conversația imediat. Folosește-l pentru fiecare nume pe care îl refuzi deliberat.

IPv6 merită aceeași decizie și nu o valoare implicită. Dacă răspunzi NXDOMAIN pentru A și lași AAAA să treacă, clientul poate ajunge la numele blocat prin IPv6 și filtrul pierde. Unele implementări răspund interogărilor AAAA blocate cu o adresă de loopback, astfel încât conexiunea să eșueze imediat în loc să treacă pe o alternativă. Alege o singură politică, aplică-o ambelor tipuri de înregistrări și înregistrează-le pe amândouă.

O rută care capturează tot posedă tot

Un design doar cu DNS nu adaugă niciodată addRoute("0.0.0.0", 0), așa că doar interogările intră în interfața TUN, iar restul continuă să folosească rețeaua de dedesubt. Aceasta este forma sigură și explică și de ce unele liste de permisiuni par să funcționeze pe Wi-Fi și eșuează în rețeaua mobilă.

În momentul în care există o rută implicită, fiecare flux este al tău. Un pachet TCP care ajunge la interfață și nu este nici transmis, nici servit cu un răspuns stă în coadă până când clientului i se epuizează timpul, și nu există nicio eroare de înregistrat pentru că nimic nu a eșuat. Am văzut pe cineva petrecând o după-amiază întreagă pe o listă de permisiuni stricată care în realitate era un transmitător lipsă pentru portul 443. Înregistrează protocolul și destinația fiecărui pachet pe care decizi să îl arunci.

Socketul resolverului trebuie să iasă din tunel

Transmiterea este locul următoarei capcane. Resolverii operatorului stau adesea pe o adresă privată la care tunelul nu ajunge, așa că interogarea pe care o transmiți nu merge nicăieri și aplicația raportează expirarea timpului de citire. Trimite interogările aprobate către un resolver despre care știi că este accesibil și dă mai întâi acel socket lui VpnService.protect(). Un socket neprotejat trimite interogarea înapoi în propriul tău tunel, unde ajunge din nou la aplicație și se învârte până când se declanșează timeout-ul.

Defecțiunea arată identic cu o întrerupere de rețea, și de aceea supraviețuiește atât de mult în rapoartele de erori. Ține contoare pentru interogările trimise și răspunsurile primite: dacă primul număr crește iar al doilea rămâne la zero, te uiți la socket, nu la listă.

Dimensiunea și sumele de control eșuează fără mesaj

Aruncările silențioase vin și din dimensiune. Un răspuns DNS cu un set mare de răspunsuri sau un handshake TCP cu opțiuni generoase poate depăși un MTU de cale pe care nu l-ai stabilit niciodată explicit; fragmentul dispare în amonte și clientul fie reîncearcă mai mic, fie nu mai încearcă. Setarea MTU-ului interfeței la 1280 este o alegere conservatoare care rezistă pe majoritatea căilor mobile, iar să fii explicit este mai bine decât să moștenești o valoare implicită pe care nimeni nu a măsurat-o.

Dacă îți construiești singur pachetele, suma de control este celălalt ucigaș silențios. O sumă de control UDP calculată peste un pseudo-antecedent care omite lungimea UDP sau câmpul de protocol produce un pachet care pare bine format la tine și este aruncat de receptor fără urmă. Compară pseudo-antecedentul cu specificația câmp cu câmp și tipărește suma calculată pentru un flux cunoscut în timp ce depanezi.

Ce nu va face un VpnService pentru tine

Două limite merită cunoscute înainte de a proiecta în jurul lor. Nicio aplicație obișnuită nu poate instala un proxy la nivel de sistem sau un fișier PAC în afara Wi-Fi, iar acea parte nu s-a schimbat. Calea per aplicație s-a schimbat: înainte de API 29, VpnService.Builder nu putea transporta deloc un proxy, iar din API 29, setHttpProxy(ProxyInfo.buildPacProxy(uri)) oferă platformei un script PAC pentru evaluare pentru rețeaua VPN. Evaluează regulile în propriul cod când vizezi API-uri mai vechi sau când modelul PAC nu poate exprima regula. Nici UDP nu este gratuit: browserele trimit doar TCP printr-un proxy SOCKS, iar majoritatea clienților SOCKS5 nu implementează niciodată UDP ASSOCIATE, așa că un design bazat pe proxy aruncă sau pierde UDP în timp ce TCP pare sănătos.

Acea a doua limită este motivul pentru care un protocol bazat pe UDP poate funcționa într-o rețea obișnuită și poate eșua în momentul în care VPN-ul tău se conectează. Dacă se așteaptă ca tunelul să transporte UDP, transmitătorul trebuie să implementeze calea de transmitere pentru asta, nu doar calea TCP cu un alt tip de socket.

Verifică în această ordine

Parcurge lista în ordine și oprește-te la primul pas care eșuează, pentru că fiecare verificare ulterioară este zgomot până când precedenta rezistă.

  1. Listează rutele instalate și confirmă dacă pachetele care nu sunt DNS intră în interfața TUN.
  2. Înregistrează numele interogării, decizia și codul de răspuns pentru un nume blocat și unul aprobat.
  3. Confirmă că socketul resolverului este protejat și că resolverul ales răspunde din interiorul tunelului.
  4. Confirmă MTU-ul interfeței și suma de control a unui răspuns construit manual.
  5. Repetă aceeași pagină pe Wi-Fi și în rețeaua mobilă, pentru că resolverii operatorului și MTU-ul căii diferă.

Dacă toate cinci rezistă și pagina tot se blochează, următorul suspect este transmitătorul însuși: compară o cerere capturată cu un răspuns capturat și caută un număr de octeți lipsă sau un socket pe jumătate închis. Referința VpnService.Builder acoperă contractele de rută, DNS și MTU, iar protect() este un apel VpnService separat, nu o opțiune a builder-ului; o interfață conectată înseamnă doar că primul contract este îndeplinit.

Articole similare