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()-дин жыйынтыгын, маршруттарды, тышкары калтырылган пакеттерди жана өчүрүү себебин жазып алыңыз. Ошол төрт сап пакет кармабастан эле макулдук маселесин камтылыш маселесинен жана жашоо цикли маселесинен ажыратат.

Тектеш макалалар