VpnService অ্যাপে স্প্লিট টানেলিং কয়েকটি নির্দিষ্ট জায়গায় ভাঙে, আর প্রতিটির সমাধান আলাদা। ক্লায়েন্ট শুধু একটি ব্যর্থ গন্তব্য দেখায়, তাই প্রথম কাজ হলো আপনি কোন সীমায় আটকাচ্ছেন তা বের করা: builder যে রুটগুলো পেয়েছে, যে প্রক্সিতে প্রমাণীকরণ করতে পারছেন না, যে resolver শুধু ঘোষণা করেছেন, নাকি প্ল্যাটফর্ম প্রতি ব্যবহারকারীর জন্য যে অ্যাপ-তালিকা প্রয়োগ করে। ঠিক এ কারণেই আমি চার ধাপের একটি পরীক্ষা রাখি। এটি নিচের ক্রমে চলে এবং প্রথম ব্যর্থতায় থেমে যায়।

builder প্ল্যাটফর্মকে কী দিয়েছে তা নিশ্চিত করুন

VpnService.Builder হলো ঘোষণার একটি তালিকা। establish() একটি ভার্চুয়াল ইন্টারফেসের জন্য ParcelFileDescriptor ফেরত দেয়, আর প্ল্যাটফর্ম কেবল addAddress ও addRoute-এর প্রিফিক্সের সঙ্গে মেলা ট্র্যাফিক ওই descriptor-এ পাঠায়; addDnsServer ক্লায়েন্টদের বলে কোন resolver-কে জিজ্ঞাসা করতে হবে। আপনি যে প্রিফিক্স যোগ করেছেন তার বাইরের কোনো গন্তব্য কখনো আপনার অ্যাপে পৌঁছায় না, ইন্টারফেস যা-ই প্রতিশ্রুতি দিক। এই একক সত্যই কোড বদলানোর আগেই ভাঙা স্প্লিট টানেলিংয়ের বেশিরভাগ অভিযোগ ব্যাখ্যা করে।

আপনার builder যে ঠিকানা, রুট ও DNS তালিকা তৈরি করেছে তা লগ করুন, তারপর সেগুলো ব্যর্থ গন্তব্যের সঙ্গে মিলিয়ে দেখুন। একটি ব্যবস্থাপনা হোস্টের জন্য একটি অনুপস্থিত /32 ভাঙা টানেলের চেয়ে অনেক বেশি সাধারণ। তালিকা ঠিক থাকলে এগিয়ে যান; ভুল হলে বাকি চেকলিস্ট বৃথা পরিশ্রম।

excludeRoute শুধু API 33 থেকে আছে

গন্তব্যভিত্তিক বাদ দেওয়া সেই অনুরোধ যা সবচেয়ে বেশি সাপোর্ট থ্রেডে পরিণত হয়। builder.excludeRoute(IpPrefix) এসেছে API 33-এ; তার আগে একমাত্র সমর্থিত উপায় হলো বাদ দেওয়া প্রিফিক্স ছাড়া বাকি সবের জন্য রুট যোগ করা, অর্থাৎ পূরক প্রিফিক্সগুলো আপনাকেই হিসাব করতে হবে। দুটি কল আয়নার প্রতিচ্ছবি নয়: একটি ইন্টারফেসের রুট সেট থেকে বিয়োগ করে, অন্যটি সেটটি শূন্য থেকে গড়ে, আর পুরোনো ডিভাইস অচেনা কল হয় ছুড়ে ফেলবে নয়তো নীরবে উপেক্ষা করবে। Build.VERSION.SDK_INT-এর ভিত্তিতে শাখা করুন এবং বাস্তব হার্ডওয়্যারে দুটি পথই পরীক্ষা করুন।

পূরক হিসাব করার সময় প্রিফিক্সের সারিবদ্ধতা খেয়াল করুন। /16 রুটের ভেতরে /24 বাদ দিতে হলে অবশিষ্ট অংশ সত্যিই বর্ণনা করে এমন আচ্ছাদনকারী প্রিফিক্স দরকার, একটি বিকৃত /8 নয়। চূড়ান্ত তালিকা লগ করুন এবং নিশ্চিত করুন যে বাদ দেওয়া হোস্ট এখনো টানেলের বাইরের পথে রিজলভ হয়।

প্রক্সি শংসাপত্র প্রকাশ করা যায় না

setHttpProxy(ProxyInfo) ইন্টারফেসকে একটি HTTP বা SOCKS প্রক্সির দিকে নির্দেশ করে, আর PAC URL প্রতি গন্তব্যে তাদের মধ্যে বাছাই করতে পারে। ProxyInfo বা PAC ফরম্যাট কোনোটিই ব্যবহারকারীর নাম ও পাসওয়ার্ড বহন করে না, তাই প্রমাণীকরণযুক্ত প্রক্সি প্ল্যাটফর্ম API-র বাইরে থাকে। যেসব অ্যাপের এটি দরকার তারা সাধারণত একটি userspace stack, tun2socks বা অনুরূপ ইঞ্জিন যুক্ত করে এবং 407 চ্যালেঞ্জের উত্তর নিজেরাই দেয়।

