نہیں، VpnService ایپ فون کا VPN آن نہیں کر سکتی
ایپ ایک سرنگ اور اس کے زندگی چکر کی مالک ہے، فون کے VPN سوئچ کی نہیں۔ رضامندی واپس لی جا سکتی ہے، ایک وقت میں صرف ایک VPN چلتا ہے، اور پیکٹ آپ ہی کو تجزیہ کرنے ہیں۔
غلط مفروضہ یہ ہے کہ VpnService ایپ سسٹم کا سوئچ پلٹ دیتی ہے اور اس کے بعد فون محفوظ رہتا ہے۔ دراصل ہونے والا معاملہ زیادہ تنگ ہے: ایپ رضامندی مانگتی ہے، ایک انٹرفیس بناتی ہے، فائل ڈسکرپٹر کے ذریعے پیکٹ پڑھتی اور لکھتی ہے، اور رکنے پر انٹرفیس واپس دے دیتی ہے۔ ایک وقت میں ایسی صرف ایک سرنگ زندہ رہ سکتی ہے، اور سسٹم بغیر پوچھے اسے لے سکتا ہے۔ لوگ پروٹوکول پر جو الجھن والا رویہ ڈالتے ہیں اس کا بڑا حصہ اسی ملکیت کے ماڈل سے آتا ہے۔
ہر زندگی چکر کی تبدیلی کو کنارے کا معاملہ نہیں بلکہ ڈیزائن کا حصہ سمجھیں۔
افسانہ: ایپ فون کا VPN آن کرتی ہے
بہاؤ VpnService.prepare() سے شروع ہوتا ہے، جو ایک Intent واپس کرتا ہے جسے صارف کو قبول کرنا ہوتا ہے، اور establish() کے ساتھ آگے بڑھتا ہے، جو بعد میں بھی ایپ کے قابو سے باہر وجوہات کی بنا پر ناکام ہو سکتا ہے۔ دوسری سرنگ قائم کرنے سے پہلا انٹرفیس غیر فعال ہو جاتا ہے اور سسٹم اطلاع بدل جاتی ہے۔ اس ترتیب میں کہیں بھی ایپ پورے آلے کا سوئچ نہیں رکھتی؛ اس کے پاس دیا گیا ڈسکرپٹر اور اس پر آنے والے پیکٹ ہوتے ہیں۔ مستقل اطلاع بھی ایپ کے ڈیزائن کی نہیں بلکہ سسٹم کی ہے۔
چونکہ انٹرفیس چھینا نہیں جاتا بلکہ دیا جاتا ہے، کوئی دوسرا کلائنٹ کسی بھی وقت جگہ لے سکتا ہے۔ ایپ یہ ریٹرن ویلیو سے نہیں بلکہ کال بیک سے جانتی ہے۔
ایپ کو زبردستی روکنے کا اطلاع پینل میں نظر آنا بھی اسی وجہ سے ہے: سسٹم بتاتا ہے کہ اس نے کیا دیا، یہ نہیں کہ ایپ نے کیا دکھانے کا انتخاب کیا۔
افسانہ: رضامندی ایک بار دی جاتی ہے
جب سسٹم سرنگ کسی دوسرے کلائنٹ کے حوالے کرتا ہے، تو وہ اس binder پر منسوخی کا لین دین بھیجتا ہے جو بنیادی کلاس onBind() سے واپس کرتی ہے۔ جو ایپ اس binder کو اپنے سے بدلتی ہے اسے منسوخی کا لین دین خود آگے بھیجنا ہوتا ہے، ورنہ VpnService.onRevoke() کبھی فعال نہیں ہوتا اور سروس ایسے ڈسکرپٹر میں لکھتی رہتی ہے جو اب موجود نہیں۔ صارف بھی منظوری واپس لے سکتا ہے، اور سسٹم ری اسٹارٹ یا ہمیشہ آن دوبارہ جڑنے کے دوران سروس بند کر سکتا ہے۔
اس سے نمٹنا لاگ کی ایک سطر نہیں بلکہ ٹھوس کام ہے: ڈسکرپٹر پڑھنا بند کریں، اسے بند کریں، منسلک جھنڈا صاف کریں اور صارف کو بتائیں کہ وہ کس حالت میں ہے۔ پھر فیصلہ کریں کہ ایپ کو دوبارہ قائم کرنا ہے، دوبارہ پوچھنا ہے یا انتظار کرنا ہے، کیونکہ ہمیشہ آن ترتیب میں سسٹم خود سروس واپس لا سکتا ہے۔
جو ایپ منسوخی کو حالت نہیں بلکہ استثنا سمجھتی ہے وہ منسلک ہونے کی اطلاع دیتی رہے گی جبکہ اس کا لکھا ہر پیکٹ پھینک دیا جاتا ہے، اور رازداری کے آلے کے لیے یہ بدترین ممکن ناکامی کی حالت ہے۔
افسانہ: انٹرفیس آپ کی طرف سے ٹریفک چھانتا ہے
جو آتا ہے وہ خام IP ہے۔ پکڑنا یا چھاننا یعنی IP، TCP، UDP اور IPv6 ہیڈر تجزیہ کرنا، بہاؤ پر نظر رکھنا اور جوابات ڈسکرپٹر میں واپس لکھنا، جو کیپچر ایپ کو سسٹم سہولت نہیں بلکہ چھوٹا نیٹ ورک اسٹیک بنا دیتا ہے۔ protect() اس لیے موجود ہے کہ ایپ کا اپنا ٹرانسپورٹ ساکٹ خود کو پکڑنے کے بجائے اپنی سرنگ سے گزر جائے، اور یہ ایک بولین واپس کرتا ہے جو سرنگ بلند ہونے کے بعد بنائے گئے ہر ساکٹ پر جانچ کے لائق ہے۔
بلڈر کی اجازت یافتہ اور ممنوعہ ایپ فہرستیں طے کرتی ہیں کہ کس کا ٹریفک انٹرفیس میں داخل ہوگا۔ وہ انٹرفیس کے اٹھائے ہوئے IP پیکٹوں سے آگے پے لوڈ تک رسائی نہیں دیتیں، اور کوئی API کسی دوسرے کلائنٹ کی سرنگ ظاہر نہیں کرتا یا سسٹم کی VPN پروفائلز کی فہرست دیتا ہے۔ نیٹ ورک صلاحیتیں کہہ سکتی ہیں کہ کوئی نیٹ ورک VPN ٹرانسپورٹ ہے، مگر یہ نہیں کہ سرنگ کس کی ہے۔
اسی لیے دوسری ایپس کے بارے میں حالت کے سوالات متضاد جواب پاتے ہیں: معلومات آپ کے عمل کو ظاہر ہی نہیں کی جاتیں۔
زندگی چکر دراصل کیا مانگتا ہے
جن حالتوں کو آپ کنٹرول کرتے ہیں ان پر نظر رکھیں: تیار، قائم، منسوخ، بند۔ دوبارہ جڑنا توڑنا اور پھر قائم کرنا ہونا چاہیے، ڈسکرپٹر اور ٹرانسپورٹ ساکٹ کا ایک ہی مالک ہونا چاہیے، کیونکہ قائم کرنے کی دوڑ میں دو اجزاء واحد دستیاب جگہ کے لیے لڑیں گے۔ جدید Android پر پس منظر میں شروعات محدود ہیں، اس لیے بوٹ نشریات کوئی ضمانت نہیں؛ جہاں API سطح اجازت دے وہاں اپنی اطلاع کے ساتھ پیش منظر سروس استعمال کریں۔
صرف آخری حالت نہیں بلکہ تبدیلی لاگ کریں، اور حالت مشین کو اتنا چھوٹا رکھیں کہ ہر راستہ جانچا جا سکے۔ جس حالت تک آپ جانچ میں نہیں پہنچ سکتے وہ حالت بغیر لاگ کے میدان میں ملے گی۔
آلے پر زندگی چکر جانچیں
ایمولیٹر رابطہ جعلی بنا سکتے ہیں مگر رضامندی اور منسوخی کا راستہ قابلِ یقین طور پر نہیں، اس لیے یہ جگہ چرانے کے لیے دوسرا VPN کلائنٹ نصب شدہ حقیقی آلے پر چلائیں۔
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
سرنگ شروع کریں، اطلاع اور انٹرفیس کی تصدیق کریں، پھر دوسرا کلائنٹ جوڑیں اور دیکھیں کہ منسوخی کا کال بیک چلتا ہے اور حالت صاف ہوتی ہے۔ ایپ کو زبردستی روکیں اور جانچیں کہ کیا کوئی چیز اب بھی زندہ ڈسکرپٹر کی اطلاع دیتی ہے۔
ہمیشہ آن ری اسٹارٹ کے بعد ترتیب دہرائیں، کیونکہ وہ راستہ صارف کے شروع کردہ رابطے سے مختلف ہے۔ آپ سبز نشان نہیں بلکہ یہ ثبوت ڈھونڈ رہے ہیں کہ ایپ نے ایسے ڈسکرپٹر کا استعمال بند کر دیا جو اب اس کی ملکیت نہ تھا۔