Android VpnService haqidagi to'rt taxmin menga bir marta haftaning katta qismini yo'qotdi va ularning hech biri tarmoq protokoli haqida emas edi. Ilova ulangan dedi, kalit belgisi holat qatorida o'tirardi, trafik esa baribir oddiy marshrut orqali chiqib ketardi. Men tunnelni to'xtatmagunimcha o'zimniki deb, qo'shgan marshrutlarim olingan marshrutlar deb, stopSelf chaqiruvi interfeysni ham birga tushirib yuboradi deb o'ylab yurdim. Xizmat aynan hujjatlashtirilganidek ishlardi. Buzilgan qism mening fikrlash modelim edi.

Afsona 1: tunnel siz uni to'xtatmaguningizcha turadi

Android bir vaqtda bitta VPN interfeysini ushlab turadi. Ikkinchisini o'rnating va platforma uni navbatga qo'ymaydi, aksincha siznikini o'chirib, xizmatingizda onRevoke() chaqiradi. Agar kodingiz hamma narsani faqat tugma bosilganda buzsa, egallash orqada deskriptor va o'qish oqimini qoldiradi, keyingi ishga tushish ikkalasini ham meros oladi.

VpnService ma'lumotnomasi aniq aytadi: ilovada allaqachon rozilik bo'lsa prepare() null qaytaradi, foydalanuvchi hali tasdiqlashi kerak bo'lsa Intent qaytaradi. Bu rozilik faqat niyatni startActivityForResult() orqali yetkazsangiz keladi. Yetkazishni o'tkazib yuborsangiz, establish() null qaytaradi, bu esa ruxsat muammosi emas, jim muvaffaqiyatsizlik sifatida o'qiladi.

Always-on VPN siz boshqarmaydigan egasini qo'shadi. Tizim tunnelni yuklanishdan yoki halokatdan keyin qaytarishi mumkin va u foydalanuvchi qo'lda ishga tushirgan VPN ilovasidan oldin o'tadi. onRevoke() ni odatdagi o'chirishingiz kabi resurslarni yopadigan qilib quring va unga chekka holat emas, asosiy to'xtatish yo'li sifatida qarang. Foydalanuvchilar VPN ilovalarini almashtiradi; kodingiz bunga dosh berishi kerak.

Afsona 2: addRoute aynan siz sanab o'tgan narsani ushlaydi

Qaysi paketlar deskriptoringizga yetib borishini marshrutlar hal qiladi, qamrov xatosi esa trafikning bir qismi tunneldan o'tib, bir qismi o'tmaydigan asabni buzadigan holatni keltiradi. addRoute("0.0.0.0", 0) butun IPv4 ni oladi, ya'ni endi har bir paket sizga keladi va uni uzatish yoki tashlab yuborish kerak. Bitta xost uchun /32 deyarli hech narsa olmaydi, qolgan hamma narsa esa VPN hech qachon o'rnatilmagandek tutadi.

Oilalar alohida sanaladi. Faqat IPv4 marshruti IPv6 ni asosiy tarmoqda qoldiradi, shuning uchun ikki stekli xost tunnel ichida to'liq ulangandek ko'rinib tursa ham tashqarida nomni hal qilib, ulanishi mumkin.

DNS ham xuddi shu mantiqqa ergashadi. addDnsServer() bilan qo'shilgan serverlar tunnel hal qiluvchisidan foydalanadigan ilovalarga boradi, o'zining shifrlangan DNS so'nggi nuqtasi yoki xost nomiga o'rnatilgan Private DNS bo'lgan ilova esa ularni butunlay e'tiborsiz qoldiradi. Nomlar bir ilova uchun muvaffaqiyatsiz bo'lib, boshqasi uchun ishlasa, bitta marshrutga ham tegmasdan oldin hal qiluvchi yo'lini tekshiring.

Afsona 3: ilova bo'yicha qoidalar ikki tomonga buriladigan kalitdir

addDisallowedApplication() va addAllowedApplication() ikkita filtr emas, ikkita rejimdir. Quruvchi har bir tunnel uchun bitta model qabul qiladi, shuning uchun uchta paketdan tashqari hammasini istagan ilova taqiqlangan ro'yxatdan foydalanadi va ruxsat etilgan ro'yxatni bo'sh qoldiradi. Ikkalasi bir-birini istisno qiladi va odamlar buni sozlamalar ekrani allaqachon ikkala kalitni taklif qilgandan keyin ishlash vaqtida bilib oladi.

Paket ko'rinishi ham tishlaydi. Android 11 va undan keyingi versiyalarda faqat ko'radigan paketlaringizni nomlay olasiz, shuning uchun ilovangizga ishga tushirishga ruxsat berilmagan so'rovdan tuzilgan istisnolar ro'yxati yozuvlarni jim o'tkazib yuboradi. Ro'yxatga ishonishdan oldin uni paket menejeri aslida nima qaytarishi bilan solishtiring.

Ish profillari auditoriyani yana bo'ladi. Profil ichida boshlangan tunnel shu profilni va uning ilovalarini qamrab oladi, shaxsiy tomon esa o'z ulanishini saqlaydi. Bu bir marta qabul qilinadigan qamrov qarori va uning joyi qo'llab-quvvatlash chiptasida emas, interfeysda.

Afsona 4: stopSelf tunnelni tugatadi

Yo'q. Interfeys establish() qaytaradigan deskriptorda yashaydi va xizmat obyekti shu deskriptor ochiq turganda o'lishi mumkin. Avval ParcelFileDescriptor ni yoping, havolani qo'yib yuboring, o'qish va yozish oqimlarini birlashtiring yoki to'xtating, so'ngra stopForeground(true) va stopSelf() ni shu tartibda chaqiring.

Unutilgan deskriptor xizmatning o'z to'xtatish so'rovidan omon qolgandek ko'rinishining odatiy sababi va uni qo'shgan ilovadan uzoqroq yashaydigan eskirgan marshrutni ham tushuntiradi.

Teskari xato ham shunchalik keng tarqalgan: o'qish oqimi o'qishning o'rtasida turganda deskriptorni yopish, keyin esa o'sha oqimni hech kim kod yozmagan istisno bilan shug'ullanishga qoldirish. To'xtatish tartibini bir joyda saqlang va foydalanuvchi amali ham, onRevoke() ham o'sha yerga chaqirsin.

Tunnel o'lik ko'ringanda nimani tekshirish kerak

Tunnel ko'tarilishidan oldin uzoq umr ko'radigan soketlarni protect() bilan himoya qiling. Busiz tunnelning o'z transporti yana tunnel ichiga yo'naltiriladi va ulanish aynan server nosozligiga o'xshab qotib qoladi. Faqat uzoq tarmoq orqasida mavjud manzilga qarshi sinang, keshdan yoki asosiy yo'ldan javob beradigan ommaviy saytga qarshi emas.

establish() natijasini, marshrutlarni, istisno qilingan paketlarni va o'chirish sababini qayd eting. O'sha to'rt qator paket ushlamasdan rozilik muammosini qamrov muammosidan va hayot sikli muammosidan ajratadi.

O'xshash maqolalar