Разделеното тунелирање во апликација со VpnService се крши на неколку конкретни места, а поправката е различна за секое. Клиентот прикажува само една цел што не поминува, па првата работа е да утврдите на која граница удирате: рутите што ги добил builder, проксито што не можете да го автентицирате, resolver-от што само сте го објавиле или листата апликации што платформата ја применува по корисник. Токму затоа држам проверка во четири чекори. Таа оди по редоследот подолу и застанува на првиот неуспех.

Потврдете што builder ѝ предал на платформата

VpnService.Builder е листа со декларации. establish() враќа ParcelFileDescriptor за виртуелен интерфејс, а платформата во тој дескриптор праќа само сообраќај што одговара на префиксите од addAddress и addRoute; addDnsServer им кажува на клиентите кој resolver да го прашаат. Цел надвор од сите префикси што сте ги додале никогаш не стигнува до вашата апликација, што и да вети интерфејсот. Овој единствен факт ги објаснува повеќето пријави за расипано разделено тунелирање уште пред промена на код.

Запишете ги во лог листите со адреси, рути и DNS што ги составил builder, па споредете ги со целите што не поминуваат. Недостасувачки /32 за управувачки хост е многу почест од расипан тунел. Ако листата е точна, продолжете; ако не е, остатокот од контролната листа е залуден труд.

excludeRoute постои само од API 33

Исклучувањето по цел е барањето што најчесто завршува како тема за поддршка. builder.excludeRoute(IpPrefix) пристигна во API 33; пред тоа единствената поддржана приближност е да додадете рути за сè освен исклучениот префикс, што значи сами да ги пресметате комплементарните префикси. Двата повика не се огледални слики: едниот одзема од множеството рути на интерфејсот, другиот го гради множеството од нула, а постар уред ќе фрли или тивко ќе игнорира повик што не го познава. Разгранете се по Build.VERSION.SDK_INT и тестирајте ги двата пата на вистински хардвер.

Кога пресметувате комплементи, внимавајте на порамнувањето на префиксите. Исклучување /24 во рута /16 бара покривни префикси што навистина го опишуваат остатокот, а не еден деформиран /8. Запишете ја конечната листа и проверете дека исклучениот хост сè уште се разрешува по пат надвор од тунелот.

Прокси акредитивите не можат да се изразат

setHttpProxy(ProxyInfo) го насочува интерфејсот кон HTTP или SOCKS прокси, а PAC URL може да избира меѓу нив по цел. Ниту ProxyInfo ниту форматот PAC не носат корисничко име и лозинка, па проксито што бара автентикација останува надвор од API-то на платформата. Апликациите што го потребуваат обично вградуваат кориснички стек, tun2socks или сличен мотор и сами одговараат на предизвикот 407.

Овој избор има последици. Корисничкиот стек гледа само сообраќај што му го насочувате, па мора да живее во истите граници на дескрипторот и го наследува истото барање за protect() за сопствениот uplink сокет. Испуштете protect() и тунелот го фаќа сопствениот транспорт, што изгледа како ракување што застанува по првиот пакет. Тестирајте против прокси што бара акредитиви, зашто отвореното прокси поминува по патот на платформата и не докажува ништо.

Листите по апликација и DNS се исто така декларации

Builder има и addAllowedApplication и addDisallowedApplication, а тие меѓусебно се исклучуваат: повикување на двете фрла исклучок. Како и да е, листата се применува по корисник при поставување на интерфејсот, па апликацијата од работниот профил се управува од тунелот на работниот профил, а не од вашиот. Она што не го дава ниту едно API е контрола врз одлуката на друга апликација: не можете однадвор да турнете пакет во вашиот тунел и не можете да затворите или преконфигурирате веќе активен VPN.

DNS го следи истиот образец. addDnsServer објавува адреса на resolver; тоа не ѝ дозволува на вашата апликација да одговара на прашања. Ако сакате сами да разрешувате имиња, стартувајте resolver и насочете ја неговата адреса во тунелот. Апликациите што користат DoT или DoH кон фиксно име на хост можат да ја заобиколат објавената адреса, па разрешувањето имиња третирајте го како најдобар обид, освен ако не го пресретнувате и патот на прашањето.

Манифест, always-on и тестот во четири чекори

Подобноста се декларира во манифестот: BIND_VPN_SERVICE, intent филтерот android.net.VpnService и foreground услуга ако тунелот мора да преживее во позадина. Always-on VPN дополнително бара чист onRevoke, бидејќи системот го повикува кога корисникот менува поставки или друга апликација ја презема контролата.

Извршете ги проверките по ред и застанете на првиот неуспех. Прво, испечатете ги листите со адреси, рути и DNS дадени на builder. Второ, потврдете дека establish() вратил дескриптор и дека транспортниот сокет е заштитен. Трето, побарајте една цел во вклучен префикс и една во исклучен префикс. Четврто, проверете кој resolver навистина одговорил со прашање за име на хост што само вашиот resolver може да го разреши.

Завршеното ракување не докажува ниту еден од следните чекори. Секоја проверка изолира еден слој, па успехот на вториот чекор не кажува ништо за четвртиот.

Референцата за VpnService.Builder ја набројува целата површина на builder. Сè што ви треба надвор од неа е друго API, друга апликација или вашиот сопствен кориснички стек, а тоа е дизајнерска одлука, не дефект во разделеното тунелирање.

Поврзани статии