مجموعهٔ هوشمند resolver (Smart resolver pool)
مجموعهٔ هوشمند resolver همان مدیریت resolverهاست که همهٔ خروجیهای تونل DNS در آن مشترکاند:
خروجیهای DNSTT، MasterDNS و Soar هر کدام
وقتی فیلد smart_pool آنها تنظیم شود مجموعهٔ خودشان را راه میاندازند. مجموعه پرسشهای تونل را
میان resolverهای زیادی پخش میکند، وقتی یکی کند، محدودشده یا ازکارافتاده باشد سراغ دیگری میرود و
ازکارافتادهها را در پسزمینه بررسی میکند تا دوباره پاسخ دهند. سرور یا سرویس جداگانهای نیست:
درون خود خروجی زندگی میکند و هر خروجی مجموعهٔ خودش را دارد.
چرا از آن استفاده کنیم
تونل DNS پرسشهای زیادی میفرستد و resolverهای عمومی زود شروع به محدود کردن یا رد کردن آنها میکنند. با مجموعه، کلاینت تونل یک resolver محلی میبیند و مجموعه پرسشها را روی همهٔ resolverهایی که کار میکنند جاری نگه میدارد.
همین کد به هر تونل DNS قابلیتهای یکسانی هم میدهد: auto (نامزدهای کشف resolver
Soar، از کد خود آن: resolverهای دستگاه، فهرست همراه کشور
discovery.country، resolverهای عمومی جهانی و با discovery.sample_ranges نشانیهایی از
بازههای کشور؛ سپس فهرست داخلی حدود ۵۸٬۰۰۰ resolver عمومی)، امتیاز ذخیرهشده برای هر resolver تا آنهایی که قبلاً کار کردهاند اول امتحان شوند، و
بررسی با یک پرسش ساده پیش از استفاده از هر resolver. resolverهای DoT و DoH از راه مجموعه برای هر
تونلی کار میکنند، حتی برای کلاینتی که فقط UDP ساده بلد است.
چطور از فیلترینگ عبور میکند
بهتنهایی چیزی را پنهان نمیکند. وقتی تکتک resolverها محدود یا مسدود میشوند، با پخش بار و کنار گذاشتن resolverهایی که از کار میافتند، تونل DNS را سرپا نگه میدارد.
چطور کار میکند
- در شروع، خروجی مجموعهای روی یک درگاه آزاد محلی باز میکند و همان نشانی را بهعنوان تنها
resolver به کلاینتش میدهد. سپس resolverهای
resolversرا بررسی میکند، اول آنهایی که بهترین امتیاز ذخیرهشده را دارند، و هر کدام را که قبول شود اضافه میکند؛ DNSTT یک تونل واقعی هم از راه آن باز میکند، MasterDNS و Soar پرسش سادهٔpretest_domainرا میفرستند. وقتی ده تا کار کنند بررسی مکث میکند، و MasterDNS و Soar هر ۱۵ ثانیه دوباره نگاه میکنند: اگر resolverهایی از مجموعه از کار افتاده باشند و کمتر از ده تا کار کنند، resolverهای بعدی فهرست بررسی میشوند، همان کاری که کشف resolver خود Soar برای مسیرهایش میکند. - مجموعه پرسشهای تونل را روی یک سوکت ماندگار برای هر resolver جابهجا میکند، پس تقریباً
چیزی به زمان پرسش اضافه نمیکند. به سوی مجموعه، DNSTT سیصد worker دارد (
udp-workersعدد دیگری میگذارد) که هر کدام یک پرسش در راه روی سوکت خودش نگه میدارد، به جای صد worker که برای هر پرسش سوکت تازه باز میکنند، که برای resolverهای واقعی و درگاه مبدأ تصادفی است. - هر پرسش به یکی از بهترین resolverهای زیر سقف نرخشان میرود (
load_balance: auto). هزینهٔ یک resolver تأخیر آن است، با وزن گمشدنش. هر resolverی که هزینهاش تا ۱٫۲ برابر ارزانترین، بهعلاوهٔ ۵ میلیثانیه، و گمشدنش تا ۲ درصد بیشتر از کمترین باشد همارز حساب میشود و اینها به نوبت، اول آن که دیرتر استفاده شده، پرسش میگیرند: resolverهای برابر سهم برابر میگیرند و resolver آشکارا کندتر یا پرافتتر کنار میماند. تأخیر، سریعترین پاسخ resolver در ده ثانیهٔ اخیر است، پس پرسش pollی که سرور تونل یک ثانیه نگه داشته resolver حامل آن را کنار نمیگذارد؛ پرسشهای در راه حساب نمیشوند، چون تونل صدها poll در راهِ resolverی دارد که اصلاً زیر بار نیست. گمشدن با عدم قطعیتش مقایسه میشود، پس چند نمونه نه resolver سالمی را کنار میگذارد و نه resolver پرافتی را سالم جا میزند. resolver تازه تا هشت پاسخ (ردها هم حساب) هر بار یک پرسش میگیرد: شاید چند برابر کندتر باشد و سهمی از پرسشها در آن جریان مرتب تونل را متوقف میکرد. resolver کنارمانده هر دو ثانیه دوباره امتحان میشود. - سقف نرخ هر resolver از آنچه واقعاً پاسخ میدهد یاد گرفته میشود. وقتی رد کند (REFUSED، SERVFAIL، HTTP 429) سقفش ۱٫۲ برابر نرخ پاسخ اخیرش میشود (رد شدن فوراً دوباره امتحان میشود، پس کمی بالاتر رفتن آن را اشباع نگه میدارد)؛ وقتی پرسشهایی را بیندازد در حالی که به بقیه پاسخ میدهد، ۰٫۹ برابر (انداختن فقط پس از یک مهلت معلوم میشود و برای تونل یک ارسال دوباره هزینه دارد). تا وقتی پاسخ میدهد سقف هر ثانیه ۱۰٪ بالا میرود و وقتی دیگر جلوی پرسشی را نگیرد برداشته میشود. پس از هر resolver تا سقفش استفاده میشود و تونل به جمع سقفها نزدیک میشود. resolverی که کمتر از سه ثانیه در استفاده بوده روی همان مدت سنجیده میشود، و تا یک ثانیه پس از آنکه مجموعه پرسش اضافه به آن فرستاد، ردهایش سقفش را پایین نمیآورند: وگرنه تونلی که صدها پرسش در راه دارد سقفها را بسیار پایینتر از حدها میبرد. رد شدن پس از یک دورهٔ آرام سقف را حداکثر ۳۰٪ پایین میآورد، اولین رد شدن پیش از تکمیل ده پاسخ ده تا فرض میشود (resolverی که همین حالا رد کرده دستکم چند ده تا در ثانیه پاسخ میدهد)، و پاسخهایی در دو ثانیهٔ کامل با نرخی بالاتر از سقف آن را فوراً بالا میبرند.
- پرسش ردشده فوراً به resolver دیگری که ظرفیت دارد فرستاده میشود، و رد شدن سطل توکن مجموعه را برای resolverی که رد کرده خالی میکند: همین حالا ظرفیتی ندارد، سقفش هر چه باشد. پرسشی که همهٔ resolverها را در سقف ببیند، یا همه ردش کرده باشند، تا ۳۰۰ میلیثانیه (حدود مهلتی که کلاینت تونل برای پاسخ میماند) منتظر توکن میماند و فقط پس از آن شکست میخورد: توکن در چند صدم ثانیه میرسد، در حالی که پرسش فرستادهشده بالاتر از سقف پس از یک رفتوبرگشت رد میشود و باز باید منتظر بماند، و شکست برای تونل یک مهلت ارسال دوباره هزینه دارد. هنوز برای هر resolver هر ۱۰۰ میلیثانیه یک پرسش منتظر بالاتر از سقف فرستاده میشود تا ظرفیتی را که سقف نمیشناسد پیدا کند، مگر آن resolver بهتازگی چیزی را رد کرده یا گم کرده باشد. DNSTT منتظر نمیماند: KCP آن ارسال دوباره را با رفتوبرگشت زمانبندی میکند و پرسش نگهداشتهشده برایش از پرسش شکستخورده گرانتر است (اندازهگیری: ۸۲ در برابر ۱۷۳ kbps روی یک resolver محدود).
- پس از میانگین تأخیر resolver بهعلاوهٔ چهار انحراف، مثل زمان ارسال دوبارهٔ TCP، پرسش روی
resolver دیگری هم امتحان میشود، اما تلاش اول تا
per_attemptباز میماند و پاسخش هنوز به حساب میآید: سرور تونل pollها را نگه میدارد و انداختن پاسخ دیررس دادهاش را از بین میبرد. اگر resolver اول واقعاً پرسش گم میکند، تلاش دوباره به هر resolverی میرود و اگر نه، فقط به یکی تقریباً به سرعت بهترین، چون آنگاه پرسش به احتمال زیاد نگه داشته شده است. پرسشهای بیپاسخِ resolverی که بهتازگی بسته گم کرده زودتر به resolver دوم هم فرستاده میشوند؛ resolver سالم هرگز این نسخه را نمیگیرد. - با
duplicate(DNSTT: ۲؛ MasterDNS: همانpacket_duplicationآن، وگرنه ۳) هر پرسش تا همین تعداد بار فرستاده میشود. سرور DNSTT هم به هر پرسش با بستههایی که در صف دارد پاسخ میدهد، و کلاینت VayDNS هر پاسخی را که از سوکت نگهداشتهشده میخواند تحویل میدهد، پس آنجا هم نسخهها داده میآورند (۴۰ در برابر ۳۳ مگابیت بر ثانیه روی یک resolver با ۱۰ میلیثانیه فاصله). سرور MasterDNS به هر پرسشی که میگیرد با بستهٔ بعدیاش پاسخ میدهد، پس پرسشی که دو بار فرستاده شود دو بسته میآورد و تونل بیشتر از آنچه کلاینتش نگه میدارد در راه دارد. نسخه به resolver بدون محدودیت نرخی میرود که بستهاش زودتر و مطمئنتر برمیگردد، کمترین تأخیر با وزن گمشدن، که شاید همان resolver ارسال اول باشد (توازن خود MasterDnsVPN هرگز نسخهای به آن نمیفرستد)، و همهٔ پاسخها به تونل میرسند. نسخهها کمی پس از ارسال اول میروند، یک چهارم رفتوبرگشت: کلاینت هر پاسخ را با یک پرسش تأیید میکند، و تأییدهای پاسخهایی که با هم میرسند در یک پرسش بستهبندی میشوند ولی تأییدهای پاسخهایی که جدا میرسند هر کدام یک پرسش میشوند، پس کلاینت پرسشهای بیشتری در راه نگه میدارد. مجموعه فقط به اندازهای نسخه میفرستد که داده بیاورند: تا وقتی بیشتر پاسخهای نسخهها پر باشند بیشتر میفرستد، وقتی نیمی خالی برگردند (سرور چیزی برای فرستادن ندارد، یا کلاینت درگیر CPU خودش است) کمتر، و بدون نسخه هر دو ثانیه چند تایی امتحان میکند. resolver دارای محدودیت نرخ نسخه نمیگیرد و سهمیهاش برای دادهٔ تازه میماند. - پس از
down_afterبار timeout یا خطای شبکهٔ پشت سر هم بدون هیچ پاسخی، resolver ازکارافتاده است. هر ثانیه یک نسخه از یک پرسش زنده را میگیرد و با اولین پاسخ دوباره در استفاده است. resolverی که هنوز به بعضی پرسشها پاسخ میدهد سقف میگیرد و هرگز کنار گذاشته نمیشود. - امتیاز هر resolver، از ۱۰- تا ۱۰، در فایل کش زیر کلید مخصوص همان خروجی ذخیره میشود
(
dnstt_resolvers<record type>،masterdns_resolvers).
پیکربندی
smart_pool یا true است، مجموعه با همهٔ پیشفرضها، یا یک شیء:
{
"outbounds": [
{
"type": "dnstt",
"tag": "dnstt-out",
"domain": "t.example.com",
"pubkey": "<server public key, hex>",
"resolvers": ["auto", "8.8.8.8:53", "dot://1.1.1.1:853", "https://cloudflare-dns.com/dns-query"],
"smart_pool": {
"enabled": true,
"load_balance": "lowest_latency"
}
},
{
"type": "masterdns",
"tag": "masterdns-out",
"domains": ["v.example.com"],
"resolvers": ["auto"],
"key": "<the server's encrypt_key.txt>",
"smart_pool": true
}
]
}
| فیلد | نوع | پیشفرض | معنی |
|---|---|---|---|
enabled |
bool | false |
مجموعه را اجرا کن. "smart_pool": true همان {"enabled": true} است. |
load_balance |
string | "auto" |
auto هر پرسش را به resolver با بیشترین ظرفیت آزاد میفرستد، چنانکه بالا گفته شد. roundrobin نوبتی. weighted تصادفی بر اساس وزن (اینجا همه برابر). lowest_latency سریعترین را ترجیح میدهد. |
deadline |
duration | 5s |
بیشترین زمان یک پرسش، روی همهٔ تلاشها. |
per_attempt |
duration | 2s |
بیشترین زمان یک تلاش روی یک resolver؛ وقتی تأخیرش معلوم شد، هر تلاش میانگین تأخیر بهعلاوهٔ چهار انحراف میگیرد، حداکثر همین. |
probe_interval |
duration | 5s |
هر چند وقت یک resolver ازکارافتاده بررسی شود. |
down_after |
int | 8 |
چند timeout یا خطای شبکهٔ پشت سر هم resolver را ازکارافتاده میکند. |
duplicate |
int | dnstt: 2؛ masterdns: همان packet_duplication، وگرنه 3 |
هر پرسش حداکثر چند بار فرستاده شود، نسخهها هم حساب؛ مجموعه به اندازهای میفرستد که داده بیاورند. نسخهها فقط به resolverهای بدون محدودیت نرخ میروند و همهٔ پاسخها به تونل میرسند. |
discovery |
object | نحوهٔ یافتن resolverهای "auto"، برای DNSTT و MasterDNS و Soar یکسان: country (کد ISO شبکهٔ دستگاه؛ هستهٔ هیدیفای آن را از منطقهٔ برنامه پر میکند)، sample_ranges (نشانیهایی از بازههای کشور که آخر آزموده شوند، پیشفرض ۰)، want (چند resolver سالم نگه داشته شود: برای مجموعه ۱۰، برای کلاینت Soar ۱۶)، concurrency (آزمونهای همزمان: برای مجموعه ۱۰۰، برای کلاینت Soar ۱۶)، pace (کمترین فاصلهٔ دو آزمون: برای مجموعه هیچ، برای کلاینت Soar 20ms). |
خود resolverها همان resolvers خروجیاند: host:port یا host برای UDP،
tcp://host:port، dot://host:853، https://host/dns-query و auto.
نکتهها
- مجموعه چیزی را که کلاینت تونل میبیند عوض میکند: یک resolver. کلاینتی که خودش resolverها را متوازن میکند (MasterDNS) آنگاه روی کل مجموعه توازن میکند و یک MTU برایش اندازه میگیرد، پس مجموعهای از resolverهای با محدودیتهای متفاوت MTU کوچکترین را میگیرد.
- مجموعه را با هر تونل DNS به کار ببرید؛ همین است که resolverهای DoT، DoH و
autoرا ممکن میکند. اندازهگیری روی loopback باtest/dnstunnel-bench/run.sh، که اینترنت و دسترسی root نمیخواهد:QUICK=1در حدود دو دقیقه بررسی میکند که مجموعه درست رفتار میکند (پخش برابر، دوری از resolverهای کند و پرافت، ترکیب resolverهای محدود)، و اجرای کامل با سه دور حدود ۴۰ دقیقه طول میکشد. میانهٔ سرعت دانلود به kbps (کیلوبیت در ثانیه) در سه دور: ۱۲۸ کیلوبایت از راه resolverهای محدود، وگرنه ۵ ثانیه. resolverها: یک یا سه نامحدود؛ یکی نامحدود با ۱۰ میلیثانیه فاصله؛ یکی محدود به ۳۰ پرسش در ثانیه؛ سه تا محدود به ۲۰، ۳۰ و ۴۰ (سومی به جای رد، میاندازد)؛ ۳۰ و ۴۰ با سومی ازکارافتاده؛ سه نامحدود که پس از ۱۰، ۶۰ و ۲۵۰ میلیثانیه پاسخ میدهند؛ سه نامحدود با ۰٪، ۱۰٪ و ۳۰٪ گمشدن. «اولین resolver» یعنی تنها اولین resolver هر چیدمان، که در دو چیدمان آخر سریعترین و بیگمشدنترین است.
| چیدمان | DNSTT، اولین resolver | DNSTT، مجموعه | MasterDNS، اولین resolver | MasterDNS، توازن خودش | MasterDNS، خودش، بی تکرار | MasterDNS، مجموعه |
|---|---|---|---|---|---|---|
| one-unlimited | 23365 | 41468 | 22941 | 24754 | 22734 | 30334 |
| one-10ms | 22203 | 38378 | 725 | 545 | 567 | 29084 |
| one-limited | 150 | 158 | 79 | 78 | 80 | 86 |
| three-unlimited | 25003 | 42300 | 22120 | 18467 | 29569 | 30486 |
| three-limited | 87 | 563 | 34 | 337 | 327 | 295 |
| three-limited-one-dead | 156 | 212 | 79 | 211 | 275 | 232 |
| three-uneven-latency | 20707 | 36280 | 644 | 17694 | 896 | 26833 |
| three-lossy | 23328 | 41464 | 23041 | 23652 | 74 | 29703 |
DNSTT در همهٔ چیدمانها از راه مجموعه سریعتر است: بدون محدودیت نرخ ۱٫۷ تا ۱٫۸ برابر DNSTT روی
اولین resolverش، از راه سه resolver محدود ۶٫۵ برابر و با یکی ازکارافتاده ۱٫۴ برابر؛ روی یک
resolver محدود هر دو برابرند. MasterDNS از راه مجموعه در شش چیدمان از هشت از هر راه دیگر اجرایش
جلوتر است: ۴۰ برابر MasterDNS تنها روی یک resolver با ۱۰ میلیثانیه فاصله (نسخهها پنجرهٔ تونل را
پر نگه میدارند)، ۱٫۵ برابر توازن خودش با تأخیرهای ناهمسان، ۱٫۳ برابر با resolverهای پرافت (که
آپلودش ۷ برابر سریعتر است: توازن خودش پرسشها را روی resolverهای پرافت هم پخش میکند)، ۱٫۲
برابر روی یک resolver نامحدود، و همپای آن روی سه resolver نامحدود. از راه سه resolver محدود و با
یکی ازکارافتاده یک دهم تا یک ششم از توازن خودش عقبتر است، در حالی که ۹ برابر MasterDNS تنهاست:
resolverی که بالاتر از نرخش میاندازد، انداختن را فقط پس از یک مهلت نشان میدهد، و سهم مجموعه
از آنها باز است. دورها روی loopback یک پنجم یا بیشتر فرق میکنند، پس فاصلههای کوچکتر جدول
تفاوت نیستند. توازن خود MasterDNS هر بسته را از دو resolver میفرستد (packet_duplication)؛ مجموعه همین
را با duplicate روی resolverهای بدون محدودیت نرخ میکند، که با تأخیرهای ناهمسان ۲۰ برابر ارزش
دارد، جایی که MasterDNS وگرنه حدود یک پرسش در راه نگه میدارد و منتظر هر پاسخ میماند. نتیجهها
روی loopback از اجرایی به اجرای دیگر حدود یک پنجم فرق میکنند، پس فاصلههای کوچکتر جدول تفاوت
نیستند. جدول کامل، با آپلود و نرخ پرسش، از اجرای همین اسکریپت به دست میآید؛ بنچمارک خود مجموعه،
بدون تونل، go run ./cmd/hmrdbench bench در
hmrd_multi_resolver_dns است.
- مجموعه فقط روی 127.0.0.1 و روی درگاهی که در شروع انتخاب میشود گوش میدهد و با خروجی بسته
میشود.