Android VpnService koplar til, men ingen pakkar når nettet
Tunnelen er oppe og sidene feilar framleis: sjekk lesemodus, socket-vern, TCP-støtte og ruttelista før du gir VPN-tenaren skulda.
Tunnelen kjem opp, nøkkelikonet viser seg, og kvar førespurnad i nettlesaren går framleis ut på tid. I ein VpnService-app tyder den kombinasjonen vanlegvis at leseløkka lever og utgangshalvdelen er øydelagd, ikkje at tenaren avviste handtrykket. Android har alt oppretta grensesnittet, flytta apptrafikken inn i det og gjeve prosessen din ei filskildring. Alt etter den skildringa er koden din, og eitt manglande kall er nok til å svelgje heile straumen.
Kva systemet gir deg, og kva det ikkje gir
establish() returnerer ein ParcelFileDescriptor for eit tun-grensesnitt. Systemet skriv utgåande IP-pakkar inn i det og ventar at prosessen din les dei, avgjer kvar dei skal og sender dei gjennom ein vanleg socket. Under ligg det inga kjernesending, så når ingen les, blir pakkane sleppte og teljarane til grensesnittet flyttar seg aldri.
Byrja med å telje i staden for å gjette. Auk ein teljar ved kvar vellukka lesing og logg destinasjon og protokoll for dei første pakkane. Blir teljaren ståande på null medan trafikk blir generert, ligg feilen før videresendingskoden din, noko som snevrar søket inn til builder-konfigurasjonen og lesemodusen. Flyttar teljaren seg og nettlesaren feilar framleis, ligg feilen i sendevegen eller på vegen tilbake.
Blokkerande lesing avgjer om løkka flyttar seg
setBlocking(true) set skildringa i blokkerande modus og har eksistert sidan API 21. På eldre utgåver er løysinga ein liten JNI-hjelpar som fjernar O_NONBLOCK med fcntl på den rå skildringa. Modusen betyr noko fordi han avgjer kva ei lesing utan ventande pakke har lov til å gjere.
I ikkje-blokkerande modus returnerer den lesinga straks med ein negativ verdi, og ei løkke som ignorerer resultatet spinner ein kjerne utan å flytte trafikk i det heile. I blokkerande modus ventar lesinga, som er det ein videresendingstråd vil, men tråden kan då ikkje lenger avbrytast. Å lukke skildringa er den støtta måten å få lesinga til å feile slik at løkka kan avsluttast. Å drepe prosessen eller avbryte tråden kan i staden late neste oppstart stå att med ei skildring som ikkje lenger passar til grensesnittet.
Les éin pakke per kall inn i ein buffer på storleik med MTU-en til grensesnittet, og handsame null eller ein negativ retur som ein feil, ikkje som ein tom pakke.
protect() før den første sendinga, ikkje etter
Kvar socket som ber ein innkapsla pakke må vernast først. Ein uverna socket følgjer dei same rutereglane du nettopp installerte, så pakkane går tilbake i tunnelen og di eiga leseløkke plukkar dei opp att. Tunnelen videresender då si eiga utgang i det uendelege, og utanfrå ser det ut som ei tilkopling som opnar og aldri svarar.
Rekkjefølgja er protect, deretter connect eller bind, deretter send. Å kalle protect etter connect kan late den tidlegare ruteavgjerda stå, så socketen held fram med å bruke feil veg sjølv om kallet returnerte true. Sjekk booleanen han returnerer og feil høgt når han er false, i staden for å gå inn i ei sending som ikkje kan verke.
Ein tillatingsfeil frå sendto på ein socket som aldri har rørt protect(), er den vanlege signaturen på denne feilen.
UDP-dømet ber ikkje HTTPS
Videresendingsdømet som dei fleste rettleiarar kopierer, handterer UDP og ingenting anna. TCP treng eit handtrykk, sekvensnummer, stadfestingar og omsending, og ei løkke som kopierer last mellom ei tun-skildring og ein socket gir ingen av delane. Derfor kan ein UDP-DNS-førespurnad kome gjennom medan kvar HTTPS-førespurnad stoppar. Køyr anten ein TCP-stakk i brukarrommet eller overlat tilkoplingane til ein lokal proxy som avsluttar TCP på eininga og videresender straumen.
Test delinga før du skriv om noko: send éin UDP-førespurnad og opne éi TCP-tilkopling til same vert. Viss UDP-vegen verkar og TCP-vegen heng ved første SYN, er videresendingskoden problemet og ikkje tenaren. Den eine samanlikninga sparar mykje tid som blir brukt på å lese tenarloggar utan noko nyttig innhald.
Ruttelista og filter per app avgrensar kva du ser
addAddress, addRoute og addDns definerer kva systemet sender inn i tunnelen. Ein destinasjon utanfor ruttelista når aldri skildringa di, så inga mengd videresendingslogikk vil rette det. Logg builder-kalla du gjer og samanlikn dei med adressene appen faktisk prøver å nå.
Val per app har si eiga nedre grense: addAllowedApplication og addDisallowedApplication krev API 21, og på eldre utgåver manglar metodane rett og slett. Eit design som filtrerer på pakke kan ikkje køyre der i det heile.
Gjenoppretting lét att utdaterte skildringar
Eit nytt kall til establish() returnerer ei ny skildring og lukkar førre grensesnitt. Den gamle skildringa er no ugyldig, så ein tråd som framleis blokkerer på henne får ein feil, og sockets laga for den gamle vegen må kanskje bytast ut. onRevoke er tilbakemeldinga for ein tunnel som brukaren eller systemet har teke bort. Å halde livssyklusen hos éin eigar, som stoppar lesaren før ny oppretting, hindrar dei fleste tilkopla-men-døde tilstandane.
Les kvar feil etter trinnet han høyrer til. Ei tilbakestilling av tilkoplinga frå recvfrom tyder at den fjerne enden lukka medan koden din framleis las. Ein feil om dårleg adresse peikar på header-parsing eller ein offset-feil, ikkje på nettet.
Arbeidsrekkjefølgje: blokkerande modus på, éi pakke per lesing, header parsa, socket verna, last send, rein lukking ved overlevering og ei rutteliste som dekkjer destinasjonen.