Hayır, bir VpnService uygulaması telefonun VPN’ini açamaz
Uygulama bir tüneli ve yaşam döngüsünü sahiplenir, telefonun VPN anahtarını değil. Onay geri alınabilir, aynı anda yalnızca bir VPN çalışır ve paketleri çözümlemek size kalır.
Yanlış varsayım, bir VpnService uygulamasının sistem anahtarını çevirdiği ve telefondan sonra korunduğu yönündedir. Gerçekte olan daha dardır: uygulama onay ister, bir arayüz kurar, paketleri bir dosya tanımlayıcısı üzerinden okur ve yazar, durduğunda arayüzü geri verir. Aynı anda yalnızca böyle bir tünel canlı olabilir ve sistem onu sormadan alabilir. İnsanların protokole yüklediği kafa karıştırıcı davranışın çoğu bu sahiplik modelinden gelir.
Her yaşam döngüsü geçişini uç durum değil, tasarımın bir parçası olarak ele alın.
Efsane: uygulama telefonun VPN’ini açar
Akış VpnService.prepare() ile başlar; bu, kullanıcının kabul etmesi gereken bir Intent döndürür ve establish() ile sürer; bu da sonradan uygulamanın denetlemediği nedenlerle başarısız olabilir. İkinci bir tünel kurmak ilk arayüzü devre dışı bırakır ve sistem bildirimini değiştirir. Bu dizide hiçbir yerde uygulama cihaz çapında bir anahtara sahip değildir; ona verilen tanımlayıcıya ve üzerine düşen paketlere sahiptir. Kalıcı bildirim bile uygulamanın tasarımına değil sisteme aittir.
Arayüz kapılmak yerine verildiği için başka bir istemci yeri her an alabilir. Uygulama bunu dönüş değeriyle değil, geri çağırmayla öğrenir.
Uygulamanın zorla durdurulması bildirim panelinde aynı nedenle görünür: sistem ne verdiğini bildirir, uygulamanın ne göstermeyi seçtiğini değil.
Efsane: onay bir kez verilir
Sistem tüneli başka bir istemciye devrettiğinde, temel sınıfın onBind() içinden döndürdüğü binder üzerinde bir geri alma işlemi gönderir. O binderı kendi binderıyla değiştiren uygulama geri alma işlemini kendisi iletmelidir, yoksa VpnService.onRevoke() hiç tetiklenmez ve hizmet artık var olmayan bir tanımlayıcıya yazmaya devam eder. Onay kullanıcı tarafından da geri alınabilir ve sistem, hizmeti yeniden başlatma ya da her zaman açık yeniden bağlanma sırasında durdurabilir.
Bununla uğraşmak bir günlük satırı değil somut bir iştir: tanımlayıcıyı okumayı bırakın, kapatın, bağlı bayrağını temizleyin ve kullanıcıya hangi durumda olduğunu söyleyin. Sonra uygulamanın yeniden kurması, yeniden sorması ya da beklemesi gerekip gerekmediğine karar verin, çünkü her zaman açık yapılandırmada sistem hizmeti kendisi geri getirebilir.
Geri almayı durum değil istisna olarak gören bir uygulama, yazdığı her paket düşürülürken bağlı olduğunu bildirmeye devam eder ve bu, bir gizlilik aracı için olası en kötü arıza kipidir.
Efsane: arayüz trafiği sizin yerinize süzer
Gelen şey ham IP’dir. Yakalamak ya da süzmek; IP, TCP, UDP ve IPv6 başlıklarını ayrıştırmak, akışları izlemek ve yanıtları tanımlayıcıya geri yazmak demektir; bu da bir yakalama uygulamasını sistem olanağı değil, küçük bir ağ yığını yapar. protect(), uygulamanın kendi taşıma soketinin kendini yakalamak yerine tünelini atlaması için vardır ve tünel ayağa kalktıktan sonra oluşturulan her sokette denetlenmeyi hak eden bir boolean döndürür.
Oluşturucudaki izin verilen ve yasaklanan uygulama listeleri, kimin trafiğinin arayüze gireceğine karar verir. Arayüzün taşıdığı IP paketlerinin ötesindeki yüke erişim vermezler ve hiçbir API başka bir istemcinin tünelini açığa çıkarmaz ya da sistemin VPN profillerini listeler. Ağ yetenekleri bir ağın VPN taşıması olduğunu söyleyebilir, ama tünelin kimin olduğunu söyleyemez.
Bu yüzden diğer uygulamaların durumuyla ilgili sorular çelişkili yanıtlar alır: bilgi sürecinize açık edilmiyordur.
Yaşam döngüsü gerçekte ne ister
Denetlediğiniz durumları izleyin: hazır, kurulmuş, geri alınmış, durdurulmuş. Yeniden bağlanma, sökme ve ardından kurma olmalıdır; tanımlayıcının ve taşıma soketinin tek sahibi olmalıdır, çünkü kurmak için yarışan iki bileşen tek boş yer için çekişir. Modern Android’de arka plan başlatmaları kısıtlıdır, bu yüzden açılış yayını bir garanti değildir; API düzeyinin izin verdiği yerde bildirimiyle birlikte bir ön plan hizmeti kullanın.
Yalnızca son durumu değil geçişi günlüğe kaydedin ve durum makinesini her yolu sınamaya yetecek kadar küçük tutun. Testte ulaşamadığınız bir durum, sahada günlük olmadan karşılaşacağınız durumdur.
Yaşam döngüsünü bir cihazda sınayın
Emülatörler bağlantıyı taklit edebilir ama onay ve geri alma yolunu ikna edici biçimde değil, bu yüzden bunları yeri çalmak için ikinci bir VPN istemcisi kurulmuş gerçek bir cihazda çalıştırın.
adb shell dumpsys connectivity | grep -i -A3 vpn
adb shell dumpsys activity services | grep -i -A2 vpnservice
adb logcat -s VpnService:V AndroidRuntime:E
adb shell am force-stop com.example.vpnapp
Tüneli başlatın, bildirimi ve arayüzü doğrulayın, sonra diğer istemciyi bağlayın ve geri alma geri çağırmasının çalışıp çalışmadığını, durumun temizlenip temizlenmediğini izleyin. Uygulamayı zorla durdurun ve hâlâ canlı bir tanımlayıcı bildiren bir şey olup olmadığını denetleyin.
Diziyi her zaman açık bir yeniden başlatmadan sonra yineleyin, çünkü o yol kullanıcının başlattığı bağlantıdan farklıdır. Aradığınız şey yeşil bir simge değil, uygulamanın artık sahibi olmadığı bir tanımlayıcıyı kullanmayı bıraktığının kanıtıdır.