Списък с разрешени в Android VpnService: защо разрешените страници все още висят
Списъкът ви с разрешени е вероятно наред. Страниците висят заради отговора от DNS, таблицата с маршрути, сокета на резолвера или MTU. Ето реда за проверка.
Добавихте домейн в списъка с разрешени, заявката стига до приложението ви, съпоставителят връща разрешено, а страницата така и не се зарежда. В този момент повечето хора започват да преписват съпоставителя и да добавят сравнения с шаблони. По моя опит съпоставителят обикновено е правилен, а повредата е един слой по-надолу: в отговора от DNS, който сте синтезирали, в маршрута, който сте инсталирали, или в сокета, който сте използвали, за да препратите заявката. Списъкът е лесната част; пътят на пакета е мястото, където списъците с разрешени се чупят.
Съпоставителят рядко е причината
Първото нещо, което трябва да разделите, е блокирано от счупено. Блокираното име трябва да се провали бързо и видимо, защото браузърът получава отговор, че името не съществува, и спира. Счупеното име виси, повтаря опити през IPv6, преминава към друг транспорт и ви оставя с въртящ се индикатор. Ако виждате въртящ се индикатор, списъкът ви с разрешени най-вероятно отговаря с нещо, с което клиентът не може да работи.
Празният запис A е класическата грешка. Връщането на NOERROR с нула отговора изглежда като валиден отговор, затова stub резолверът го приема, браузърът опитва отново, опитва AAAA, опитва друг резолвер и се отказва много по-късно. Код на отговора 3, NXDOMAIN, с повторена секция с въпроса и копиран идентификатор на транзакцията приключва разговора веднага. Използвайте го за всяко име, което умишлено отказвате.
IPv6 заслужава същото решение, а не стойност по подразбиране. Ако отговаряте с NXDOMAIN за A и пропускате AAAA, клиентът може да стигне до блокираното име през IPv6 и филтърът изтича. Някои реализации отговарят на блокирани заявки AAAA с адрес loopback, така че връзката се проваля веднага, вместо да преминава към резервен вариант. Изберете една политика, приложете я към двата типа записи и записвайте в журнала и двата.
Маршрут, който улавя всичко, притежава всичко
Схема само с DNS никога не добавя addRoute("0.0.0.0", 0), така че в интерфейса TUN влизат само заявки, а всичко останало продължава да използва подлежащата мрежа. Това е безопасната форма и обяснява защо някои списъци с разрешени изглеждат работещи на Wi-Fi и се провалят в мобилната мрежа.
В момента, в който съществува маршрут по подразбиране, всеки поток е ваш. TCP пакет, който пристига на интерфейса и не се препраща, нито се обслужва с отговор, стои в опашката, докато на клиента не изтече времето, и няма грешка за запис, защото нищо не е дало грешка. Гледал съм човек да прекарва следобед върху счупен списък с разрешени, който всъщност бил липсващ препращач за порт 443. Записвайте протокола и дестинацията на всеки пакет, който решите да отхвърлите.
Сокетът на резолвера трябва да излезе от тунела
Препращането е мястото на следващия капан. Резолверите на оператора често живеят на частен адрес, до който тунелът не достига, затова заявката, която препращате, отива наникъде и приложението отчита изтичане на времето за четене. Изпращайте одобрените заявки към резолвер, за който знаете, че е достъпен, и първо подайте този сокет на VpnService.protect(). Незащитен сокет връща заявката обратно в собствения ви тунел, където тя отново стига до приложението и се върти, докато не задейства таймаутът.
Провалът изглежда идентично на прекъсване на мрежата и точно затова оцелява толкова дълго в докладите за грешки. Поддържайте броячи за изпратени заявки и получени отговори: ако първото число расте, а второто остава нула, гледате сокета, не списъка.
Размерът и контролните суми се провалят без съобщение
Тихите отхвърляния идват и от размера. Отговор от DNS с голям набор от отговори или TCP ръкостискане с щедри опции може да надхвърли MTU на пътя, който никога не сте задали явно; фрагментът изчезва по-нагоре и клиентът или повтаря опита с по-малък размер, или спира да опитва. Задаването на MTU на интерфейса на 1280 е консервативен избор, който издържа на повечето мобилни пътища, и явното задаване е по-добро от наследяване на стойност по подразбиране, която никой не е измервал.
Ако изграждате пакетите сами, контролната сума е другият тих убиец. UDP контролна сума, изчислена върху псевдозаглавка, която пропуска дължината на UDP или полето за протокол, дава пакет, който изглежда добре оформен от ваша страна и се отхвърля от получателя без следа. Сравнете псевдозаглавката си със спецификацията поле по поле и отпечатайте изчислената контролна сума за един известен поток, докато отстранявате проблема.
Какво VpnService няма да направи вместо вас
Две ограничения си струва да знаят, преди да проектирате около тях. Никое обикновено приложение не може да инсталира системен прокси или PAC файл извън Wi-Fi и тази част не се е променила. Пътят за отделни приложения се промени: преди API 29 VpnService.Builder изобщо не можеше да носи прокси, а от API 29 setHttpProxy(ProxyInfo.buildPacProxy(uri)) дава на платформата PAC скрипт за оценка за VPN мрежата. Оценявайте правилата в собствения си код, когато се насочвате към по-стари API или когато PAC моделът не може да изрази правилото. UDP също не е безплатен: браузърите изпращат само TCP през SOCKS прокси и повечето клиенти SOCKS5 никога не реализират UDP ASSOCIATE, така че схема на базата на прокси отхвърля или изтича UDP, докато TCP изглежда здрав.
Това второ ограничение е причината протокол на базата на UDP да работи в обикновена мрежа и да се провали в момента, в който вашият VPN се свърже. Ако тунелът трябва да носи UDP, препращачът трябва да реализира пътя за препращане за него, а не само пътя за TCP с различен тип сокет.
Проверявайте в този ред
Минете списъка по ред и спрете на първия етап, който се провали, защото всяка следваща проверка е шум, докато предишната не издържи.
- Изброете инсталираните маршрути и потвърдете дали пакети, различни от DNS, изобщо влизат в интерфейса TUN.
- Запишете в журнала името на заявката, решението и кода на отговора за едно блокирано и едно разрешено име.
- Потвърдете, че сокетът на резолвера е защитен и че избраният резолвер отговаря отвътре в тунела.
- Потвърдете MTU на интерфейса и контролната сума на един отговор, изграден на ръка.
- Повторете същата страница на Wi-Fi и в мобилната мрежа, защото резолверите на оператора и MTU на пътя се различават.
Ако и петте издържат, а страницата все още виси, следващият заподозрян е самият препращач: сравнете прихваната заявка с прихванат отговор и търсете липсващ брой байтове или наполовина затворен сокет. Справочникът за VpnService.Builder покрива договорите за маршрут, DNS и MTU, а protect() е отделно извикване на VpnService, а не опция на builder-а; свързан интерфейс означава само, че първият договор е изпълнен.