Android-də VPN aşkarlamaq: tun0 və ping niyə yanıltır
İnterfeys adı sadəcə bir konvensiyadır; ping yalnız bir hostun əlçatan olduğunu sübut edir. Şəbəkənin nə bəyan etdiyini NetworkCapabilities-dən soruşun, sonra geri çağırışlara reaksiya verin.
Android tətbiqlərinin VPN aşkarlamaq üçün istifadə etdiyi yoxlama interfeys adıdır və bu heç vaxt VPN yoxlaması olmayıb. O, tərtibatçının gözlədiyi sətirlə hansısa interfeysin mövcud olduğunu bildirir; bu bir cihazda OpenVPN deməkdir, digərində isə tamamilə əlaqəsiz bir şey. Ping də eyni cür sübutdur: o, bir təyinatın cavab verdiyini sübut edir, paketi tunelin daşıdığını yox. Platformanın həqiqətən təqdim etdiyi şey şəbəkələr çoxluğu və hər birinin bəyan etdiyi transportlardır; bu, fərqli cavabı olan fərqli bir sualdır.
Mif: tun0 varsa, VPN işləyir
Android VPN tətbiqləri sistemə tun fayl deskriptoru verən VpnService tətbiqləridir. İnterfeys adı onu yaradan şəxsdən gəlir: OpenVPN adətən tun0 yaradır, PPTP və L2TP ppp0 yaradır, WireGuard interfeysini tunelin adı ilə adlandırır, IPsec tuneli isə ümumiyyətlə adi ad təqdim etməyə bilər. Buna görə /proc/net/dev oxumaq və ya NetworkInterface obyektlərini sadalamaq faktı deyil, konvensiyanı axtarır.
Hər iki səhv istiqaməti canlıdır. Cihazda istifadəçinin VPN-i ilə heç bir əlaqəsi olmayan yerli tunel üçün tun0 ola bilər və tamamilə sağlam WireGuard tuneli yalnız tun0 sətrini tanıyan yoxlama üçün görünməz qala bilər. Eyni funksiyada yalan müsbət və yalan mənfi nəticələr onu diaqnostika kimi yararsız edir, çünki ‘VPN aşkarlanmadı’ deyən dəstək bileti heç bir istiqamətdə etibarlı ola bilməz.
Giriş də daralıb. Daha yeni Android buraxılışları adi tətbiqin /proc-dan nə oxuya biləcəyini məhdudlaşdırır, buna görə Android 8-də işləyən ada əsaslanan yoxlama kod dəyişikliyi olmadan sonrakı cihazda boş siyahı qaytara bilər. Yoxlama səssizcə açıq şəkildə uğursuz olduqda, tətbiq tunelin yaxşı işlədiyi avadanlıqda VPN olmadığı qənaətinə gəlir.
Mif: uğurlu ping tunelin trafik daşıdığını bildirir
Daxili hosta ping o hosta marşrut olduqda uğur qazanır. Bölünmüş tunel ilə host Wi-Fi üzərindən əlçatan ola bilər, halbuki tunel işləyir və tamamilə başqa bir şey edir. Hər tətbiq üçün VPN ilə ping edən tətbiq tuneldən kənarda qala bilər, buna görə ICMP əks-sədası ondan kənarda çıxıb geri qayıdır və bu, yoxlamanın fərz etdiyinin əksini sübut edir.
ICMP və TCP də ayrıca filtrlənir. Əks-səda sorğularına icazə verən, lakin xidmətin ehtiyac duyduğu TCP portunu atan tunel ping testindən keçəcək və tətbiqdə uğursuz olacaq; bu, cihazın xassəsi deyil, qarşı tərəfdəki firewall qaydasıdır. Uğurlu pingin yeganə dürüst oxunuşu budur: bu təyinat bu protokola bu anda cavab verdi.
Platforma həqiqətən nə bildirir
API 21-dən etibarən ConnectivityManager.getAllNetworks() sistemin bildiyi hər şəbəkəni qaytarır və getNetworkCapabilities(network) həmin şəbəkənin nə bəyan etdiyini bildirir. VPN şəbəkəsi TRANSPORT_VPN daşıyır. Bütün şəbəkələr üzərində dövrə vurmaq bir suala cavab verir: bu cihazda hazırda hər hansı VPN işləyirmi?
İkinci, daha dar sual fərqli bir çağırış tələb edir. getActiveNetwork() tətbiqin trafikinin hazırda istifadə edəcəyi şəbəkəni qaytarır və eyni qabiliyyəti onun üzərində yoxlamaq cavab verir: trafikim VPN-dən keçəcəkmi? İkisi bir-biri ilə ziddiyyət təşkil edə bilər və bu ziddiyyət məlumatvericidir. İşləyən, lakin aktiv olmayan VPN hər tətbiq üçün marşrutlaşdırmaya və ya bölünmüş tunel konfiqurasiyasına işarə edir, itkin tunelə yox.
Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Hər iki çağırışı qoruyun. Siyahıya salma ilə sorğu arasında yoxa çıxmış şəbəkə üçün qabiliyyətlər null ola bilər və ötürmə zamanı aktiv şəbəkə null ola bilər. NET_CAPABILITY_NOT_VPN əks istiqamətdə faydalıdır: şəbəkənin sadəcə VPN transport bayrağından məhrum olmasını deyil, mütləq tunel olmadığını təsdiqləmək üçün.
NetworkInfo API ilə eyni mif idi
NetworkInfo ilə ConnectivityManager.TYPE_VPN bu yoxlamanın ikinci nəslidir və bir səbəbdən köhnəlmişdir. Müasir buraxılışlarda VPN üçün qaytarılan obyekt tunel qoşulmuş halda null ola bilər, buna görə null-u ‘VPN yoxdur’ kimi qəbul edən kod cavabın ən çox vacib olduğu cihazlarda səssizcə yalan mənfi nəticə bildirir.
Adətən onu müşayiət edən yayım da daha yaxşı deyil. CONNECTIVITY_CHANGE-in hər şəbəkə dəyişikliyində deyil, yalnız VPN qoşulduqda və ya ayrıldıqda işə düşdüyü buraxılışlar da daxil olmaqla, VPN keçidlərini buraxmaqla bağlı sənədləşdirilmiş tarixçəsi var. Onun üzərində qurulmuş dinləyici, növbəti tam yenidən qoşulmaya qədər platformanın görünüşündən uzaqlaşan bir vəziyyət dəyişəni yaradır.
Sorğu göndərmək yerinə geri çağırışlara reaksiya verin
TRANSPORT_VPN tələb edən və NET_CAPABILITY_NOT_VPN-i çıxaran bir NetworkRequest qeydiyyatdan keçirin, sonra həmin sorğu ilə registerNetworkCallback çağırın. onAvailable və onLost sonra bəyanat dəyişdikdə sizə deyir və bu, həqiqətən vacib olan hadisədir. Komponent dayandıqda geri çağırışın qeydiyyatını ləğv edin, əks halda tətbiq ölü ekrana istinad saxlayır və geri çağırış işləməyə davam edir.
NetworkRequest req = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(req, callback);
Bu, ACCESS_NETWORK_STATE tələb edir; bu normal icazədir: root yox, istifadəçi razılıq dialoqu yox, VPN icazəsi yox. Taymerlə sorğu göndərmək insanların geri çəkildiyi alternativdir və o, batareyanı yandırmaqla yanaşı, intervaldan qısa keçidi də buraxır; bu isə istifadəçinin bildirdiyi keçidin özüdür.
Cavabın hələ də deyə bilmədiyi şeylər
Bu API-lərin heç biri tunelin sağlam olub-olmadığını, hansı serverdə bitdiyini və ya müəyyən tətbiqin trafikinin onun içində olub-olmadığını bildirmir. VPN şəbəkəsi istifadə edilə bilən marşrutsuz mövcud ola bilər və hər tətbiq üçün tunel sual verən tətbiqi istisna edə bilər.
Buna görə yoxlamanın gördüyünü qeyd edin, təkcə boolean dəyəri deyil. Şəbəkə identifikatorunu, bəyan edilmiş transportları və həmin şəbəkənin aktiv olub-olmadığını qeyd edin. ‘VPN aşkarlanmadı’ deyən hesabat yararsızdır, halbuki bir şəbəkənin TRANSPORT_VPN bəyan etdiyini, lakin getActiveNetwork()-in Wi-Fi qaytardığını deyən hesabat birbaşa marşrutlaşdırmaya və ya istisna siyahısına işarə edir və bu, kiminsə üzərində işləyə biləcəyi düzəlişdir.