پرش به مطلب اصلی

ObfuscatorClient

ObfuscatorClient پیش از آن‌که ترافیک به سمت transport برود، یک تبدیل برگشت‌پذیر روی payload اعمال می‌کند. در پیاده‌سازی فعلی تنها روش پشتیبانی‌شده XOR است.

این node باید با یک ObfuscatorServer جفت شود که method، key و گزینه‌های header یکسانی دارد. این فقط یک لایه سبک برای شکل‌دهی و مبهم‌سازی ترافیک است و رمزنگاری به شمار نمی‌آید. اگر به محرمانگی یا احراز هویت نیاز دارید، از EncryptionClient/EncryptionServer یا TLS استفاده کنید.

وقتی header غیرفعال باشد، مسیر همچنان بدون state است. حالت TLS-like برای بازسازی stream عادی، state دریافت نگه می‌دارد؛ line از نوع packet همچنان state اتصال ندارد.

جایگاه رایج​

مسیر TCP ساده:

client side: TcpListener -> ObfuscatorClient -> TcpConnector
server side: TcpListener -> ObfuscatorServer -> TcpConnector

مسیر UDP:

client side: UdpListener -> ObfuscatorClient -> UdpConnector
server side: UdpListener -> ObfuscatorServer -> UdpConnector

مسیر raw packet:

TunDevice -> ObfuscatorClient -> packet transport
packet transport -> ObfuscatorServer -> RawSocket

وقتی payload یک packet خام IPv4 است و header‌ها باید برای مسیریابی، تزریق یا خروجی packet خوانا بمانند، از skip: "ipv4" یا skip: "transport" استفاده کنید.

این نود چه می‌کند؟​

  • مقدار settings.method: "xor" الزامی است.
  • وجود settings.xor_key الزامی است.
  • پیش از فرستادن payload در جهت upstream به next، روی بایت‌های آن XOR اعمال می‌کند.
  • در صورت فعال بودن گزینه مربوط، پس از XOR در مسیر upstream یک header پنج‌بایتی شبیه TLS record به ابتدای payload اضافه می‌کند.
  • در مسیر downstream، در صورت فعال بودن گزینه مربوط، پیش از XOR همان header پنج‌بایتی را برمی‌دارد.
  • پیش از فرستادن payload در جهت downstream به node قبلی، روی بایت‌های آن XOR اعمال می‌کند.
  • می‌تواند header مربوط به IPv4 یا مجموعه header‌های IPv4 و TCP/UDP را بدون obfuscation باقی بگذارد.
  • پس از تغییر بایت‌ها، payload‌های packet-line را برای محاسبه دوباره checksum علامت‌گذاری می‌کند.
  • فقط برای stream عادی با header شبیه TLS، state دریافت محدود نگه می‌دارد.

از آن‌جا که XOR متقارن است، اجرای دوباره همان تبدیل با همان key، payload اصلی را بازیابی می‌کند.

نمونه تنظیم​

{
"name": "obfuscator-client",
"type": "ObfuscatorClient",
"settings": {
"method": "xor",
"xor_key": 90,
"skip": "transport",
"tls_record_header": true
},
"next": "transport-node"
}

سرور متناظر:

{
"name": "obfuscator-server",
"type": "ObfuscatorServer",
"settings": {
"method": "xor",
"xor_key": 90,
"skip": "transport",
"tls_record_header": true
},
"next": "service-node"
}

فیلدهای اجباری​

فیلدهای top-level:

فیلدنوعتوضیح
namestringنامی که کاربر برای node انتخاب می‌کند؛ باید در فایل config یکتا باشد.
typestringباید دقیقاً "ObfuscatorClient" باشد.
settingsobjectاجباری. باید method و xor_key داشته باشد.
nextstringدر استفاده معمول الزامی است. node بعدی payload‌های obfuscate‌شده در جهت upstream را دریافت می‌کند.

فیلدهای اجباری داخل settings:

فیلدنوعتوضیح
methodstringدر پیاده‌سازی فعلی باید "xor" باشد.
xor_keyintegerکلید XOR. مقدار در یک بایت ذخیره می‌شود؛ بنابراین مقادیر خارج از 0..255 به uint8_t تبدیل می‌شوند.