এই সিদ্ধান্তের পরিণতি আছে। userspace stack কেবল সেই ট্র্যাফিক দেখে যা আপনি তার দিকে রুট করেন, তাই এটিকে একই descriptor সীমানার ভিতরে থাকতে হয়, আর নিজের uplink সকেটের জন্য একই protect() প্রয়োজনীয়তা উত্তরাধিকারসূত্রে পায়। protect() বাদ দিলে টানেল নিজেরই ট্রান্সপোর্ট ধরে ফেলে, যা প্রথম প্যাকেটের পরে আটকে যাওয়া হ্যান্ডশেক হিসেবে দেখা যায়। শংসাপত্র দাবি করে এমন প্রক্সির বিরুদ্ধে পরীক্ষা করুন, কারণ খোলা প্রক্সি প্ল্যাটফর্মের পথে পাস করে এবং কিছুই প্রমাণ করে না।

অ্যাপ-তালিকা ও DNS-ও ঘোষণা

Builder-এ addAllowedApplication ও addDisallowedApplication দুটোই আছে, এবং এগুলি পরস্পরবিরোধী: দুটোই কল করলে ব্যতিক্রম ছোড়ে। যেভাবেই হোক তালিকাটি ইন্টারফেস সেটআপে প্রতি ব্যবহারকারীর জন্য প্রয়োগ হয়, তাই ওয়ার্ক প্রোফাইলের অ্যাপ আপনার টানেলের বদলে ওয়ার্ক প্রোফাইলের টানেলে চলে। কোনো API আপনাকে অন্য অ্যাপের সিদ্ধান্ত নিয়ন্ত্রণের সুযোগ দেয় না: বাইরে থেকে প্যাকেজকে আপনার টানেলে জোর করতে পারবেন না, আর ইতিমধ্যে সক্রিয় VPN বন্ধ বা পুনঃকনফিগার করতে পারবেন না।

DNS একই নমুনা অনুসরণ করে। addDnsServer একটি resolver ঠিকানা ঘোষণা করে; এটি আপনার অ্যাপকে কোয়েরির উত্তর দিতে দেয় না। নিজেই নাম রিজলভ করতে চাইলে একটি resolver চালান এবং তার ঠিকানা টানেলে রুট করুন। নির্দিষ্ট হোস্টনামে DoT বা DoH ব্যবহার করা অ্যাপ আপনার ঘোষিত ঠিকানা এড়িয়ে যেতে পারে, তাই কোয়েরির পথও আটকাতে না পারলে নাম রিজল্যুশনকে সর্বোত্তম প্রচেষ্টা হিসেবেই ধরুন।

ম্যানিফেস্ট, always-on এবং চার ধাপের পরীক্ষা

যোগ্যতা ম্যানিফেস্টে ঘোষিত হয়: BIND_VPN_SERVICE, android.net.VpnService intent ফিল্টার, আর টানেল ব্যাকগ্রাউন্ডে টিকতে হলে একটি foreground সার্ভিস। Always-on VPN অতিরিক্তভাবে পরিষ্কার onRevoke দাবি করে, কারণ ব্যবহারকারী সেটিংস বদলালে বা অন্য অ্যাপ নিয়ন্ত্রণ নিলে সিস্টেম এটি কল করে।

পরীক্ষাগুলো ক্রমে চালান এবং প্রথম ব্যর্থতায় থামুন। প্রথমে, builder-কে দেওয়া ঠিকানা, রুট ও DNS তালিকা বের করুন। দ্বিতীয়ত, নিশ্চিত করুন establish() একটি descriptor ফেরত দিয়েছে এবং ট্রান্সপোর্ট সকেট সুরক্ষিত। তৃতীয়ত, অন্তর্ভুক্ত প্রিফিক্সের ভিতরে একটি এবং বাদ দেওয়া প্রিফিক্সের ভিতরে একটি গন্তব্য অনুরোধ করুন। চতুর্থত, শুধু আপনার resolver যে হোস্টনাম রিজলভ করতে পারে তা জিজ্ঞাসা করে দেখুন কোন resolver আসলে উত্তর দিয়েছে।

সম্পন্ন হ্যান্ডশেক পরের ধাপগুলোর একটিও প্রমাণ করে না। প্রতিটি পরীক্ষা একটি স্তর আলাদা করে, তাই দ্বিতীয় ধাপে পাস চতুর্থ ধাপ সম্পর্কে কিছুই বলে না।

VpnService.Builder রেফারেন্স builder-এর সম্পূর্ণ পৃষ্ঠ তালিকাভুক্ত করে। এর বাইরে যা দরকার তা অন্য API, অন্য অ্যাপ বা আপনার নিজের userspace stack, আর সেটি স্প্লিট-টানেলের বাগ নয়, নকশার সিদ্ধান্ত।

সম্পর্কিত নিবন্ধ