Predor se dvigne, ikona ključa se pojavi, vsaka zahteva v brskalniku pa še vedno poteče. V aplikaciji VpnService ta kombinacija običajno pomeni, da je bralna zanka živa in je izhodna polovica pokvarjena, ne pa da je strežnik zavrnil rokovanje. Android je vmesnik že ustvaril, vanj premaknil promet aplikacije in vašemu procesu predal opisovalec datoteke. Vse za tem opisovalcem je vaša koda in en sam manjkajoč klic zadostuje, da pogoltne celoten tok.

Kaj sistem da in česa ne

establish() vrne ParcelFileDescriptor za vmesnik tun. Sistem vanj zapisuje odhodne pakete IP in pričakuje, da jih bo vaš proces prebral, odločil, kam gredo, in jih poslal prek običajne vtičnice. Spodaj ni posredovanja v jedru, zato se paketi, ko nihče ne bere, zavržejo in števci vmesnika se nikoli ne premaknejo.

Začnite s štetjem namesto z ugibanjem. Povečajte števec pri vsakem uspešnem branju in zabeležite cilj ter protokol prvih paketov. Če števec ostane na nič, medtem ko se ustvarja promet, je napaka nad vašo kodo za posredovanje, kar zoži iskanje na konfiguracijo graditelja in način branja. Če se števec premakne in brskalnik še vedno ne deluje, je napaka na poti pošiljanja ali na poti nazaj.

Blokirajoče branje odloči, ali se zanka premakne

setBlocking(true) postavi opisovalec v blokirajoči način in obstaja od API 21. V starejših izdajah je rešitev majhen pomočnik JNI, ki počisti O_NONBLOCK s fcntl na surovem opisovalcu. Način je pomemben, ker določa, kaj sme branje brez čakajočega paketa storiti.

V neblokirajočem načinu se tako branje takoj vrne z negativno vrednostjo in zanka, ki rezultat ignorira, vrti jedro, ne da bi premaknila promet. V blokirajočem načinu branje čaka, kar nit za posredovanje želi, a niti potem ni več mogoče prekiniti. Zaprtje opisovalca je podprt način, da branje ne uspe, da lahko zanka izstopi. Ubijanje procesa ali prekinitev niti lahko pusti naslednji zagon z opisovalcem, ki ne ustreza več vmesniku.

Berite en paket na klic v medpomnilnik velikosti MTU vmesnika in ničlo ali negativni rezultat obravnavajte kot napako, ne kot prazen paket.

protect() pred prvim pošiljanjem, ne po njem

Vsaka vtičnica, ki prenaša zapakiran paket, mora biti najprej zaščitena. Nezaščitena vtičnica sledi istim pravilom usmerjanja, ki ste jih pravkar nastavili, zato se njeni paketi vrnejo v predor in vaša lastna bralna zanka jih znova pobere. Predor nato neskončno posreduje lastni izhod, od zunaj pa je videti kot povezava, ki se odpre in nikoli ne odgovori.

Vrstni red je protect, nato connect ali bind, nato send. Klic protect po connect lahko pusti prejšnjo odločitev o poti, zato vtičnica še naprej uporablja napačno pot, čeprav je klic vrnil true. Preverite vrnjeni boolean in glasno ne uspejte, ko je false, namesto da bi vstopili v pošiljanje, ki ne more delovati.

Napaka dovoljenja iz sendto na vtičnici, ki se ni nikoli dotaknila protect(), je običajen podpis te napake.

Primer UDP ne prenaša HTTPS

Primer posredovanja, ki ga kopira večina vadnic, obravnava samo UDP. TCP potrebuje rokovanje, zaporedne številke, potrditve in ponovno pošiljanje, zanka, ki kopira tovor med opisovalcem tun in vtičnico, pa ne ponuja nič od tega. Zato zahteva DNS prek UDP lahko gre skozi, medtem ko se vsaka zahteva HTTPS ustavi. Bodisi zaženite sklad TCP v uporabniškem prostoru bodisi povezave predajte lokalnemu posredniku, ki zaključi TCP na napravi in posreduje tok.

Preizkusite delitev, preden kar koli prepišete: pošljite eno zahtevo UDP in odprite eno povezavo TCP do istega gostitelja. Če pot UDP deluje in se pot TCP ustavi pri prvem SYN, je problem v kodi za posredovanje in ne v strežniku. Ta ena primerjava prihrani veliko časa, porabljenega za branje strežniških dnevnikov, ki ne vsebujejo nič koristnega.

Seznam poti in filtri po aplikacijah omejujejo, kaj vidite

addAddress, addRoute in addDns določajo, kaj sistem pošlje v predor. Cilj zunaj seznama poti nikoli ne doseže vašega opisovalca, zato nobena količina logike posredovanja tega ne popravi. Zapisujte klice graditelja in jih primerjajte z naslovi, ki jih aplikacija resnično poskuša doseči.

Izbira po aplikacijah ima svoj prag: addAllowedApplication in addDisallowedApplication zahtevata API 21, v starejših izdajah pa teh metod preprosto ni. Zasnova, ki filtrira po paketu, tam sploh ne more delovati.

Ponovna vzpostavitev pušča zastarele opisovalce

Nov klic establish() vrne nov opisovalec in zapre prejšnji vmesnik. Stari opisovalec je zdaj neveljaven, zato nit, ki na njem še blokira, dobi napako, vtičnice, ustvarjene za staro pot, pa bo morda treba zamenjati. onRevoke je povratni klic za predor, ki so ga uporabnik ali sistem odvzeli. Vzdrževanje življenjskega cikla pri enem lastniku, ki pred ponovno vzpostavitvijo ustavi bralca, prepreči večino stanj povezano-a-mrtvo.

Vsako napako berite po koraku, ki mu pripada. Ponastavitev povezave iz recvfrom pomeni, da se je oddaljeni konec zaprl, medtem ko je vaša koda še brala. Napaka zaradi napačnega naslova kaže na razčlenjevanje glave ali napako odmika, ne na omrežje.

Vrstni red dela: blokirajoči način vklopljen, en paket na branje, glava razčlenjena, vtičnica zaščitena, tovor poslan, čisto zaprtje pri predaji in seznam poti, ki pokriva cilj.

Sorodni članki