Android پر VPN کی شناخت: tun0 اور ping کیوں گمراہ کرتے ہیں
انٹرفیس کا نام ایک روایت ہے؛ ping ثابت کرتا ہے کہ ایک میزبان قابلِ رسائی تھا۔ NetworkCapabilities سے پوچھیں کہ نیٹ ورک کیا اعلان کرتا ہے، پھر کال بیکس پر ردِعمل دیں۔
زیادہ تر Android ایپس VPN کی شناخت کے لیے جو جانچ استعمال کرتی ہیں وہ انٹرفیس کا نام ہے، اور یہ کبھی VPN کی جانچ نہیں تھی۔ یہ بتاتی ہے کہ ڈویلپر کی متوقع سٹرنگ کے ساتھ کوئی انٹرفیس موجود ہے، جس کا ایک ڈیوائس پر مطلب OpenVPN ہے اور دوسرے پر بالکل غیر متعلق چیز۔ ping بھی اسی قسم کا ثبوت ہے: یہ ثابت کرتا ہے کہ ایک منزل نے جواب دیا، یہ نہیں کہ کسی ٹنل نے پیکٹ اٹھایا۔ پلیٹ فارم درحقیقت جو پیش کرتا ہے وہ نیٹ ورکس کا مجموعہ اور ہر ایک کا اعلان کردہ ٹرانسپورٹ ہے، اور یہ مختلف جواب والا مختلف سوال ہے۔
افسانہ: اگر tun0 موجود ہے تو VPN چل رہا ہے
Android VPN ایپس VpnService کی تطبیقیں ہیں جو سسٹم کو tun فائل ڈسکرپٹر دیتی ہیں۔ انٹرفیس کا نام اسی سے آتا ہے جس نے اسے بنایا: OpenVPN عام طور پر tun0 بناتا ہے، PPTP اور L2TP ppp0 بناتے ہیں، WireGuard اپنے انٹرفیس کا نام ٹنل پر رکھتا ہے، اور IPsec ٹنل شاید کوئی روایتی نام ہی نہ دکھائے۔ اس لیے /proc/net/dev پڑھنا یا NetworkInterface اشیاء گننا حقیقت نہیں، روایت تلاش کرتا ہے۔
غلطی کی دونوں سمتیں زندہ ہیں۔ ڈیوائس میں صارف کے VPN سے کوئی تعلق نہ رکھنے والی مقامی ٹنل کے لیے tun0 ہو سکتا ہے، اور مکمل طور پر صحت مند WireGuard ٹنل صرف tun0 سٹرنگ جاننے والی جانچ کے لیے غیب ہو سکتا ہے۔ ایک ہی فنکشن میں جھوٹے مثبت اور جھوٹے منفی نتائج اسے تشخیص کے طور پر بے کار بنا دیتے ہیں، کیونکہ «کوئی VPN نہیں ملا» کہنے والی معاون ٹکٹ پر کسی بھی سمت بھروسہ نہیں کیا جا سکتا۔
رسائی بھی تنگ ہوئی ہے۔ نئے Android ورژن محدود کرتے ہیں کہ ایک عام ایپ /proc سے کیا پڑھ سکتی ہے، اس لیے Android 8 پر کام کرنے والی نام پر مبنی جانچ بغیر کسی کوڈ تبدیلی کے بعد کے ڈیوائس پر خالی فہرست واپس کر سکتی ہے۔ جب جانچ خاموشی سے کھلی طرف ناکام ہوتی ہے، ایپ نتیجہ نکالتی ہے کہ جس ہارڈویئر پر ٹنل ٹھیک ہے وہاں VPN نہیں ہے۔
افسانہ: کامیاب ping کا مطلب ہے کہ ٹنل ٹریفک اٹھا رہی ہے
اندرونی میزبان کو ping اس وقت کامیاب ہوتا ہے جب اس میزبان تک راستہ موجود ہو۔ منقسم ٹنلنگ میں میزبان Wi-Fi کے ذریعے قابلِ رسائی ہو سکتا ہے جبکہ ٹنل چل رہی ہو اور بالکل کچھ اور کر رہی ہو۔ فی ایپ VPN میں ping کرنے والی ایپ ٹنل سے باہر رکھی جا سکتی ہے، اس لیے ICMP گونج اس سے باہر جاتی ہے اور واپس آتی ہے، جو جانچ کے مفروضے کے خلاف ثابت کرتی ہے۔
ICMP اور TCP بھی الگ الگ فلٹر ہوتے ہیں۔ جو ٹنل گونج کی درخواستوں کی اجازت دیتی ہے مگر کسی سروس کے لیے ضروری TCP پورٹ چھوڑ دیتی ہے، وہ ping ٹیسٹ پاس کرے گی اور ایپ میں ناکام ہو گی، اور یہ ڈیوائس کی خصوصیت نہیں بلکہ دوسری طرف کا فائر وال قاعدہ ہے۔ کامیاب ping کی واحد دیانت دار قرأت یہ ہے کہ اس منزل نے اس لمحے اس پروٹوکول کا جواب دیا۔
پلیٹ فارم درحقیقت کیا بتاتا ہے
API 21 سے ConnectivityManager.getAllNetworks() سسٹم کے جانے ہوئے ہر نیٹ ورک کو واپس کرتا ہے، اور getNetworkCapabilities(network) بتاتا ہے کہ وہ نیٹ ورک کیا اعلان کرتا ہے۔ VPN نیٹ ورک TRANSPORT_VPN رکھتا ہے۔ تمام نیٹ ورکس پر گزرنا ایک سوال کا جواب دیتا ہے: کیا اس ڈیوائس پر ابھی کوئی VPN چل رہا ہے؟
دوسرا، زیادہ تنگ سوال الگ کال مانگتا ہے۔ getActiveNetwork() وہ نیٹ ورک واپس کرتا ہے جو ایپ کا ٹریفک ابھی استعمال کرے گا، اور اس پر وہی صلاحیت جانچنے سے جواب ملتا ہے: کیا میرا ٹریفک VPN سے گزرے گا؟ دونوں متفق نہ ہوں تو یہ اختلاف معلوماتی ہے۔ چل رہا مگر موجودہ نہ ہونے والا VPN فی ایپ روٹنگ یا منقسم ٹنل ترتیب کی طرف اشارہ کرتا ہے، گمشدہ ٹنل کی طرف نہیں۔
Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
دونوں کالز کی حفاظت کریں۔ فہرست بنانے اور پوچھنے کے درمیان غائب ہونے والے نیٹ ورک کے لیے صلاحیتیں null ہو سکتی ہیں، اور حوالگی کے دوران فعال نیٹ ورک null ہو سکتا ہے۔ NET_CAPABILITY_NOT_VPN مخالف سمت میں مفید ہے، یہ یقینی بنانے کے لیے کہ نیٹ ورک قطعی طور پر ٹنل نہیں ہے، نہ کہ صرف اس میں VPN ٹرانسپورٹ جھنڈا نہیں ہے۔
NetworkInfo API کے ساتھ وہی افسانہ تھا
NetworkInfo کے ساتھ ConnectivityManager.TYPE_VPN اس جانچ کی دوسری نسل ہے، اور یہ کسی وجہ سے متروک ہے۔ جدید ورژن میں ٹنل جڑی ہونے پر بھی VPN کے لیے واپس کردہ شے null ہو سکتی ہے، اس لیے null کو «VPN نہیں» سمجھنے والا کوڈ ان ڈیوائسز پر خاموشی سے جھوٹا منفی بتاتا ہے جہاں جواب سب سے زیادہ اہم ہے۔
عام طور پر ساتھ آنے والی نشریات بھی بہتر نہیں۔ CONNECTIVITY_CHANGE کا VPN تبدیلیاں چھوڑ دینے کا دستاویزی تاریخ ہے، جن میں وہ بلڈز بھی شامل ہیں جہاں یہ ہر نیٹ ورک تبدیلی پر نہیں بلکہ صرف VPN جڑنے یا ٹوٹنے پر چلتا تھا۔ اس پر بنایا گیا سامع ایک حالت متغیر بناتا ہے جو اگلے مکمل دوبارہ رابطے تک پلیٹ فارم کے نظریے سے دور ہوتا جاتا ہے۔
پولنگ کے بجائے کال بیکس پر ردِعمل دیں
ایسی NetworkRequest درج کریں جو TRANSPORT_VPN مانگے اور NET_CAPABILITY_NOT_VPN ہٹائے، پھر اس درخواست کے ساتھ registerNetworkCallback کال کریں۔ پھر onAvailable اور onLost بتاتے ہیں کہ اعلان کب بدلتا ہے، اور یہی وہ واقعہ ہے جو آپ کے لیے واقعی اہم ہے۔ جزو رکنے پر کال بیک کی رجسٹریشن منسوخ کریں، ورنہ ایپ مردہ اسکرین کا حوالہ رکھتی ہے اور کال بیک چلتا رہتا ہے۔
NetworkRequest req = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(req, callback);
اس کے لیے ACCESS_NETWORK_STATE درکار ہے، جو ایک عام اجازت ہے: root نہیں، صارف کی رضامندی کا مکالمہ نہیں، VPN اجازت نہیں۔ ٹائمر پر پولنگ وہ متبادل ہے جس کی طرف لوگ لوٹتے ہیں، اور یہ بیٹری جلاتی ہے اور پھر بھی وقفے سے چھوٹی تبدیلی چھوڑ دیتی ہے، اور وہی تبدیلی ہے جس کی صارف شکایت کرتا ہے۔
جواب اب بھی آپ کو کیا نہیں بتا سکتا
ان API میں سے کوئی بھی یہ نہیں بتاتا کہ ٹنل صحت مند ہے، کس سرور پر ختم ہوتی ہے، یا کسی خاص ایپ کا ٹریفک اس کے اندر ہے۔ VPN نیٹ ورک مفید راستوں کے بغیر بھی موجود ہو سکتا ہے، اور فی ایپ ٹنل سوال پوچھنے والی ایپ کو باہر رکھ سکتی ہے۔
اس لیے درج کریں کہ جانچ نے کیا دیکھا، صرف بولین نہیں۔ نیٹ ورک شناخت کنندہ، اعلان کردہ ٹرانسپورٹ اور یہ کہ وہ نیٹ ورک فعال تھا، لاگ کریں۔ «کوئی VPN نہیں ملا» کہنے والی رپورٹ بے کار ہے، جبکہ یہ کہنے والی رپورٹ کہ کسی نیٹ ورک نے TRANSPORT_VPN اعلان کیا مگر getActiveNetwork() نے Wi-Fi واپس کیا، سیدھا روٹنگ یا خارجی فہرست کی طرف اشارہ کرتی ہے، اور یہ ایسی درستی ہے جس پر کوئی کام کر سکتا ہے۔