টানেল চালু হয়, চাবির আইকন দেখা যায়, আর ব্রাউজারের প্রতিটি অনুরোধ এখনও সময় শেষে ব্যর্থ হয়। VpnService অ্যাপে এই সমন্বয় সাধারণত বোঝায় পড়ার লুপ বেঁচে আছে আর আউটপুটের অর্ধেক ভাঙা, সার্ভার হ্যান্ডশেক প্রত্যাখ্যান করেনি। Android ইতিমধ্যে ইন্টারফেস তৈরি করেছে, অ্যাপের ট্রাফিক সেখানে সরিয়েছে এবং আপনার প্রসেসকে একটি ফাইল ডেসক্রিপ্টর দিয়েছে। সেই ডেসক্রিপ্টরের পরের সবকিছুই আপনার কোড, আর একটি অনুপস্থিত কলই পুরো স্ট্রিম গিলে ফেলার জন্য যথেষ্ট।

সিস্টেম আপনাকে কী দেয়, আর কী দেয় না

establish() একটি tun ইন্টারফেসের জন্য ParcelFileDescriptor ফেরত দেয়। সিস্টেম বাইরের দিকে যাওয়া IP প্যাকেট সেখানে লেখে এবং আশা করে আপনার প্রসেস সেগুলো পড়বে, কোথায় যাবে ঠিক করবে এবং সাধারণ সকেট দিয়ে পাঠাবে। এর নিচে কোনো কার্নেল ফরওয়ার্ডিং নেই, তাই কেউ না পড়লে প্যাকেটগুলো ফেলে দেওয়া হয় আর ইন্টারফেসের কাউন্টার কখনও নড়ে না।

অনুমানের বদলে গুনে শুরু করুন। প্রতিটি সফল পড়ায় একটি কাউন্টার বাড়ান এবং প্রথম কয়েকটি প্যাকেটের গন্তব্য ও প্রোটোকল লগ করুন। ট্রাফিক তৈরি হওয়ার সময় কাউন্টার শূন্য থাকলে ত্রুটি আপনার ফরওয়ার্ডিং কোডের আগে, যা অনুসন্ধানকে বিল্ডার কনফিগারেশন ও পড়ার মোডে সীমিত করে। কাউন্টার নড়লে আর ব্রাউজার তবু ব্যর্থ হলে ত্রুটি পাঠানোর পথে বা ফেরার পথে।

ব্লকিং পড়া লুপ নড়বে কি না ঠিক করে

setBlocking(true) ডেসক্রিপ্টরকে ব্লকিং মোডে রাখে এবং API 21 থেকে আছে। পুরনো সংস্করণে সমাধান হলো ছোট একটি JNI হেল্পার, যা কাঁচা ডেসক্রিপ্টরে fcntl দিয়ে O_NONBLOCK মুছে দেয়। মোডটি গুরুত্বপূর্ণ, কারণ এটি ঠিক করে অপেক্ষমাণ প্যাকেট ছাড়া একটি পড়া কী করতে পারবে।

নন-ব্লকিং মোডে সেই পড়া সঙ্গে সঙ্গে ঋণাত্মক মান নিয়ে ফেরে, আর ফলাফল উপেক্ষা করা লুপ কোনো ট্রাফিক না সরিয়েই একটি কোর ঘোরায়। ব্লকিং মোডে পড়া অপেক্ষা করে, ফরওয়ার্ডিং থ্রেডের ঠিক সেটাই দরকার, কিন্তু তখন থ্রেডটি আর বাধা দেওয়া যায় না। ডেসক্রিপ্টর বন্ধ করাই পড়াটিকে ব্যর্থ করার সমর্থিত উপায়, যাতে লুপ বেরোতে পারে। বদলে প্রসেস মারা বা থ্রেডে বাধা দিলে পরের শুরুতে এমন ডেসক্রিপ্টর থেকে যেতে পারে যা আর ইন্টারফেসের সঙ্গে মেলে না।

প্রতি কলে ইন্টারফেসের MTU আকারের বাফারে একটি প্যাকেট পড়ুন, আর শূন্য বা ঋণাত্মক ফেরতকে খালি প্যাকেট নয়, ত্রুটি হিসেবে ধরুন।

প্রথম পাঠানোর আগে protect(), পরে নয়

এনক্যাপসুলেটেড প্যাকেট বহন করা প্রতিটি সকেট আগে সুরক্ষিত করতে হবে। অসুরক্ষিত সকেট আপনি এইমাত্র বসানো রাউটিং নিয়মই মানে, তাই এর প্যাকেট আবার টানেলে ফিরে যায় এবং আপনার নিজের পড়ার লুপ সেগুলো আবার তুলে নেয়। তখন টানেল নিজের আউটপুট অনন্তকাল ফরওয়ার্ড করে, আর বাইরে থেকে দেখায় এমন সংযোগ যা খোলে কিন্তু কখনও উত্তর দেয় না।

