ObfuscatorServer
ObfuscatorServer همتای سمت سرور ObfuscatorClient است. این دو node باید از تبدیل برگشتپذیر، method، key و گزینههای header یکسان استفاده کنند.
در پیادهسازی فعلی تنها روش پشتیبانیشده XOR است. این node برای شکلدهی و مبهمسازی ترافیک به کار میرود و یک لایه رمزنگاری نیست.
وقتی 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:
packet transport -> ObfuscatorServer -> RawSocket
مقدارهای skip و tls_record_header باید با سمت client یکسان باشند.
این نود چه میکند؟
- مقدار
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 دریافت محدود نگه میدارد.
پیادهسازی سرور قرینه پیادهسازی client است. نامهای client و server فقط نقش آنها در deployment را مشخص میکنند؛ خود تبدیل متقارن است.
نمونه تنظیم
{
"name": "obfuscator-server",
"type": "ObfuscatorServer",
"settings": {
"method": "xor",
"xor_key": 90,
"skip": "transport",
"tls_record_header": true
},
"next": "service-node"
}
client متناظر:
{
"name": "obfuscator-client",
"type": "ObfuscatorClient",
"settings": {
"method": "xor",
"xor_key": 90,
"skip": "transport",
"tls_record_header": true
},
"next": "transport-node"
}
فیلدهای اجباری
فیلدهای top-level:
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نامی که کاربر برای node انتخاب میکند؛ باید در فایل config یکتا باشد. |
type | string | باید دقیقاً "ObfuscatorServer" باشد. |
settings | object | اجباری. باید method و xor_key داشته باشد. |
next | string | اجباری در استفاده معمولی. node بعدی upstream payloadهای transform شده را دریافت میکند. |
فیلدهای اجباری داخل settings:
| فیلد | نوع | توضیح |
|---|---|---|
method | string | باید "xor" باشد در پیادهسازی فعلی. |
xor_key | integer | کلید XOR. مقدار در یک بایت ذخیره میشود؛ بنابراین مقادیر خارج از 0..255 به uint8_t تبدیل میشوند. |
تنظیمات اختیاری
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
skip | string | "none" | مشخص میکند کدام headerهای ابتدایی packet پیش از XOR دستنخورده بمانند. مقدارهای مجاز "none"، "ipv4" و "transport" هستند. |
tls_record_header | boolean | false | یک header پنجبایتی شبیه TLS application-data را به payload سمت transport اضافه میکند یا از آن برمیدارد. |
tls_header | boolean | تنظیم نشده | نام مستعار قدیمی 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های سمت transport اینطور ارسال میشوند:
17 03 03 + length(2 bytes, big-endian) + obfuscated payload
این بایتها یک TLS application-data record header را تقلید میکنند:
| فیلد | مقدار |
|---|---|
| content type | 23 / 0x17 |
| legacy version | 0x0303 |
| length | طول payload obfuscated شده، ۱۶ بیتی big-endian |
در مسیر دریافت، ابتدا 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 بایتبهبایت با کلید یکبایتی تنظیمشده است.
نکتههای عملی
ObfuscatorServerوObfuscatorClientبایدmethod،xor_key،skipوtls_record_headerیکسان داشته باشند.- این قابلیت obfuscation است، نه امنیت رمزنگاریشده.
skip: "none"برای byte streamهای معمولی سادهترین حالت است.skip: "transport"برای packetهای خام IPv4 TCP/UDP که IP و transport headerهایشان باید قابل خواندن بمانند مفید است.- اگر فقط به رمزنگاری امن نیاز دارید، از
EncryptionClientوEncryptionServerاستفاده کنید.
متادیتای نود
metadata برگرفته از کد منبع:
این node با kNodeFlagBlocksSplice استفاده از splice را برای کل chain صریحاً غیرفعال میکند. این flag حتی در صورت وجود همزمان kNodeFlagSupportsSplice اولویت دارد.
| ویژگی | مقدار |
|---|---|
| node flags | kNodeFlagBlocksSplice |
can_have_prev | true |
can_have_next | true |
layer_group | kNodeLayerAnything |
layer_group_prev_node | kNodeLayerSameAsNext |
layer_group_next_node | kNodeLayerSameAsPrev |
required_padding_left | 5 bytes |