Четири претпоставки за Android VpnService еднаш ме чинеа поголем дел од неделата, и ниту една не беше за мрежниот протокол. Апликацијата велеше поврзано, иконата со клуч стоеше во статусната лента, а сообраќајот сепак излегуваше по нормалната рута. Постојано претпоставував дека тунелот е мој додека не го сопрам, дека рутите што ги додадам се рутите што ги добивам, и дека повикот на stopSelf ќе го симне интерфејсот со себе. Услугата работеше точно како што е документирано. Расипаниот дел беше мојот ментален модел.

Мит 1: тунелот стои додека не го сопрете

Android држи еден VPN интерфејс истовремено. Поставете втор и платформата не го става во ред, туку го деактивира вашиот и го повикува onRevoke() во вашата услуга. Ако вашиот код расклопува само при притисок на копче, преземањето остава дескриптор и нишка за читање, и следното стартување ги наследува двете.

Референцата за VpnService јасно вели дека prepare() враќа null кога апликацијата веќе има согласност, и Intent кога корисникот сѐ уште треба да ја одобри. Таа согласност пристигнува само ако намерата ја доставите преку startActivityForResult(). Прескокнете го предавањето и establish() враќа null, што се чита како тивок неуспех, а не како проблем со дозволи.

Always-on VPN додава сопственик што не го контролирате. Системот може да го врати тунелот по подигнување или по пад, и го истиснува VPN-апликацијата што корисникот ја стартувал рачно. Изградете onRevoke() така што ќе ги затвора истите ресурси како вашето нормално исклучување, и третирајте го како главен пат за запирање, а не како рабен случај. Корисниците менуваат VPN-апликации; вашиот код мора да го преживее тоа.

Мит 2: addRoute фаќа точно она што го наведовте

Рутите одлучуваат кои пакети стигнуваат до вашиот дескриптор, а грешката во опсегот го создава избезумувачкиот случај кога дел од сообраќајот оди низ тунелот, а дел не. addRoute("0.0.0.0", 0) го зема целиот IPv4, што значи дека секој пакет сега стигнува кај вас и мора да биде препратен или отфрлен. /32 за еден единствен домаќин зема речиси ништо, и сѐ друго се однесува како VPN-от никогаш да не бил инсталиран.

Семејствата се бројат одделно. Рута само за IPv4 го остава IPv6 на основната мрежа, па домаќин со двоен стек сѐ уште може да решава и да се поврзе надвор од тунелот додека внатре изгледа совршено поврзан.

DNS следи истата логика. Серверите додадени со addDnsServer() одат кај апликациите што го користат решавачот на тунелот, додека апликација со своја шифрирана DNS крајна точка или Private DNS поставен на име на домаќин сосема ги игнорира. Кога имињата паѓаат за една апликација и работат за друга, проверете ја патеката на решавачот пред да допрете и една рута.

Мит 3: правилата по апликација се прекинувач што може да се преврти на двете страни

addDisallowedApplication() и addAllowedApplication() се два режима, а не два филтри. Градителот прима еден модел по тунел, па апликација што сака сѐ освен три пакети ја користи забранетата листа и ја остава дозволената листа празна. Двете меѓусебно се исклучуваат, и луѓето го откриваат тоа за време на извршување откако екранот за поставки веќе ги понудил двата прекинувачи.

Видливоста на пакетите исто така гризе. Од Android 11 можете да именувате само пакети што ги гледате, па листата за исклучување изградена од барање што вашата апликација не смее да го изврши тивко ќе пропушти записи. Споредете ја листата со она што менаџерот за пакети навистина го враќа пред да ѝ верувате.

Работните профили повторно ја делат публиката. Тунел стартуван внатре во профилот го покрива тој профил и неговите апликации, додека личната страна ја задржува сопствената поврзаност. Тоа е одлука за опсег донесена еднаш и ѝ е местото во интерфејсот, а не во тикет за поддршка.

Мит 4: stopSelf го завршува тунелот

Не. Интерфејсот живее во дескрипторот што го враќа establish(), и објектот на услугата може да умре додека тој дескриптор останува отворен. Прво затворете го ParcelFileDescriptor, отфрлете ја референцата, придружете се на нишките за читање и пишување или прекинете ги, потоа повикајте stopForeground(true) и stopSelf() по тој редослед.

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

Обратната грешка е исто толку честа: затворање на дескрипторот додека нишка за читање е на средина од читање, а потоа оставање таа нишка да се справи со исклучок за кој никој не напишал ракување. Држете го редоследот на исклучување на едно место и нека и дејството на корисникот и onRevoke() повикуваат во него.

Што да проверите кога тунелот изгледа мртов

Заштитете ги долготрајните сокети со protect() пред тунелот да се подигне. Без тоа, сопствениот транспорт на тунелот се рутира назад во тунелот и врската заглавува на начин што изгледа точно како дефект на серверот. Тестирајте против адреса што постои само зад далечната мрежа, а не против јавна страница што одговара од кешот или од основната патека.

Запишете го резултатот од establish(), рутите, исклучените пакети и причината за исклучувањето. Тие четири реда го делат проблемот со согласноста од проблемот со опсегот и од проблемот со животниот циклус без фаќање пакети.

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