Die tonnel kom op, die sleutelikoon verskyn, en elke versoek in die blaaier verstryk steeds. In 'n VpnService-toepassing beteken dié kombinasie gewoonlik dat die leeslus leef en die uitvoerhelfte stukkend is, nie dat die bediener die handdruk verwerp het nie. Android het reeds die koppelvlak geskep, die toepassing se verkeer daarin geskuif en jou proses 'n lêerbeskrywer gegee. Alles ná daardie beskrywer is jou kode, en een ontbrekende oproep is genoeg om die hele stroom te verswelg.

Wat die stelsel vir jou gee, en wat nie

establish() gee 'n ParcelFileDescriptor vir 'n tun-koppelvlak terug. Die stelsel skryf uitgaande IP-pakette daarin en verwag dat jou proses hulle lees, besluit waarheen hulle gaan en hulle deur 'n gewone socket stuur. Daar is geen kern-aanstuur daaronder nie, so wanneer niemand lees nie, word die pakette laat vaar en beweeg die koppelvlak-tellers nooit.

Begin deur te tel eerder as om te raai. Verhoog 'n teller by elke suksesvolle lees en teken die bestemming en protokol van die eerste paar pakette aan. As die teller op nul bly terwyl verkeer gegenereer word, sit die fout stroomop van jou aanstuur-kode, wat die soektog vernou tot die bouer-konfigurasie en die leesmodus. As die teller beweeg en die blaaier faal steeds, is die fout in die stuurpad of op die terugweg.

Blokkerende lees bepaal of die lus beweeg

setBlocking(true) plaas die beskrywer in blokkerende modus en bestaan sedert API 21. Op ouer weergawes is die regstelling 'n klein JNI-hulp wat O_NONBLOCK met fcntl op die rou beskrywer skoonmaak. Die modus maak saak omdat dit bepaal wat 'n lees sonder 'n hangende pakket mag doen.

In nie-blokkerende modus keer daardie lees dadelik terug met 'n negatiewe waarde, en 'n lus wat die resultaat ignoreer, draai 'n kern rond sonder om enige verkeer te beweeg. In blokkerende modus wag die lees, wat 'n aanstuur-draad wil hê, maar die draad kan dan nie meer onderbreek word nie. Om die beskrywer te sluit is die ondersteunde manier om die lees te laat misluk sodat die lus kan uitgaan. Om eerder die proses te dood of die draad te onderbreek, kan die volgende begin laat sit met 'n beskrywer wat nie meer by die koppelvlak pas nie.

Lees een pakket per oproep in 'n buffer wat vir die koppelvlak se MTU gegroot is, en hanteer nul of 'n negatiewe terugkeer as 'n fout eerder as 'n leë pakket.

protect() voor die eerste stuur, nie daarna nie

Elke socket wat 'n ingekapselde pakket dra, moet eerste beskerm word. 'n Onbeskermde socket gehoorsaam dieselfde roetereëls wat jy pas ingestel het, so sy pakette gaan terug in die tonnel en jou eie leeslus tel hulle weer op. Die tonnel stuur dan sy eie uitset onbepaald aan, en van buite lyk dit soos 'n verbinding wat oopmaak en nooit antwoord nie.

Die volgorde is protect, dan connect of bind, dan send. Om protect ná connect te roep, kan die vroeëre roetebesluit laat staan, so die socket bly die verkeerde pad gebruik selfs al het die oproep true teruggegee. Kontroleer die boolean wat dit teruggee en faal hardop wanneer dit false is, in plaas daarvan om in 'n stuur te stap wat nie kan werk nie.

'n Toestemmingsfout van sendto op 'n socket wat nooit protect() aangeraak het nie, is die gewone kenmerk van hierdie fout.

Die UDP-voorbeeld dra nie HTTPS nie

Die aanstuur-voorbeeld wat die meeste gidse kopieer, hanteer UDP en niks anders nie. TCP het 'n handdruk, volgordenommers, bevestigings en herversending nodig, en 'n lus wat vragte tussen 'n tun-beskrywer en 'n socket kopieer, verskaf nie een daarvan nie. Dit is hoekom 'n UDP-DNS-navraag kan deurkom terwyl elke HTTPS-versoek vassteek. Begin 'n gebruikersruimte-TCP-stapel of gee verbindings aan 'n plaaslike proxy wat TCP op die toestel beëindig en die stroom aanstuur.

Toets die skeiding voordat jy iets herskryf: stuur een UDP-navraag en maak een TCP-verbinding na dieselfde gasheer oop. As die UDP-pad werk en die TCP-pad by die eerste SYN hang, is die aanstuur-kode die probleem en die bediener nie. Daardie enkele vergelyking spaar baie tyd wat aan bediener-logboeke bestee word wat niks nuttigs bevat nie.

Roetelys en per-toepassing-filters bepaal wat jy sien

addAddress, addRoute en addDns bepaal wat die stelsel in die tonnel stuur. 'n Bestemming buite die roetelys bereik nooit jou beskrywer nie, so geen hoeveelheid aanstuur-logika sal dit regmaak nie. Teken die bouer-oproepe aan wat jy maak en vergelyk hulle met die adresse wat die toepassing werklik probeer bereik.

Per-toepassing-keuse het sy eie vloer: addAllowedApplication en addDisallowedApplication het API 21 nodig, en op ouer weergawes is die metodes eenvoudig afwesig. 'n Ontwerp wat volgens pakket filter, kan daar glad nie loop nie.

Her-vestiging laat verouderde beskrywers agter

Om establish() weer te roep, gee 'n nuwe beskrywer en sluit die vorige koppelvlak. Die ou beskrywer is nou ongeldig, so 'n draad wat nog daarop blokkeer, kry 'n fout, en sockets wat vir die ou pad geskep is, moet moontlik vervang word. onRevoke is die terugroep vir 'n tonnel wat die gebruiker of die stelsel weggeneem het. Om die lewensiklus by een eienaar te hou, wat die leser stop voordat weer gevestig word, verhoed die meeste van die verbonde-maar-dood-toestande.

Lees elke fout volgens die stap waaraan dit behoort. 'n Verbinding-herstel van recvfrom beteken die ander kant het gesluit terwyl jou kode nog gelees het. 'n Slegte-adres-fout wys na kop-ontleding of 'n offset-fout, nie na die netwerk nie.

Volgorde van werk: blokkerende modus aan, een pakket per lees, kop ontleed, socket beskerm, vrag gestuur, skoon sluiting by oordrag, en 'n roetelys wat die bestemming dek.

Verwante artikels