Туннельдің өз пакеттерін туннельден тыс ұстаудың екі жолы бар: тасымалдау сокетін қорғау немесе бүкіл қолданбаны VPN-нен шығару. Олар әртүрлі мәселелерді шешеді, ал қосу оңай болғандықтан кеңірек нұсқаны таңдау — қолданбалардың байқамай жергілікті желіге қатынау мен бөлінген DNS-тен айырылу жолы. Туннельден тыс қалуы керек нәрседен бастаңыз, содан кейін соған жететін ең кішкентай ауқымды таңдаңыз.

protect() туннельден нені алып тастайды

VpnService.protect() әр сокет үшін шақыру. Сіз оған туннельді тасымалдайтын сокетті немесе дескрипторды бересіз, ал Android сол сокетті белгілейді, сонда оның трафигі VPN интерфейсінің орнына астындағы желіні пайдаланады. Қолданба жіберетін қалғанның барлығы әдеттегі VPN маршрутын ұстайды.

Дәл осы тар ауқым — мәселенің мәні. Қорғалған сокет HTTP клиентіңіздің атауларды қалай шешетінін, аналитикалық шақыруларыңыздың қай интерфейсті пайдаланатынын немесе кейінірек ашылған екінші қосылымға не болатынын өзгертпейді.

Ауқымды анықтайтын үш шарт

Бірінші шарт — тек туннель тасымалы ғана шығуы керек пе. Егер қолданбаның қалған трафигі әлі де VPN арқылы өтуі керек болса, сокетті қорғау дұрыс ауқым. Екінші шарт — сокет жасауды өзіңіз басқарасыз ба. Ішінде тереңде өз сокетін ашатын кітапхана сізге protect() шақыратын орын қалдырмайды, егер ол ілгек ұсынбаса немесе дайын сокетті қабылдамаса. Үшінші шарт — қолданба жергілікті желіге немесе captive portal-ға тікелей жетуі керек пе, бұл туннельді тірі ұстаудан басқа талап.

Қолданбаны addDisallowedApplication() арқылы шығару — дөрекі құрал. Ол қолданба жасайтын әрбір қосылымды VPN-нен алып тастайды, соның ішінде тексергіңіз келгендерді де, ал кейбір құрылғыларда жергілікті анықтау тәртібін де өзгертеді.

Үшінші нұсқа — тасымалды таңдалған желіге байлау:bindProcessToNetwork() немесе сокет деңгейіндегі желілік тұтқа арқылы. Бұл құрылғыда бір уақытта Wi-Fi мен ұялы желі болғанда және туннельдің белгілі бір жоғары арнамен жүруін қалағанда пайдалы.

Оны тасымал жасалып жатқанда шақырыңыз

Шақырыңыз protect() тасымалды жасайтын сол блокта,connect() немесе send() дейін. Егер шақыру false қайтарса, сокетті жауып, орнатуды сәтсіз етіңіз; жалғастыру сізді пакеттері өз интерфейсіңізге қайта кіруі мүмкін тасымалмен қалдырады.

Пішін бір жерде ұстауға жеткілікті кішкентай:

DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
    transport.close();
    throw new IOException("transport not protected");
}
transport.connect(serverAddress);

Мұнда үш бөлшек тістеп алады. Біріншіден,boolean — алатын жалғыз сигналыңыз, сондықтан сәтсіздікті қайтарып алынған келісімді қабылданбаған шақырудан ажырататын жеткілікті контекстімен жазып алыңыз. Екіншіден,protect() сокетті қамтиды, дескриптор нөмірін емес: егер сокетті жауып, кейін сол бүтін санды басқа сокет үшін қайта қолдансаңыз, жаңа сокет қорғалмаған. Үшіншіден, native кітапхана тасымалды ашқанда, native код қосылмай тұрып дескрипторды қорғаңыз, әйтпесе маршрут әлдеқашан таңдалған болуы мүмкін.VpnService анықтамасы нысаналы API деңгейіңіз шынымен қандай қайта жүктеулерді ұсынатынын растайтын орын.

Тек бақытты жолды емес, бөгелген жолды да сынаңыз

Туннель көтерілген күйде тексеріңіз, өйткені екі жол тек сонда ғана ерекшеленуі мүмкін. Қорғалған тасымал арқылы бір сұраныс және protect() ешқашан шақырмаған код жолынан тағы бір сұраныс жіберіп, алыс жақтың не көретінін салыстырыңыз: қорғалған қосылым астындағы желімен дереккөз ретінде келуі керек, ал қорғалмағаны туннельдің өз мекенжайын көрсетіп, оның байт санағыштарын қозғайды. Құрылғыда ss немесе netstat радиодан бірдеңе шықпай тұрып, сол бөлінуді көрсетеді.

Қайтарып алуды бөлек сынаңыз, өйткені келісімнің өзгеруі сокет кодыңызды ұстамай-ақ интерфейсті құлатады. VPN келісімін қайтарып алыңыз немесе қызметті тоқтатыңыз, тасымалды қайта жасаңыз және protect() әлі де true қайтаратынын және келісім қайта берілгенде тасымалдың қайта қосылатынын растаңыз.

Егер қорғалған сұраныс бәрібір туннельді ұстанса, қорғау шақыруы сокет әлдеқашан маршрутталғаннан кейін болып жатқан шығар. Оны ертерекке жылжытып, қайта сынаңыз.

Шешімді тасымал конфигурациясының қасында жазып ұстаңыз: қай сокеттер қорғалған, қай қолданба трафигі шығарылған және қорғау сәтсіз болғанда қолданба не істейді. Дәл осы жол кейінгі рефактордың бір тест құлағаны үшін шығаруды бүкіл қолданбаға кеңейтуіне жол бермейді. Ол келесі адамға манифестті кері инженерияламай-ақ ниетті тексеру тәсілін де береді.

Қатысты мақалалар