VpnService protect() atau pengecualian aplikasi: mana yang memperbaiki perutean
Lindungi socket yang membawa terowongan, atau kecualikan seluruh aplikasi: tiga syarat yang menentukan cakupan apa yang dibutuhkan VPN Android Anda.
Ada dua cara agar paket terowongan itu sendiri tetap di luar terowongan: lindungi socket transport, atau keluarkan seluruh aplikasi dari VPN. Keduanya menyelesaikan masalah yang berbeda, dan memilih cakupan yang lebih luas karena lebih mudah dipasang adalah cara aplikasi kehilangan akses jaringan lokal dan DNS terpisah tanpa menyadarinya. Mulailah dari apa yang harus tetap di luar terowongan, lalu pilih cakupan terkecil yang mencapainya.
Apa yang dihapus protect() dari terowongan
VpnService.protect() adalah panggilan per socket. Anda menyerahkan socket atau deskriptor yang membawa terowongan, dan Android menandai socket itu agar lalu lintasnya memakai jaringan di bawahnya alih-alih antarmuka VPN. Semua hal lain yang dikirim aplikasi tetap mengikuti rute VPN normal.
Cakupan sempit itulah intinya. Socket yang dilindungi tidak mengubah bagaimana klien HTTP Anda menyelesaikan nama, antarmuka mana yang dipakai panggilan analitik Anda, atau apa yang terjadi pada koneksi kedua yang dibuka nanti.
Tiga syarat yang menentukan cakupan
Syarat pertama adalah apakah hanya transport terowongan yang harus keluar. Jika lalu lintas aplikasi lainnya harus tetap melewati VPN, perlindungan socket adalah cakupan yang tepat. Syarat kedua adalah apakah Anda mengendalikan pembuatan socket. Pustaka yang membuka socket-nya sendiri jauh di dalam tidak menyisakan tempat bagi Anda untuk memanggil protect() kecuali ia mengekspos hook atau menerima socket yang sudah dibuat. Syarat ketiga adalah apakah aplikasi harus menjangkau jaringan lokal atau captive portal secara langsung, yang merupakan kebutuhan berbeda dari menjaga terowongan tetap hidup.
Mengecualikan aplikasi dengan addDisallowedApplication() adalah alat tumpul. Itu menghapus setiap koneksi yang dibuat aplikasi dari VPN, termasuk yang ingin Anda periksa, dan pada sebagian perangkat juga mengubah perilaku penemuan lokal.
Opsi ketiga adalah mengikat transport ke jaringan tertentu dengan bindProcessToNetwork() atau gagang jaringan pada tingkat socket. Itu berguna ketika perangkat memiliki Wi-Fi dan seluler sekaligus dan Anda ingin terowongan melewati uplink tertentu.
Panggil saat transport sedang dibuat
Panggil protect() di blok yang sama yang membuat transport, sebelum connect() atau send(). Jika panggilan mengembalikan false, tutup socket dan gagalkan penyiapan; melanjutkan meninggalkan Anda dengan transport yang paketnya bisa masuk kembali ke antarmuka Anda sendiri.
Bentuknya cukup kecil untuk disimpan di satu tempat:
DatagramSocket transport = new DatagramSocket();
if (!protect(transport)) {
transport.close();
throw new IOException("transport not protected");
}
transport.connect(serverAddress);Tiga detail menggigit di sini. Pertama,boolean adalah satu-satunya sinyal yang Anda dapat, jadi catat kegagalan dengan konteks yang cukup untuk membedakan persetujuan yang dicabut dari panggilan yang ditolak. Kedua,protect() mencakup socket, bukan nomor deskriptor: jika Anda menutup socket dan nanti memakai lagi bilangan bulat yang sama untuk socket lain, socket baru itu tidak terlindungi. Ketiga, ketika pustaka native membuka transport, lindungi deskriptor sebelum kode native terhubung, atau rutenya mungkin sudah dipilih.Referensi VpnService adalah tempat untuk memastikan kelebihan beban mana yang benar-benar disediakan tingkat API target Anda.
Uji jalur yang terblokir, bukan hanya jalur bahagia
Verifikasi dengan terowongan aktif, karena hanya dalam keadaan itu kedua jalur bisa berbeda. Kirim satu permintaan melalui transport yang dilindungi dan satu dari jalur kode yang tidak pernah memanggil protect(), lalu bandingkan apa yang dilihat ujung jauh: koneksi yang dilindungi seharusnya tiba dengan jaringan di bawahnya sebagai sumber, sementara yang tidak dilindungi menunjukkan alamat terowongan itu sendiri dan menggerakkan penghitung byte terowongan. Di perangkat,ss atau netstat menunjukkan pembelahan yang sama sebelum apa pun meninggalkan radio.
Uji pencabutan secara terpisah, karena perubahan persetujuan meruntuhkan antarmuka tanpa menyentuh kode socket Anda. Cabut persetujuan VPN atau hentikan layanan, bangun ulang transport, dan pastikan bahwa protect() masih mengembalikan true dan transport tersambung kembali begitu persetujuan diberikan lagi.
Jika permintaan yang dilindungi tetap mengikuti terowongan, panggilan perlindungan mungkin terjadi setelah socket sudah dirutekan. Pindahkan lebih awal dan uji lagi.
Simpan keputusan tertulis di samping penyiapan transport: socket mana yang dilindungi, lalu lintas aplikasi mana yang dikecualikan, dan apa yang dilakukan aplikasi saat perlindungan gagal. Baris itulah yang mencegah refaktor berikutnya memperluas pengecualian ke seluruh aplikasi karena satu pengujian gagal. Itu juga memberi orang berikutnya cara memeriksa niat tanpa merekayasa balik manifes.