Přidali jste doménu na povolený seznam, dotaz dorazí do vaší aplikace, porovnávač vrátí povoleno, a stránka se přesto nikdy nenačte. V tu chvíli většina lidí začne přepisovat porovnávač a přidávat porovnávání se zástupnými znaky. Podle mé zkušenosti bývá porovnávač obvykle správný a porucha leží o jednu vrstvu níž: v odpovědi DNS, kterou jste syntetizovali, v trase, kterou jste nainstalovali, nebo v socketu, který jste použili k předání dotazu. Seznam je ta snadná část; cesta paketu je místo, kde se povolené seznamy lámou.

Porovnávač je zřídka viníkem

První, co je třeba oddělit, je blokováno od rozbitého. Blokované jméno má selhat rychle a viditelně, protože prohlížeč dostane odpověď, že jméno neexistuje, a přestane. Rozbité jméno visí, zkouší to znovu přes IPv6, přepne na jiný transport a nechá vás s točícím se indikátorem. Pokud vidíte točící se indikátor, váš povolený seznam pravděpodobně odpovídá něčím, s čím klient nemůže nic dělat.

Prázdný záznam A je klasická chyba. Vrácení NOERROR s nulou odpovědí vypadá jako platná odpověď, takže stub resolver ji přijme, prohlížeč to zkusí znovu, zkusí AAAA, zkusí jiný resolver a mnohem později to vzdá. Kód odpovědi 3, NXDOMAIN, s opakovanou sekcí dotazu a zkopírovaným ID transakce ukončí konverzaci okamžitě. Použijte ho pro každé jméno, které záměrně odmítáte.

IPv6 si zaslouží stejné rozhodnutí, ne výchozí hodnotu. Pokud na A odpovíte NXDOMAIN a AAAA pustíte, klient se může k blokovanému jménu dostat přes IPv6 a filtr prosakuje. Některé implementace odpovídají na blokované dotazy AAAA adresou loopback, aby spojení hned selhalo, místo aby přešlo na náhradní cestu. Zvolte jednu politiku, použijte ji na oba typy záznamů a oba logujte.

Trasa, která zachytí vše, vlastní vše

Návrh pouze s DNS nikdy nepřidá addRoute("0.0.0.0", 0), takže do rozhraní TUN vstupují jen dotazy a vše ostatní dál používá podkladovou síť. To je bezpečný tvar a také to vysvětluje, proč některé povolené seznamy fungují na Wi-Fi a selhávají v mobilní síti.

Ve chvíli, kdy existuje výchozí trasa, je každý tok váš. Paket TCP, který dorazí na rozhraní a není ani předán, ani zodpovězen, sedí ve frontě, dokud klientovi nevyprší čas, a není žádná chyba k zalogování, protože nic nechybovalo. Viděl jsem někoho strávit odpoledne na rozbitém povoleném seznamu, který byl ve skutečnosti chybějícím předačem pro port 443. Logujte protokol a cíl každého paketu, který se rozhodnete zahodit.

Socket resolveru musí opustit tunel

Předávání je místo další pasti. Resolvery operátora často žijí na privátní adrese, kam se tunel nedostane, takže dotaz, který předáte, jde nikam a aplikace hlásí vypršení času čtení. Posílejte schválené dotazy resolveru, o kterém víte, že je dostupný, a ten socket nejdřív předejte do VpnService.protect(). Nechráněný socket pošle dotaz zpět do vašeho vlastního tunelu, kde znovu dorazí do aplikace a krouží, dokud nevyprší časový limit.

Porucha vypadá stejně jako výpadek sítě, a proto v hlášeních chyb přežívá tak dlouho. Veďte počítadla odeslaných dotazů a přijatých odpovědí: pokud první číslo roste a druhé zůstává na nule, díváte se na socket, ne na seznam.

Velikost a kontrolní součty selhávají bez zprávy

Tiché zahazování přichází i z velikosti. Odpověď DNS s velkou sadou odpovědí nebo TCP handshake se štědrými volbami může překročit MTU cesty, kterou jste nikdy explicitně nenastavili; fragment zmizí výš po cestě a klient buď zkusí menší, nebo přestane zkoušet. Nastavení MTU rozhraní na 1280 je konzervativní volba, která přežije většinu mobilních cest, a být explicitní je lepší než dědit výchozí hodnotu, kterou nikdo neměřil.

Pokud si pakety stavíte sami, kontrolní součet je další tichý zabiják. UDP kontrolní součet spočítaný přes pseudo-záhlaví, které vynechává délku UDP nebo pole protokolu, vytvoří paket, který na vaší straně vypadá správně, a příjemce ho bez stopy zahodí. Porovnejte své pseudo-záhlaví se specifikací pole po poli a při ladění vypište spočítaný kontrolní součet pro jeden známý tok.

Co za vás VpnService neudělá

Dva limity stojí za to znát, než kolem nich začnete navrhovat. Žádná běžná aplikace nemůže nainstalovat systémovou proxy ani soubor PAC mimo Wi-Fi a ta část se nezměnila. Cesta pro jednotlivé aplikace ano: před API 29 nemohl VpnService.Builder nést proxy vůbec a od API 29 předává setHttpProxy(ProxyInfo.buildPacProxy(uri)) platformě skript PAC k vyhodnocení pro síť VPN. Vyhodnocujte pravidla ve vlastním kódu, když cílíte na starší API nebo když model PAC pravidlo neumí vyjádřit. UDP také není zdarma: prohlížeče posílají přes SOCKS proxy jen TCP a většina klientů SOCKS5 nikdy neimplementuje UDP ASSOCIATE, takže návrh založený na proxy UDP zahazuje nebo propouští, zatímco TCP vypadá zdravě.

Tento druhý limit je důvod, proč protokol založený na UDP může fungovat v běžné síti a selhat ve chvíli, kdy se vaše VPN připojí. Pokud má tunel nést UDP, musí předač implementovat předávací cestu i pro něj, ne jen cestu TCP s jiným typem socketu.

Kontrolujte v tomto pořadí

Projděte seznam popořadě a zastavte se u prvního kroku, který selže, protože každá další kontrola je šum, dokud předchozí nedrží.

  1. Vypište nainstalované trasy a potvrďte, zda pakety, které nejsou DNS, vůbec vstupují do rozhraní TUN.
  2. Zalogujte jméno dotazu, rozhodnutí a kód odpovědi pro jedno blokované a jedno schválené jméno.
  3. Potvrďte, že socket resolveru je chráněný a že zvolený resolver odpovídá zevnitř tunelu.
  4. Potvrďte MTU rozhraní a kontrolní součet jedné odpovědi, kterou jste sestavili ručně.
  5. Zopakujte stejnou stránku na Wi-Fi a v mobilní síti, protože resolvery operátora a MTU cesty se liší.

Pokud všech pět drží a stránka se stále zasekává, dalším podezřelým je sám předač: porovnejte zachycený požadavek se zachycenou odpovědí a hledejte chybějící počet bajtů nebo polouzavřený socket. Referenční příručka VpnService.Builder pokrývá smlouvy o trase, DNS a MTU a protect() je samostatné volání VpnService, ne volba builderu; připojené rozhraní znamená jen to, že první smlouva je splněna.

Související články