Sie haben eine Domain auf die Freigabeliste gesetzt, die Abfrage erreicht Ihre App, der Matcher liefert erlaubt, und trotzdem lädt die Seite nie. An diesem Punkt beginnen die meisten, den Matcher neu zu schreiben und Platzhaltervergleiche hinzuzufügen. Meiner Erfahrung nach ist der Matcher meist korrekt, und der Fehler liegt eine Ebene tiefer: in der DNS-Antwort, die Sie synthetisiert haben, in der Route, die Sie installiert haben, oder im Socket, mit dem Sie die Abfrage weitergeleitet haben. Die Liste ist der einfache Teil; der Paketpfad ist der Ort, an dem Freigabelisten brechen.

Der Matcher ist selten der Fehler

Zuerst gilt es, blockiert von kaputt zu trennen. Ein blockierter Name sollte schnell und sichtbar scheitern, denn der Browser bekommt eine Antwort, dass der Name nicht existiert, und hört auf. Ein kaputter Name hängt, wiederholt über IPv6, fällt auf einen anderen Transport zurück und lässt Sie mit einem drehenden Ladekreis zurück. Wenn Sie einen drehenden Ladekreis sehen, antwortet Ihre Freigabeliste wahrscheinlich mit etwas, worauf der Client nicht reagieren kann.

Ein leerer A-Record ist der klassische Fehler. NOERROR mit null Antworten zurückzugeben sieht wie eine gültige Antwort aus, also akzeptiert der Stub-Resolver sie, der Browser versucht es erneut, probiert AAAA, probiert einen anderen Resolver und gibt viel später auf. Ein Antwortcode 3, NXDOMAIN, mit wiederholtem Frageabschnitt und kopierter Transaktions-ID beendet das Gespräch sofort. Verwenden Sie ihn für jeden Namen, den Sie bewusst ablehnen.

IPv6 verdient dieselbe Entscheidung statt einer Voreinstellung. Wenn Sie für A mit NXDOMAIN antworten und AAAA durchlassen, kann der Client den blockierten Namen über IPv6 erreichen, und der Filter leckt. Manche Implementierungen beantworten blockierte AAAA-Abfragen mit einer Loopback-Adresse, damit die Verbindung sofort scheitert, statt auf einen Ersatzweg auszuweichen. Wählen Sie eine Richtlinie, wenden Sie sie auf beide Record-Typen an und protokollieren Sie beide.

Eine Route, die alles fängt, besitzt alles

Ein reines DNS-Design fügt nie addRoute("0.0.0.0", 0) hinzu, also gelangen nur Abfragen in die TUN-Schnittstelle, und alles andere nutzt weiter das darunterliegende Netz. Das ist die sichere Form, und es erklärt auch, warum manche Freigabelisten unter Wi-Fi zu funktionieren scheinen und im Mobilfunknetz scheitern.

In dem Moment, in dem eine Standardroute existiert, gehört jeder Fluss Ihnen. Ein TCP-Paket, das an der Schnittstelle ankommt und weder weitergeleitet noch beantwortet wird, wartet in der Warteschlange, bis der Client in einen Timeout läuft, und es gibt keinen Fehler zu protokollieren, weil nichts fehlgeschlagen ist. Ich habe jemanden einen Nachmittag an einer kaputten Freigabeliste verbringen sehen, die in Wahrheit ein fehlender Weiterleiter für Port 443 war. Protokollieren Sie Protokoll und Ziel jedes Pakets, das Sie verwerfen.

Der Resolver-Socket muss den Tunnel verlassen

Die Weiterleitung ist der Ort der nächsten Falle. Resolver des Mobilfunkanbieters liegen oft auf einer privaten Adresse, die der Tunnel nicht erreicht, also geht die weitergeleitete Abfrage nirgendwohin, und die App meldet einen Lese-Timeout. Senden Sie genehmigte Abfragen an einen Resolver, von dem Sie wissen, dass er erreichbar ist, und übergeben Sie diesen Socket zuerst an VpnService.protect(). Ein ungeschützter Socket schickt die Abfrage zurück in Ihren eigenen Tunnel, wo sie wieder bei der App ankommt und kreist, bis der Timeout zuschlägt.

Der Fehler sieht identisch aus wie ein Netzausfall, und deshalb überlebt er so lange in Fehlerberichten. Führen Sie Zähler für gesendete Abfragen und empfangene Antworten: Wenn die erste Zahl steigt und die zweite bei null bleibt, betrachten Sie den Socket, nicht die Liste.

