قیمت VpnService.Builder.addDnsServer تک سٹرنگ کے طور پر پہنچی اور builder نے کچھ بھی قائم ہونے سے پہلے وہیں مسترد کر دیا۔ یہ مسترد ہونا مقامی ہے: کسی resolver سے رابطہ نہیں ہوا، tunnel نہیں، اور جو ناکام ہوا وہ فارمیٹ جانچ ہے، DNS نہیں۔

یہ اہم ہے کیونکہ عام VPN ایپ میں تین یکساں DNS اِن پٹ ہوتے ہیں: پروفائل میں قیمت، سرور کی بھیجی قیمت اور سسٹم سطح پر Private DNS۔ صرف پہلے دو ہی اس کال تک پہنچتے ہیں۔

تیاری آبجیکٹ دلیل کو فوراً جانچتا ہے

VpnService.Builder tunnel نہیں ہے۔ یہ پتے، راستے اور سرور جمع کرنے والا تیاری آبجیکٹ ہے، اور صرف establish() ہی ان فہرستوں کو زندہ ParcelFileDescriptor اور انٹرفیس میں بدلتا ہے۔ DNS اندراج addDnsServer کال کرتے وقت جانچا جاتا ہے۔

دو نتائج: سٹیک ٹریس مختصر اور جان بوجھ کر بے معلومات ہے، اور لاپرواہ catch resolver کے بغیر tunnel چھوڑ دیتا ہے۔

builder دی گئی کوئی بھی فہرست رکھتا ہے؛ جانچ پاس کرنے والا سرور شامل کرنا صرف یہ ثابت کرتا ہے کہ سٹرنگ تعمیر کے وقت پتہ لٹرل کے طور پر تجزیہ ہوئی۔

دلیل کیا ہو سکتی ہے

پیرامیٹر پتہ ہے، endpoint نہیں۔ Builder حوالہ IPv4 یا IPv6 لٹرل لیتا ہے، اور تجزیہ صرف عددی ہے: android.net.InetAddresses.parseNumericAddress API 29 سے اس پارسر کا عوامی چہرہ ہے۔

عملی نقشہ ایسا دکھتا ہے:

InputResultReason
10.8.0.1acceptedIPv4 literal
2606:4700:4700::1111acceptedIPv6 literal
dns.example.comrejectedhostname; numeric parsing does not resolve it
10.8.0.1:53rejectedport belongs to your own config model
https://dns.example.com/dns-queryrejectedDoH URL, a different configuration path
127.0.0.1rejectedloopback is not a remote resolver
0.0.0.0 or ::accepted by the builderparses fine, but names no destination; only your own validator rejects it

ورژن اس بات پر اثر ڈالتا ہے کہ آپ کیسے تصدیق کرتے ہیں، اس پر نہیں کہ builder کیا قبول کرتا ہے۔ InetAddresses API 29 سے دستیاب ہے، اس لیے Android 10 پر تجزیہ ایک کال ہے؛ addDnsServer(InetAddress) اصل شکل، String نیا اضافہ۔ API 29 سے نیچے InetAddress.getByName روکنے والی نیٹ ورک تلاش سے ہوسٹ نام حل کرے گا۔

loopback یہاں مسترد ہوتا ہے، wildcard نہیں

ڈیسک ٹاپ VPN کلائنٹ اپنا stub resolver اکثر 127.0.0.1:53 پر بھیجتے ہیں؛ اسے Android پروفائل میں نقل کرنے سے مقامی مشین بیان کرنے والی قیمت آتی ہے، اور یہاں مقامی مشین نہیں ہے۔ builder صرف اسی صورت کو مسترد کرتا ہے: null یا loopback پتہ IllegalArgumentException پھینکتا ہے۔

wildcard مخالف صورت ہے اور یہیں زیادہ تر قراءتیں غلط ہوتی ہیں۔ 0.0.0.0 اور :: loopback نہیں ہیں، اس لیے builder انہیں قبول کرتا ہے؛ تعمیر میں کچھ نہیں پھینکا جاتا۔ پھر انٹرفیس ایسی اندراج کے ساتھ اٹھتا ہے جو کوئی منزل نہیں بتاتی۔ اسے پکڑنا آپ کے ویلیڈیٹر کا کام ہے: isAnyLocalAddress کو غلط اِن پٹ سمجھیں۔

ترتیب کی حد پر ایک بار تجزیہ کریں

سستا حل یہ ہے کہ سٹرنگ آپ کے ماڈل میں جہاں داخل ہو وہیں تصدیق کریں:

import android.net.InetAddresses
import java.net.InetAddress

private fun dnsLiteralOrNull(raw: String?): InetAddress? {
    val text = raw?.trim().orEmpty()
    if (text.isEmpty()) return null
    val address = try {
        InetAddresses.parseNumericAddress(text)
    } catch (e: IllegalArgumentException) {
        return null
    }
    return address.takeUnless { it.isAnyLocalAddress || it.isLoopbackAddress }
}

null نتیجہ غلط متن والی فیلڈ سطح کی خرابی بنتا ہے اور صرف اسی شکل پر مدد کارروائی کر سکتی ہے۔ کامیابی کے راستے میں آبجیکٹ سیدھا builder.addDnsServer(address) میں جاتا ہے، اور IllegalArgumentException کو خاص طور پر پکڑیں، Exception کو نہیں۔

جب مطلوبہ قیمت مسترد ہو تو عوامی resolver سے مت بدلیں؛ خاموشی سے عوامی DNS رکھنے والا پروفائل جڑتا ہے مگر اندرونی نام حل ہونا بند ہو جاتا ہے۔

انٹرفیس بننے کے بعد رسائی کی تصدیق کریں

builder کی قبولیت پہلا جانچ نقطہ ہے، آخری نہیں۔ انٹرفیس اٹھنے کے بعد LinkProperties.getDnsServers() بتاتا ہے کہ نیٹ ورک نے واقعی کیا حاصل کیا اور adb shell dumpsys connectivity وہی خصوصیات باہر سے چھاپتا ہے۔

Private DNS الگ کنٹرول ہے اور اس دلیل سے نہیں گزرتا؛ DoT فراہم کنندہ کا ہوسٹ نام سسٹم سیٹنگ کا ہے، IP فیلڈ کا نہیں۔

پروفائل پارسر دوبارہ بدلنے سے پہلے خام سٹرنگ اور اس کا ماخذ لاگ کریں، تصدیق کریں کہ یہ ہوسٹ نام نہیں لٹرل ہے، loopback یا wildcard نہیں ہے، اور تجزیہ شدہ آبجیکٹ آگے بھیجیں۔

متعلقہ مضامین