تنظیمات اختیاری​

گزینهنوعپیش‌فرضتوضیح
skipstring"none"مشخص می‌کند کدام header‌های ابتدایی packet پیش از XOR دست‌نخورده بمانند. مقدارهای مجاز "none"، "ipv4" و "transport" هستند.
tls_record_headerbooleanfalseیک header پنج‌بایتی شبیه TLS application-data را به payload سمت transport اضافه می‌کند یا از آن برمی‌دارد.
tls_headerbooleanتنظیم نشدهنام مستعار قدیمی tls_record_header است. همچنان پذیرفته می‌شود، اما یک warning در log ثبت می‌شود.

حالت‌های Skip​

skip تعیین می‌کند چند بایت ابتدایی payload از XOR مستثنا باشند:

مقداررفتار
"none"کل payload XOR می‌شود.
"ipv4"اگر payload با یک packet معتبر IPv4 شروع شود، header آن دست‌نخورده می‌ماند و بایت‌های بعدی XOR می‌شوند.
"transport"اگر payload با یک packet معتبر TCP/UDP روی IPv4 شروع شود، header‌های IPv4 و TCP/UDP دست‌نخورده می‌مانند. برای سایر protocol‌های IPv4 فقط header مربوط به IPv4 دست‌نخورده می‌ماند.

اگر payload بیش از حد کوتاه باشد یا packet معتبر IPv4 نباشد، طول skip برابر 0 در نظر گرفته می‌شود و کل payload XOR خواهد شد.

در حالت skip: "transport"، اگر header مربوط به TCP/UDP ناقص یا نامعتبر باشد، پیاده‌سازی فقط header مربوط به IPv4 را دست‌نخورده باقی می‌گذارد.

Header شبیه TLS​

وقتی tls_record_header فعال باشد، payload‌های upstream در سمت transport این‌طور ارسال می‌شوند:

17 03 03 + length(2 bytes, big-endian) + obfuscated payload

این بایت‌ها یک TLS application-data record header را تقلید می‌کنند:

فیلدمقدار
content type23 / 0x17
legacy version0x0303
lengthطول payload obfuscated شده، ۱۶ بیتی big-endian

در مسیر دریافت downstream، ابتدا header اعتبارسنجی و حذف می‌شود و سپس XOR اجرا می‌شود.

هشدار

این قابلیت TLS نیست: نه handshake دارد، نه certificate، SNI یا ALPN و نه رمزنگاری و احراز هویت. تنها ظاهر یک TLS application-data record پس از handshake را تقلید می‌کند.

رفتار stream:

  • ورودی بزرگ به record‌های حداکثر ۶۵۵۳۵ بایتی تقسیم می‌شود و XOR و skip برای هر record اجرا می‌شوند.
  • header و body تکه‌تکه بازسازی می‌شوند و چند record کامل در یک callback پردازش می‌شوند.
  • همه اعلان‌های Pause و Resume در هر دو جهت، حتی موارد تکراری، عبور می‌کنند. record کامل بدون انتظار برای Resume پردازش می‌شود و فقط record ناقص باقی می‌ماند.
  • حد نگه‌داری ثابت و برابر 2 MiB + 65540، یعنی ۲۱۶۲۶۹۲ بایت، مستقل از اندازهٔ pool، پروفایل حافظه و تنظیمات splice است. حداکثر ۶۵۵۳۵ بایت برای بدنهٔ هر record، محدودیتی جداگانه است.
  • record نامعتبر یا سرریز stream را می‌بندد و Finish تمام state را آزاد می‌کند.

روی line از نوع packet، هر packet هنوز باید یک record کامل باشد. سیاست حذف packet بزرگ، ناقص یا نامعتبر تغییر نکرده است؛ بازسازی میان packet‌های مستقل انجام نمی‌شود.

رفتار جهت‌ها​