Größe und Prüfsummen scheitern ohne Meldung

Stille Verwerfungen kommen auch von der Größe. Eine DNS-Antwort mit großem Antwortsatz oder ein TCP-Handshake mit großzügigen Optionen kann einen Pfad-MTU überschreiten, den Sie nie ausdrücklich gesetzt haben; das Fragment verschwindet stromaufwärts, und der Client versucht es entweder kleiner erneut oder hört auf. Die Schnittstellen-MTU auf 1280 zu setzen ist eine konservative Wahl, die die meisten Mobilfunkpfade übersteht, und ausdrücklich zu sein ist besser, als einen Standard zu erben, den niemand gemessen hat.

Wenn Sie Pakete selbst bauen, ist die Prüfsumme der andere stille Killer. Eine UDP-Prüfsumme über einen Pseudo-Header, der die UDP-Länge oder das Protokollfeld auslässt, erzeugt ein Paket, das auf Ihrer Seite wohlgeformt aussieht und vom Empfänger spurlos verworfen wird. Vergleichen Sie Ihren Pseudo-Header Feld für Feld mit der Spezifikation, und drucken Sie beim Debuggen die berechnete Prüfsumme für einen bekannten Fluss aus.

Was ein VpnService nicht für Sie tut

Zwei Grenzen lohnen sich, bevor Sie darum herum entwerfen. Keine gewöhnliche App kann außerhalb von Wi-Fi einen systemweiten Proxy oder eine PAC-Datei installieren, und dieser Teil hat sich nicht geändert. Der Pfad pro App schon: vor API 29 konnte VpnService.Builder überhaupt keinen Proxy tragen, und ab API 29 übergibt setHttpProxy(ProxyInfo.buildPacProxy(uri)) der Plattform ein PAC-Skript zur Auswertung für das VPN-Netzwerk. Werten Sie Regeln im eigenen Code aus, wenn Sie ältere APIs anvisieren oder wenn das PAC-Modell die Regel nicht ausdrücken kann. UDP ist auch nicht kostenlos: Browser senden über einen SOCKS-Proxy nur TCP, und die meisten SOCKS5-Clients implementieren nie UDP ASSOCIATE, also verwirft oder leckt ein proxybasiertes Design UDP, während TCP gesund aussieht.

Diese zweite Grenze ist der Grund, warum ein UDP-basiertes Protokoll in einem normalen Netz funktionieren kann und in dem Moment scheitert, in dem Ihr VPN sich verbindet. Wenn der Tunnel UDP tragen soll, muss der Weiterleiter den Weiterleitungspfad dafür implementieren, nicht nur den TCP-Pfad mit einem anderen Socket-Typ.

Prüfen Sie in dieser Reihenfolge

Arbeiten Sie die Liste der Reihe nach ab und halten Sie beim ersten Schritt an, der scheitert, denn jede spätere Prüfung ist Rauschen, solange die frühere nicht hält.

  1. Listen Sie die installierten Routen auf und bestätigen Sie, ob Nicht-DNS-Pakete überhaupt in die TUN-Schnittstelle gelangen.
  2. Protokollieren Sie Abfragename, Entscheidung und Antwortcode für einen blockierten und einen genehmigten Namen.
  3. Bestätigen Sie, dass der Resolver-Socket geschützt ist und der gewählte Resolver von innerhalb des Tunnels antwortet.
  4. Bestätigen Sie die Schnittstellen-MTU und die Prüfsumme einer von Hand gebauten Antwort.
  5. Wiederholen Sie dieselbe Seite unter Wi-Fi und im Mobilfunknetz, da sich Anbieter-Resolver und Pfad-MTU unterscheiden.

Wenn alle fünf halten und die Seite immer noch hängt, ist der nächste Verdächtige der Weiterleiter selbst: Vergleichen Sie eine aufgezeichnete Anfrage mit einer aufgezeichneten Antwort und suchen Sie nach einer fehlenden Byteanzahl oder einem halb geschlossenen Socket. Die VpnService.Builder-Referenz deckt die Verträge zu Route, DNS und MTU ab, und protect() ist ein separater VpnService-Aufruf und keine Builder-Option; eine verbundene Schnittstelle bedeutet nur, dass der erste Vertrag erfüllt ist.

Ähnliche Artikel