Android VpnService lidhet por asnjë paketë nuk arrin në rrjet
Tuneli është lart dhe faqet vazhdojnë të dështojnë: kontrolloni mënyrën e leximit, mbrojtjen e socket-it, mbështetjen TCP dhe listën e rrugëve përpara se të fajësoni serverin VPN.
Tuneli ngrihet, ikona e çelësit shfaqet dhe çdo kërkesë në shfletues përsëri skadon. Në një aplikacion VpnService ky kombinim zakonisht do të thotë që cikli i leximit është i gjallë dhe gjysma e daljes është e prishur, jo që serveri refuzoi shtrëngimin e duarve. Android e ka krijuar tashmë ndërfaqen, e ka zhvendosur trafikun e aplikacionit brenda saj dhe i ka dhënë procesit tuaj një përshkrues skedari. Gjithçka pas atij përshkruesi është kodi juaj, dhe një thirrje që mungon mjafton për të gëlltitur gjithë rrjedhën.
Çfarë ju jep sistemi dhe çfarë jo
establish() kthen një ParcelFileDescriptor për një ndërfaqe tun. Sistemi shkruan paketat IP dalëse në të dhe pret që procesi juaj t'i lexojë, të vendosë ku shkojnë dhe t'i dërgojë përmes një socket-i të zakonshëm. Poshtë nuk ka përcjellje në kernel, prandaj kur askush nuk lexon, paketat hidhen dhe numëruesit e ndërfaqes nuk lëvizin kurrë.
Filloni duke numëruar në vend të hamendjes. Rritni një numërues në çdo lexim të suksesshëm dhe regjistroni destinacionin dhe protokollin e paketave të para. Nëse numëruesi mbetet zero ndërsa krijohet trafik, defekti është mbi kodin tuaj të përcjelljes, gjë që e ngushton kërkimin në konfigurimin e builder-it dhe mënyrën e leximit. Nëse numëruesi lëviz dhe shfletuesi përsëri dështon, defekti është në rrugën e dërgimit ose në atë të kthimit.
Leximi bllokues vendos nëse cikli lëviz
setBlocking(true) e vendos përshkruesin në mënyrë bllokuese dhe ekziston që nga API 21. Në versionet e vjetra zgjidhja është një ndihmës i vogël JNI që pastron O_NONBLOCK me fcntl në përshkruesin e papërpunuar. Mënyra ka rëndësi sepse vendos çfarë lejohet të bëjë një lexim pa paketë në pritje.
Në mënyrën jo bllokuese ai lexim kthehet menjëherë me një vlerë negative dhe një cikël që injoron rezultatin rrotullon një bërthamë pa lëvizur asnjë trafik. Në mënyrën bllokuese leximi pret, gjë që dëshiron një fije përcjelljeje, por fija atëherë nuk mund të ndërpritet më. Mbyllja e përshkruesit është mënyra e mbështetur për ta bërë leximin të dështojë, që cikli të mund të dalë. Vrasja e procesit ose ndërprerja e fijes mund ta lërë nisjen tjetër me një përshkrues që nuk përputhet më me ndërfaqen.
Lexoni një paketë për thirrje në një bufer me madhësinë e MTU-së së ndërfaqes dhe trajtoni zeron ose një kthim negativ si gabim, jo si paketë boshe.
protect() përpara dërgimit të parë, jo pas tij
Çdo socket që mbart një paketë të inkapsuluar duhet të mbrohet fillimisht. Një socket i pambrojtur ndjek të njëjtat rregulla rrugëtimi që sapo vendosët, prandaj paketat e tij kthehen në tunel dhe cikli juaj i leximit i merr përsëri. Tuneli pastaj përcjell daljen e vet pa fund, dhe nga jashtë duket si një lidhje që hapet dhe nuk përgjigjet kurrë.
Rendi është protect, pastaj connect ose bind, pastaj send. Thirrja e protect pas connect mund të lërë vendimin e mëparshëm të rrugës, prandaj socket-i vazhdon të përdorë rrugën e gabuar edhe pse thirrja ktheu true. Kontrolloni boolean-in që kthen dhe dështoni me zë kur është false, në vend që të hyni në një dërgim që nuk mund të funksionojë.
Një gabim leje nga sendto në një socket që nuk e ka prekur kurrë protect() është nënshkrimi i zakonshëm i këtij gabimi.
Shembulli UDP nuk mbart HTTPS
Shembulli i përcjelljes që kopjojnë shumica e udhëzuesve trajton vetëm UDP. TCP kërkon shtrëngim duarsh, numra sekuence, konfirmime dhe ridërgim, dhe një cikël që kopjon ngarkesën midis një përshkruesi tun dhe një socket-i nuk ofron asnjërën. Kjo është arsyeja pse një kërkesë DNS përmes UDP mund të kalojë ndërsa çdo kërkesë HTTPS ngec. Ose ekzekutoni një stivë TCP në hapësirën e përdoruesit, ose jepini lidhjet një proxy lokal që përfundon TCP në pajisje dhe përcjell rrjedhën.
Testoni ndarjen përpara se të rishkruani çdo gjë: dërgoni një kërkesë UDP dhe hapni një lidhje TCP drejt të njëjtit host. Nëse rruga UDP funksionon dhe rruga TCP ngec në SYN-in e parë, problemi është kodi i përcjelljes dhe jo serveri. Ai krahasim i vetëm kursen shumë kohë të harxhuar duke lexuar regjistrat e serverit që nuk përmbajnë asgjë të dobishme.
Lista e rrugëve dhe filtrat për aplikacion kufizojnë atë që shihni
addAddress, addRoute dhe addDns përcaktojnë çfarë dërgon sistemi në tunel. Një destinacion jashtë listës së rrugëve nuk arrin kurrë te përshkruesi juaj, prandaj asnjë sasi logjike përcjelljeje nuk e rregullon. Regjistroni thirrjet e builder-it që bëni dhe krahasoni ato me adresat që aplikacioni vërtet përpiqet të arrijë.
Zgjedhja për aplikacion ka pragun e vet: addAllowedApplication dhe addDisallowedApplication kërkojnë API 21, dhe në versionet e vjetra metodat thjesht mungojnë. Një dizajn që filtron sipas paketës nuk mund të ekzekutohet fare atje.
Rivendosja lë përshkrues të vjetruar
Një thirrje e re për establish() kthen një përshkrues të ri dhe mbyll ndërfaqen e mëparshme. Përshkruesi i vjetër tani është i pavlefshëm, prandaj një fije ende e bllokuar mbi të merr gabim, dhe socket-at e krijuar për rrugën e vjetër mund të duhet të zëvendësohen. onRevoke është kthimi i thirrjes për një tunel që përdoruesi ose sistemi ka hequr. Mbajtja e ciklit të jetës te një pronar i vetëm, që ndalon lexuesin përpara rivendosjes, parandalon shumicën e gjendjeve të lidhur-por-të vdekur.
Lexoni çdo gabim sipas hapit të cilit i përket. Një rivendosje lidhjeje nga recvfrom do të thotë që skaji i largët u mbyll ndërsa kodi juaj po lexonte ende. Një gabim adrese të gabuar tregon për analizimin e kokës ose një bug zhvendosjeje, jo për rrjetin.
Rendi i punës: modaliteti bllokues ndezur, një paketë për lexim, koka e analizuar, socket-i i mbrojtur, ngarkesa e dërguar, mbyllje e pastër gjatë dorëzimit dhe një listë rrugësh që mbulon destinacionin.