callback ورودیرفتار
upstream Payloadبر اساس skip XOR را اعمال می‌کند، در صورت فعال بودن گزینه مربوط header شبیه TLS را به ابتدا می‌افزاید، درخواست محاسبه دوباره checksum را روی buffer مسیر packet-line قرار می‌دهد و payload را به next می‌فرستد.
downstream Payloadدر صورت فعال بودن گزینه مربوط header شبیه TLS را اعتبارسنجی و حذف می‌کند، بر اساس skip XOR را اعمال می‌کند، درخواست محاسبه دوباره checksum را روی buffer مسیر packet-line قرار می‌دهد و payload را به node قبلی می‌فرستد.
Init در هر دو جهتدر صورت فعال بودن framing، state دریافت normal line را مقداردهی می‌کند و سپس در همان جهت عبور می‌دهد.
Finish در هر دو جهتstate دریافت normal line را آزاد می‌کند و در جهت دور از فرستنده عبور می‌دهد.
Est، Pause و Resumeدر همان جهت، شامل اعلان‌های تکراری، عبور داده می‌شوند.

packet lineها state دریافت ندارند. در normal line دارای framing، خطای پروتکل یا عبور از حد پذیرش ابتدا state محلی را آزاد می‌کند؛ سپس سمت مقابل و بعد سمت source ثبت‌شده هنگام Init را می‌بندد. این nodeها برای XOR و پردازش record هر payload فعال می‌مانند و bypass دائمی نمی‌شوند.

Buffer و Padding​

این پنج بایت برای header اختیاری شبیه TLS در نظر گرفته شده‌اند. اگر هنگام فعال بودن tls_record_header فضای خالی سمت چپ buffer کافی نباشد، node payload را در buffer تازه‌ای با padding کافی کپی می‌کند، buffer قبلی را به pool برمی‌گرداند و سپس header را اضافه می‌کند.

اگر line فعلی packet line مربوط به worker باشد، node پس از تغییر بایت‌ها با sbufSetRecalculateChecksum(buf, true) روی buffer خروجی درخواست ترمیم می‌گذارد. اگر wrapping تخصیص را عوض کند، درخواست روی buffer باقی‌مانده قرار می‌گیرد. هر packet درخواست مستقل دارد و مسیر stream عادی درخواست checksum نمی‌گیرد.

نکته‌های Implementation​

XOR helper مسیرهای بهینه دارد:

  • AVX2 وقتی در دسترس باشد
  • پردازش chunk‌های ۶۴ بیتی با alignment مناسب در build‌های ۶۴ بیتی
  • در غیر این صورت، fallback به پردازش ۳۲ بیتی با alignment مناسب یا پردازش بایت‌به‌بایت

این مسیرها تنها روی کارایی اثر می‌گذارند؛ رفتار قابل مشاهده همچنان XOR بایت‌به‌بایت با کلید یک‌بایتی تنظیم‌شده است.

نکته‌های عملی​

  • ObfuscatorClient و ObfuscatorServer باید method، xor_key، skip و tls_record_header یکسان داشته باشند.
  • این قابلیت obfuscation است، نه امنیت رمزنگاری‌شده.
  • skip: "none" برای byte stream‌های معمولی ساده‌ترین حالت است.
  • skip: "transport" برای packet‌های خام IPv4 TCP/UDP که IP و transport headerهایشان باید قابل خواندن بمانند مفید است.
  • اگر header‌های packet خام را obfuscate کنید، ممکن است packet خروجی در مسیر downstream دیگر قابل مسیریابی نباشد؛ حتی اگر WaterWall checksum‌ها را برای محاسبه دوباره علامت‌گذاری کند.

متادیتای نود​

metadata برگرفته از کد منبع:

این node با kNodeFlagBlocksSplice استفاده از splice را برای کل chain صریحاً غیرفعال می‌کند. این flag حتی در صورت وجود هم‌زمان kNodeFlagSupportsSplice اولویت دارد.

ویژگیمقدار
node flagskNodeFlagBlocksSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayerAnything
layer_group_prev_nodekNodeLayerSameAsNext
layer_group_next_nodekNodeLayerSameAsPrev
required_padding_left5 bytes