Android-те VPN анықтау: неге tun0 мен ping шатастырады
Интерфейс атауы — шарттылық; ping бір хосттың қолжетімді болғанын дәлелдейді. NetworkCapabilities-тен желінің нені мәлімдейтінін сұрап, кейін кері шақыруларға әрекет етіңіз.
Android қолданбаларының көбі VPN анықтау үшін қолданатын тексеру — бұл интерфейс атауы, және ол ешқашан VPN тексеруі болған емес. Ол әзірлеуші күткен жолмен қандай да бір интерфейс бар екенін хабарлайды, бұл бір құрылғыда OpenVPN дегенді, екіншісінде мүлдем байланысы жоқ нәрсені білдіреді. ping — дәл сондай дәлел: ол бір мақсаттың жауап бергенін дәлелдейді, пакетті туннель тасымалдағанын емес. Платформа шын мәнінде ұсынатыны — желілер жиынтығы және олардың әрқайсысы мәлімдейтін транспорттар, ал бұл басқа жауабы бар басқа сұрақ.
Аңыз: tun0 болса, VPN қосулы
Android VPN қолданбалары — жүйеге tun файл дескрипторын беретін VpnService іске асырулары. Интерфейс атауы оны жасаған жақтан келеді: OpenVPN әдетте tun0 жасайды, PPTP мен L2TP ppp0 жасайды, WireGuard интерфейсін туннель атымен атайды, ал IPsec туннелінің мүлде дәстүрлі аты болмауы мүмкін. Сондықтан /proc/net/dev оқу немесе NetworkInterface нысандарын санау — шарттылықты іздейді, фактіні емес.
Қатенің екі бағыты да тірі. Құрылғыда пайдаланушының VPN-іне қатысы жоқ жергілікті туннель үшін tun0 болуы мүмкін, ал мүлде сау WireGuard туннелі тек tun0 жолын білетін тексеру үшін көрінбейтін болуы мүмкін. Бір функциядағы жалған оң және жалған теріс нәтижелер оны диагностика ретінде жарамсыз етеді, өйткені «VPN табылмады» дейтін қолдау билетіне ешбір бағытта сенуге болмайды.
Қолжетімділік те тарылды. Жаңа Android шығарылымдары қарапайым қолданбаның /proc-тан нені оқи алатынын шектейді, сондықтан Android 8-де жұмыс істеген атауға негізделген тексеру кодты өзгертпей-ақ кейінгі құрылғыда бос тізім қайтаруы мүмкін. Тексеру үнсіз ашық жаққа сәтсіз болса, қолданба туннель дұрыс жұмыс істейтін аппаратта VPN жоқ деген қорытындыға келеді.
Аңыз: сәтті ping туннель трафикті тасымалдайтынын білдіреді
Ішкі хостқа ping сол хостқа маршрут болған кезде сәтті болады. Бөлінген туннельде хост Wi-Fi арқылы қолжетімді болуы мүмкін, ал туннель қосулы және мүлдем басқа нәрсе істеп жатыр. Әр қолданбаға арналған VPN-де ping жасайтын қолданба туннельден шығарылуы мүмкін, сондықтан ICMP жаңғырығы одан тыс шығып, қайтып келеді, бұл тексеру болжайтынның керісін дәлелдейді.
ICMP пен TCP те бөлек сүзіледі. Жаңғырық сұрауларына рұқсат беріп, бірақ қызметке қажет TCP портын тастайтын туннель ping сынағынан өтіп, қолданбада сәтсіз болады, ал бұл құрылғының қасиеті емес, қарсы жақтағы брандмауэр ережесі. Сәтті ping-тің жалғыз адал оқылымы — бұл мақсат осы сәтте осы протоколға жауап берді.
Платформа шын мәнінде нені хабарлайды
API 21-ден бастап ConnectivityManager.getAllNetworks() жүйе білетін әр желіні қайтарады, ал getNetworkCapabilities(network) сол желінің нені мәлімдейтінін хабарлайды. VPN желісі TRANSPORT_VPN тасиды. Барлық желілерді айналып өту бір сұраққа жауап береді: осы құрылғыда қазір қандай да бір VPN қосулы ма?
Екінші, тар сұрақ басқа шақыруды талап етеді. getActiveNetwork() қолданба трафигі қазір қолданатын желіні қайтарады, ал оған сол мүмкіндікті тексеру мынаған жауап береді: менің трафигім VPN арқылы өте ме? Екеуі сәйкес келмеуі мүмкін, және бұл сәйкессіздік ақпаратты. Қосулы, бірақ ағымдағы емес VPN қолданба бойынша маршруттауға немесе бөлінген туннель конфигурациясына нұсқайды, жоғалған туннельге емес.
Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Екі шақыруды да қорғаңыз. Мүмкіндіктер тізімдеу мен сұрау арасында жоғалған желі үшін null болуы мүмкін, ал белсенді желі тапсыру кезінде null болуы мүмкін. NET_CAPABILITY_NOT_VPN қарсы бағытта пайдалы — желінің VPN транспорты жалауының жоқтығынан емес, оның нақты туннель еместігін растау үшін.
NetworkInfo API-мен бірдей аңыз болды
NetworkInfo-мен ConnectivityManager.TYPE_VPN — бұл тексерудің екінші буыны, және ол себепсіз ескірген жоқ. Заманауи шығарылымдарда туннель қосылған кезде VPN үшін қайтарылған нысан null болуы мүмкін, сондықтан null-ды «VPN жоқ» деп қарайтын код жауап маңыздырақ құрылғыларда үнсіз жалған теріс нәтиже хабарлайды.
Онымен бірге жүретін хабарлама да жақсырақ емес. CONNECTIVITY_CHANGE-тің VPN ауысуларын өткізіп жіберуінің құжатталған тарихы бар, оның ішінде әр желілік өзгерісте емес, тек VPN қосылғанда немесе ажыратылғанда іске қосылатын құрастырулар да бар. Оған құрылған тыңдаушы келесі толық қайта қосылуға дейін платформа көзқарасынан алыстайтын күй айнымалысын жасайды.
Сауалнаманың орнына кері шақыруларға әрекет етіңіз
TRANSPORT_VPN талап ететін және NET_CAPABILITY_NOT_VPN-ді жоятын NetworkRequest тіркеңіз, содан кейін сол сұраныспен registerNetworkCallback шақырыңыз. Сонда onAvailable мен onLost мәлімдеменің қашан өзгеретінін айтады, және бұл сізді шынымен қызықтыратын оқиға. Компонент тоқтағанда кері шақырудың тіркелуін болдырмаңыз, әйтпесе қолданба өлі экранға сілтеме ұстап, кері шақыру жалғаса береді.
NetworkRequest req = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(req, callback);
Бұл ACCESS_NETWORK_STATE қажет етеді, ол қалыпты рұқсат: root жоқ, пайдаланушының келісім диалогы жоқ, VPN рұқсаты жоқ. Таймермен сауалнама — адамдар жүгінетін балама, ол батареяны жұмсайды және интервалдан қысқа ауысуды өткізіп жібереді, ал бұл пайдаланушы хабарлайтын дәл сол ауысу.
Жауап әлі де айта алмайтын нәрсе
Бұл API-лердің ешқайсысы туннельдің сау екенін, қай серверде аяқталатынын немесе белгілі бір қолданбаның трафигі оның ішінде екенін хабарламайды. VPN желісі пайдалы маршруттарсыз болуы мүмкін, ал әр қолданбаға арналған туннель сұрақ қойған қолданбаны шығарып тастауы мүмкін.
Сондықтан тексеру көргенін жазып алыңыз, тек бульдік мәнді емес. Желі идентификаторын, мәлімделген транспорттарды және сол желінің белсенді болғанын журналға жазыңыз. «VPN табылмады» дейтін есеп жарамсыз, ал желі TRANSPORT_VPN мәлімдеді, бірақ getActiveNetwork() Wi-Fi қайтарды дейтін есеп тікелей маршруттауға немесе алып тастау тізіміне нұсқайды, және бұл біреу жұмыс істей алатын түзету.