ক্রম হলো protect, তারপর connect বা bind, তারপর send। connect-এর পরে protect ডাকলে আগের রুট সিদ্ধান্ত থেকে যেতে পারে, তাই কল true ফেরালেও সকেট ভুল পথই ব্যবহার করে। এটি যে বুলিয়ান ফেরত দেয় তা যাচাই করুন এবং false হলে জোরে ব্যর্থ হোন, এমন send-এ ঢুকে পড়ার বদলে যা কাজ করতে পারে না।

যে সকেট কখনও protect() ছোঁয়নি তাতে sendto থেকে পাওয়া অনুমতি ত্রুটিই এই ভুলের স্বাভাবিক চিহ্ন।

UDP নমুনা HTTPS বহন করে না

বেশিরভাগ টিউটোরিয়াল যে ফরওয়ার্ডিং নমুনা কপি করে তা UDP সামলায়, আর কিছুই না। TCP-র দরকার হ্যান্ডশেক, ক্রম সংখ্যা, স্বীকৃতি আর পুনঃপ্রেরণ, আর tun ডেসক্রিপ্টর ও সকেটের মধ্যে পেলোড কপি করা লুপ এগুলোর একটিও দেয় না। তাই UDP DNS কোয়েরি পার হয়, অথচ প্রতিটি HTTPS অনুরোধ আটকে থাকে। হয় ইউজারস্পেস TCP স্ট্যাক চালান, নয়তো সংযোগ স্থানীয় প্রক্সিকে দিন যা ডিভাইসে TCP শেষ করে স্ট্রিম ফরওয়ার্ড করে।

কিছু নতুন করে লেখার আগে বিভাজনটি পরীক্ষা করুন: একটি UDP কোয়েরি পাঠান আর একই হোস্টে একটি TCP সংযোগ খুলুন। UDP পথ কাজ করলে আর TCP পথ প্রথম SYN-এ আটকে থাকলে সমস্যা ফরওয়ার্ডিং কোডে, সার্ভারে নয়। এই একটি তুলনাই অকেজো সার্ভার লগ পড়ায় নষ্ট হওয়া অনেক সময় বাঁচায়।

রুট তালিকা ও অ্যাপভিত্তিক ফিল্টার যা দেখেন তা সীমিত করে

addAddress, addRoute ও addDns ঠিক করে সিস্টেম টানেলে কী পাঠাবে। রুট তালিকার বাইরের গন্তব্য কখনও আপনার ডেসক্রিপ্টরে পৌঁছায় না, তাই কোনো পরিমাণ ফরওয়ার্ডিং লজিক তা সারাবে না। আপনি যে বিল্ডার কল করেন তা লগ করুন এবং অ্যাপ সত্যিই যে ঠিকানায় পৌঁছাতে চায় তার সঙ্গে মেলান।

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

পুনঃস্থাপন পুরনো ডেসক্রিপ্টর রেখে যায়

আবার establish() ডাকলে নতুন ডেসক্রিপ্টর আসে আর আগের ইন্টারফেস বন্ধ হয়। পুরনো ডেসক্রিপ্টর এখন অবৈধ, তাই তাতে এখনও ব্লক থাকা থ্রেড ত্রুটি পায়, আর পুরনো পথের জন্য তৈরি সকেট বদলাতে হতে পারে। onRevoke হলো ব্যবহারকারী বা সিস্টেম যে টানেল কেড়ে নিয়েছে তার কলব্যাক। লাইফসাইকেল এক মালিকের কাছে রাখলে, যা আবার স্থাপনের আগে পাঠককে থামায়, বেশিরভাগ সংযুক্ত-কিন্তু-মৃত অবস্থা এড়ানো যায়।

প্রতিটি ত্রুটি তার নিজের ধাপ অনুযায়ী পড়ুন। recvfrom থেকে connection reset মানে আপনার কোড এখনও পড়ার সময় দূরপ্রান্ত বন্ধ হয়েছে। খারাপ ঠিকানার ত্রুটি হেডার পার্সিং বা অফসেট বাগের দিকে ইঙ্গিত করে, নেটওয়ার্কের দিকে নয়।

কাজের ক্রম: ব্লকিং মোড চালু, প্রতি পড়ায় একটি প্যাকেট, হেডার পার্স করা, সকেট সুরক্ষিত, পেলোড পাঠানো, হস্তান্তরে পরিষ্কার বন্ধ, আর গন্তব্য ঢাকা একটি রুট তালিকা।

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