Du tilføjede et domæne til tilladelseslisten, forespørgslen når din app, matcheren returnerer tilladt, og siden indlæses stadig aldrig. På det tidspunkt begynder de fleste at genskrive matcheren og tilføje jokertegnssammenligninger. Efter min erfaring er matcheren som regel korrekt, og fejlen ligger et lag senere: i DNS-svaret, du syntetiserede, i ruten, du installerede, eller i socketten, du brugte til at videresende forespørgslen. Listen er den nemme del; pakkestien er der, hvor tilladelseslister går i stykker.

Matcheren er sjældent fejlen

Det første, der skal skilles, er blokeret fra i stykker. Et blokeret navn bør fejle hurtigt og synligt, fordi browseren får et svar om, at navnet ikke findes, og stopper. Et i stykker navn hænger, prøver igen over IPv6, falder tilbage til en anden transport og efterlader dig med en snurrende indikator. Hvis du ser en snurrende indikator, svarer din tilladelsesliste sandsynligvis noget, klienten ikke kan bruge.

En tom A-post er den klassiske fejl. At returnere NOERROR med nul svar ser ud som et gyldigt svar, så stub-resolveren accepterer det, browseren prøver igen, prøver AAAA, prøver en anden resolver og giver op meget senere. En svarkode 3, NXDOMAIN, med spørgsmålssektionen gentaget og transaktions-ID'et kopieret afslutter samtalen med det samme. Brug den til hvert navn, du bevidst afviser.

IPv6 fortjener den samme beslutning i stedet for en standard. Hvis du svarer NXDOMAIN på A og lader AAAA passere, kan klienten nå det blokerede navn over IPv6, og filteret lækker. Nogle implementeringer svarer på blokerede AAAA-forespørgsler med en loopback-adresse, så forbindelsen fejler med det samme i stedet for at falde tilbage. Vælg én politik, anvend den på begge posttyper, og log begge.

En rute, der fanger alt, ejer alt

Et design med kun DNS tilføjer aldrig addRoute("0.0.0.0", 0), så kun forespørgsler kommer ind i TUN-grænsefladen, og alt andet fortsætter med at bruge det underliggende netværk. Det er den sikre form, og det forklarer også, hvorfor nogle tilladelseslister ser ud til at virke på Wi-Fi og fejle på mobilnettet.

I det øjeblik en standardrute findes, er hver strøm din. En TCP-pakke, der ankommer til grænsefladen og hverken videresendes eller besvares, bliver i køen, indtil klienten timer ud, og der er ingen fejl at logge, fordi intet fejlede. Jeg har set nogen bruge en eftermiddag på en i stykker tilladelsesliste, som i virkeligheden var en manglende videresender til port 443. Log protokollen og destinationen for hver pakke, du beslutter at droppe.

Resolver-socketten skal forlade tunnelen

Videresendelse er, hvor den næste fælde sidder. Operatørresolvere bor ofte på en privat adresse, som tunnelen ikke kan nå, så den forespørgsel, du videresender, går ingen steder, og appen rapporterer en læsetimeout. Send godkendte forespørgsler til en resolver, du ved er tilgængelig, og giv den socket til VpnService.protect() først. En ubeskyttet socket sender forespørgslen tilbage i din egen tunnel, hvor den når appen igen og cirkulerer, indtil timeouten udløses.

Fejlen ser identisk ud med et netværksudfald, og derfor overlever den så længe i fejlrapporter. Hold tællere for sendte forespørgsler og modtagne svar: hvis det første tal stiger, og det andet bliver på nul, kigger du på socketten, ikke listen.

Størrelse og kontrolsummer fejler uden en besked

Stille drop kommer også fra størrelse. Et DNS-svar med et stort svarssæt eller et TCP-håndtryk med rigelige valgmuligheder kan overskride en stiforbindelses-MTU, du aldrig har sat eksplicit; fragmentet forsvinder opstrøms, og klienten prøver enten igen med mindre eller holder op med at prøve. At sætte grænsefladens MTU til 1280 er et konservativt valg, der overlever de fleste mobilstier, og at være eksplicit er bedre end at arve en standard, ingen har målt.

Hvis du selv bygger pakker, er kontrolsummen den anden stille dræber. En UDP-kontrolsum beregnet over en pseudo-header, der udelader UDP-længden eller protokolfeltet, giver en pakke, der ser velformet ud hos dig og kasseres af modtageren uden spor. Sammenlign din pseudo-header med specifikationen felt for felt, og udskriv den beregnede kontrolsum for en kendt strøm, mens du fejlretter.

Hvad en VpnService ikke gør for dig

To grænser er værd at kende, før du designer omkring dem. Ingen almindelig app kan installere en systemdækkende proxy eller PAC-fil uden for Wi-Fi, og den del har ikke ændret sig. Stien pr. app har: før API 29 kunne VpnService.Builder slet ikke bære en proxy, og fra API 29 giver setHttpProxy(ProxyInfo.buildPacProxy(uri)) platformen et PAC-script at evaluere for VPN-netværket. Evaluer regler i din egen kode, når du retter dig mod ældre API'er, eller når PAC-modellen ikke kan udtrykke reglen. UDP er heller ikke gratis: browsere sender kun TCP gennem en SOCKS-proxy, og de fleste SOCKS5-klienter implementerer aldrig UDP ASSOCIATE, så et proxy-baseret design dropper eller lækker UDP, mens TCP ser sund ud.

Den anden grænse er grunden til, at en UDP-baseret protokol kan virke på et almindeligt netværk og fejle i det øjeblik, din VPN forbinder. Hvis tunnelen forventes at bære UDP, skal videresenderen implementere videresendelsesstien for det, ikke kun TCP-stien med en anden sockettype.

Kontroller i denne rækkefølge

Arbejd listen igennem i rækkefølge og stop ved det første trin, der fejler, fordi hver senere kontrol er støj, indtil den forrige holder.

  1. List de installerede ruter, og bekræft, om ikke-DNS-pakker overhovedet kommer ind i TUN-grænsefladen.
  2. Log forespørgselsnavnet, beslutningen og svarkoden for et blokeret og et godkendt navn.
  3. Bekræft, at resolver-socketten er beskyttet, og at den valgte resolver svarer indefra tunnelen.
  4. Bekræft grænsefladens MTU og kontrolsummen af et svar, du selv har bygget.
  5. Gentag den samme side på Wi-Fi og mobilnet, fordi operatørresolvere og sti-MTU er forskellige.

Hvis alle fem holder, og siden stadig hænger, er den næste mistænkte videresenderen selv: sammenlign en opfanget anmodning med et opfanget svar, og led efter et manglende byteantal eller en halvlukket socket. VpnService.Builder-reference dækker kontrakterne for rute, DNS og MTU, og protect() er et separat VpnService-kald og ikke en builder-indstilling; en tilsluttet grænseflade betyder kun, at den første kontrakt er opfyldt.

Relaterede artikler