VmessClient
VmessClient با یک UUID یا شناسهٔ کوتاه تنظیمشده، درخواستهای جدید VMess AEAD را ارسال میکند و
streamهای TCP یا datagramهای UDP با مقصد ثابت را روی transport بایتی حمل میکند.
رمزنگاری پیشفرض بدنه aes-128-gcm است. مقصد IPv4، IPv6 و دامنه پشتیبانی میشود.
address و port این node، مقصد نهایی ترافیک هستند. TcpConnector بعدی به
نشانی سرور VMess وصل میشود. TLS و WebSocket در nodeهای موجود transport تنظیم
میشوند و VmessClient آنها را از روی chain کشف یا پیکربندی نمیکند.
جایگاه و نمونهها
TcpListener -> VmessClient -> TcpConnector
UdpListener -> VmessClient -> TcpConnector
TcpUdpListener -> VmessClient -> TlsClient -> TcpConnector
TcpListener -> VmessClient -> HttpClient (WebSocket) -> TcpConnector
TcpListener -> VmessClient -> HttpClient (WebSocket) -> TlsClient -> TcpConnector
برای WebSocket مبتنی بر HTTP/1.1 روی TLS، ALPN سازگار با HTTP/1.1 را در TlsClient تنظیم کنید. بررسی گواهی و SNI نیز تنظیمات TlsClient هستند.
{
"name": "vmess-client",
"type": "VmessClient",
"next": "tls-client",
"settings": {
"uuid": "5783a3e7-e373-51cd-8642-c83782b807c5",
"address": "example.com",
"port": 443,
"protocol": "tcp",
"security": "aes-128-gcm",
"domain-strategy": "do-not-resolve-domains",
"first-payload-timeout-ms": 400,
"verbose": false
}
}
در این نمونه سرور VMess باید به example.com:443 وصل شود؛ connector میتواند
به host و port متفاوتی برای خود سرور VMess متصل شود.
برای حفظ مقصد ورودی از proxy یا router:
{
"name": "vmess-client",
"type": "VmessClient",
"next": "tcp-to-vmess-server",
"settings": {
"id": "5783a3e7-e373-51cd-8642-c83782b807c5",
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "dest_context->protocol",
"security": "chacha20-poly1305"
}
}
تنظیمات
| تنظیم | پیشفرض | مقدارهای مجاز |
|---|---|---|
uuid / id / user-id | الزامی | UUID هگزادسیمال خطتیرهدار/فشرده، یا رشتهٔ دقیق UTF-8 با طول ۱ تا ۳۰ بایت که با UUIDv5 و namespace صفر نگاشت میشود. |
target-address / address / target | الزامی | host/IP ثابت، dest_context->address یا line->dest_ctx->address؛ دامنه ۱ تا ۲۵۵ بایت غیر NUL. |
port | الزامی | عدد صحیح 1..65535، dest_context->port یا line->dest_ctx->port. |
protocol / proto | tcp | tcp/connect، udp/udp-associate، dest_context->protocol یا line->dest_ctx->protocol. |
domain-strategy | do-not-resolve-domains | یکی از راهبردهای جدول بعد. |
first-payload-timeout-ms | 400 | عدد صحیح 0..4294967295؛ صفر درخواست بدون payload را پس از Est بدون انتظار عمدی میفرستد. |
security | aes-128-gcm | aes-128-gcm، chacha20-poly1305، auto، none یا zero. |
chunk-masking | وابسته به حالت | Boolean؛ در همه حالتها جز zero برابر true است. |
global-padding | وابسته به حالت | Boolean؛ برای حالتهای رمزشده true و برای none و zero برابر false است. |
authenticated-length | false | Boolean؛ فقط برای بدنه رمزشده. |
encrypted-counter-policy | wrap | wrap یا strict؛ سیاست محلی هر جهت رمزشده. |
send-termination | true | Boolean؛ ارسال اختیاری رکورد پایانی framed خالی هنگام بستن محلی. |
packet-encoding | none | none یا packetaddr؛ packetaddr فقط برای UDP اعمال میشود. |
verbose | false | گزارش reason-coded حداکثر یک پیام در ثانیه برای هر node، بدون اعتبارنامه، کلید یا plaintext. |
از هر گروه alias مربوط به UUID و address دقیقاً یک نام و از گروه protocol حداکثر یک نام را انتخاب کنید. کلید تکراری، alias مبهم، تنظیم ناشناخته، نوع JSON نادرست و ترکیب framing نامعتبر، ساخت node را رد میکند. نام تنظیمات و مقدارهای رشتهای باید UTF-8 معتبر و بدون NUL داخلی باشند. فهرست کاربران، fallback، AlterID و احراز هویت قدیمی تنظیمات قابل قبول کلاینت نیستند.
در protocol پویا، TCP یا UDP دقیق انتخاب میشود. protocol ناموجود، مبهم یا
پشتیبانینشده با هشدار به TCP برمیگردد. address پویای ناموجود، port نامعتبر یا صفر
و UDP پویا همراه zero، association را پیش از Init کردن transport میبندد.
UDP ثابت همراه zero هنگام ساخت رد میشود.
شناسهٔ کوتاه بر اساس بایتهای دقیق UTF-8 و بدون trim، تغییر بزرگی حروف یا
نرمالسازی Unicode نگاشت میشود. برای نمونه waterwall به
106b9dde-356b-5769-b2f6-7ea4193ec5f7 نگاشت میشود. رشتهٔ هماندازهٔ UUID که UUID
معتبر نیست و شناسهٔ غیر UUID بلندتر از ۳۰ بایت رد میشود. در سرور managed، مقدار
password در AuthenticationClient باید UUID canonical نگاشتشده باشد؛ نرمالسازی نام
کوتاه فقط در این کلاینت و اعتبارنامههای محلی سرور انجام میشود.
این UUID میتواند توسط سامانه مدیریت کاربران VmessServer صادر شده باشد. کلاینت به AuthenticationClient یا UserController وابسته نیست، UUID خروجی را بهعنوان هویت احرازشده همتای محلی منتشر نمیکند و ثبت مصرف مضاعف روی carrier UDP ندارد. پذیرش، سهمیه، تغییر اعتبارنامه و shaping کاربران در سمت سرور/برنامه باقی میماند.
دامنهها
domain-strategy | رفتار |
|---|---|
do-not-resolve-domains | دامنه برای resolve شدن در سرور VMess فرستاده میشود. |
resolve-domains-and-accept-dns-returned-order | اولین پاسخ قابل استفاده DNS محلی انتخاب میشود. |
resolve-domains-and-prefer-ipv4 | اولویت IPv4 و سپس IPv6. |
resolve-domains-and-prefer-ipv6 | اولویت IPv6 و سپس IPv4. |
resolve-domains-and-use-only-ipv4 | پاسخ IPv4 الزامی است. |
resolve-domains-and-use-only-ipv6 | پاسخ IPv6 الزامی است. |
resolve-domains-with-core-settings | استفاده از DNS و ترجیح خانواده آدرس در core. |
resolve محلی از DomainResolver متعلق به node، پیش از VmessClient استفاده میکند. prepare hook مقصد نهایی را پیش از DNS اعمال میکند و resolver ورودی منتظر را مدیریت میکند. IP ثابت تغییر نمیکند. کلاینت مقصد انتخابشده را پیش از آنکه transport مقصد line را به endpoint سرور تغییر دهد، در state خصوصی نگه میدارد.
امنیت و framing
تمام حالتها header درخواست و پاسخ AEAD احرازشده دارند. none و zero بدنه
برنامه را رمز یا احراز اصالت نمیکنند. TLS جداگانه تنظیم میشود. برای واگذاری
محافظت بدنه به TLS، zero را برای TCP و none framed را برای UDP دستی انتخاب کنید؛
کلاینت TLS را کشف نمیکند و از روی nodeهای همسایه امنیت را کاهش نمیدهد.
auto یکبار هنگام ساخت node به AES-GCM یا ChaCha20-Poly1305 تبدیل میشود. AES در
صورت اثبات شتاب AES و carryless multiplication توسط قابلیتهای CPU/OS و پشتیبانی
provider مستقیم OpenSSL انتخاب میشود؛ در غیر این صورت ChaCha موجود ترجیح دارد.
الگوریتم صریح ناموجود ساخت را رد میکند. Auto هرگز none/zero انتخاب نمیکند.
| امنیت و فرمان | masking | global padding | authenticated length |
|---|---|---|---|
| AES-GCM یا ChaCha، TCP/UDP | اختیاری | اختیاری؛ نیازمند masking | اختیاری |
none، TCP | اختیاری | غیرفعال | غیرفعال |
none، UDP | اختیاری | اختیاری؛ نیازمند masking | غیرفعال |
zero، TCP | غیرفعال | غیرفعال | غیرفعال |
none دارای chunk framing است. zero همان NONE بدون framing است و فقط TCP را
پشتیبانی میکند. authenticated length جای طول معمولی/masked را میگیرد و نیازمند
masking نیست. همراه padding، SHAKE فقط برای padding پیش میرود. padding مربوط به
header درخواست مستقل و تصادفی بین ۰ تا ۱۵ بایت است.
بیتهای داخلی CHUNK=1، MASK=4، PADDING=8 و AUTH_LENGTH=16 ترکیبهای
1,5,13,17,21,29 را برای رمزنگاری، 1,5 را برای TCP NONE، 1,5,13 را برای
UDP NONE و 0 را برای zero میسازند. این اعداد تنظیم bitmask در JSON نیستند.
زمان ارسال و پاسخ
Init مقصد را آماده و transport را شروع میکند ولی درخواست VMess نمیفرستد. اولین payload مجاز بلافاصله ارسال میشود؛ حتی پیش از Est، داخل callback آن یا پس از Pause. timestamp، key/IV، نشانگر پاسخ، randomness مربوط به AuthID، nonce و padding هنگام اولین ارسال تازه تولید میشوند. درخواست و اولین رکورد بدنه در یک buffer معمولی قرار میگیرند. TCP بزرگتر بهترتیب به رکوردهای حداکثر ۱۶ KiB تقسیم میشود. یک خروجی معمولی تضمین یک syscall، بسته TCP یا رکورد TLS نیست.
هر Est مستقیم TCP، حتی تکراری، فوراً به برنامه میرسد؛ Est مربوط به carrier UDP فقط یکبار نگاشت میشود. اولین Est مهلت اصلی را برای درخواست ارسالنشده شروع میکند و تکرار آن مهلت را جابهجا نمیکند. با انقضا، TCP حتی هنگام Pause یک درخواست header-only میفرستد. carrier UDP در حالت Pause همان carrier صبر میکند و روی Resume میفرستد؛ payload مجاز همچنان بیدرنگ ارسال میشود. TCP خالی درخواست را شروع نمیکند.
ارسال خروجی منتظر پاسخ VMess نمیماند و مهلت جداگانهای برای انتظار پاسخ وجود ندارد؛ VmessServer ممکن است تا اولین داده backend، header پاسخ نفرستد. lifecycle و timeoutهای transport همچنان برقرارند. کلاینت طول محدود و احرازشده پاسخ، نشانگر انتظاری و طول دقیق فرمان بیرونی را بررسی میکند. option و فرمان اختیاری مصرف و نادیده گرفته میشوند؛ SwitchAccount و تغییر endpoint یا اعتبارنامه اعمال نمیشود. بدنه همرسیده حفظ میشود. tag خراب، طول نادرست یا نشانگر احرازشده اشتباه، اتصال مربوط را میبندد.
UDP، محدودیتها و پایان عمر
line UDP برنامه قرضی است و کلاینت برای هر association یک carrier TCP روی همان worker ایجاد میکند و مالک آن است. مقصد نهایی ثابت میماند. هر datagram پذیرفتهشده یک رکورد و هر رکورد دریافتی کامل و غیرخالی یک datagram است. در UDP بومی، datagram محلی خالی حذف میشود. datagram بزرگتر از حداکثر حالت در همهٔ حالتها بدون تقسیم یا شروع درخواست حذف میشود؛ timer بیکار هنوز میتواند header بفرستد. رکورد framed خالی، پس از بررسی tag در حالت رمزشده، پایان پروتکل است.
| حالت بدنه | حداکثر payload محلی UDP |
|---|---|
| AES/ChaCha، طول معمولی/masked | 65,519 بایت |
| AES/ChaCha، طول معمولی/masked و padding | 65,456 بایت |
| AES/ChaCha، authenticated length | 65,535 بایت |
| AES/ChaCha، authenticated length و padding | 65,472 بایت |
| NONE، طول معمولی/masked | 65,535 بایت |
| NONE، طول masked و padding | 65,472 بایت |
در packet-encoding=packetaddr، فرمان UDP به نشانی دقیق
sp.packet-addr.v2fly.arpa:0 میرود و این نشانه هرگز به DNS یا connector داده نمیشود.
هر رکورد پوشش خانواده/IP/port و یک datagram برنامه دارد. کلاینت یک مقصد ثابت و یک
carrier دارد؛ IP/port مبدأ پاسخ باید همان مقصد باشد. مبدأ نامنتظر حذف و شمارش میشود
و پوشش خراب association را میبندد. پوشش بدون payload یک datagram خالی واقعی است؛
رکورد بیرونی خالی VMess همچنان EOF است. برای حداکثر payload برنامه از جدول بالا،
۷ بایت IPv4 یا ۱۹ بایت IPv6 کم کنید.
Packetaddr مقصد IP میخواهد. DNS محلی صریح میتواند ابتدا دامنه را resolve کند؛
با do-not-resolve-domains دامنه پیش از Init کردن transport رد میشود. packetaddr
با TCP ثابت هنگام ساخت رد میشود و TCP پویا encoding معمولی را نگه میدارد.
این پوشش مستقل از Mux.Cool/XUDP است و pooling carrier ایجاد نمیکند.
حدود فراگیر نگهداری ورودی در هر جهت ۲ MiB بایت منطقی، ۱٬۰۲۴ entry و ۸ MiB هزینه تخصیص حافظه است و buffer فعال popشده و fragment ناقص را هم شامل میشود. اینها محدودیت پذیرش/ذخیرهاند و rate limiter یا سقف RSS کل پردازه نیستند. TX و RX guard جداگانه دارند. رکوردهای کامل پذیرفتهشده بهترتیب FIFO تخلیه میشوند، حتی اگر تحویل قبلی Pause ایجاد کند؛ صف decoded-output صرفاً برای فشار ساخته نمیشود.
سقف ورودی احرازنشده VmessServer همچنان ۶۴ KiB است. burst زودهنگام plaintext میتواند پس از افزودن framing و در زمان شناسایی، از سقف wire عبور کند. کلاینت پاسخ را acknowledgement احراز هویت نمیداند، pacing ضمنی ایجاد نمیکند، سقف سرور را بالا نمیبرد و burst ردشده را با reconnect/replay تکرار نمیکند.
پیشفرض encrypted-counter-policy=wrap همهٔ ordinalهای 0..65,535 را برای داده
استفاده میکند و با همان کلیدهای نشست از 65,535 به صفر ادامه میدهد. این رفتار برای
سازگاری با پیادهسازیهای رایج عمداً دنبالهٔ nonce شانزدهبیتی AEAD را تکرار میکند؛
rekey نیست و یکتایی nonce را حفظ نمیکند. authenticated length همان ordinal را دارد
و SHAKE مربوط به masking/padding بدون reset ادامه مییابد.
در strict دادهٔ خروجی فقط 0..65,534 است و 65,535 برای EOF رزرو میشود. دادهٔ معتبر
ورودی در 65,535 تحویل و سپس پیش از تکرار nonce اتصال بسته میشود. این تنظیم برای
NONE/zero پذیرفته میشود ولی اثری ندارد. سیاست محلی است و negotiation جدیدی ندارد؛
سمت strict میتواند همتای wrap را در حد خودش ببندد.
Finish مطابق full-close در WaterWall است: سمت فرستنده فوراً بسته، timer اولین
payload لغو، association جدا، bufferها تسویه و کلیدها پاک میشوند. termination
محدود فقط با send-termination=true پس از شروع درخواست و به سمت باز wire فرستاده
میشود. false همان full-close را بدون رکورد پایانی انجام میدهد، حتی در strict exhaustion.
EOF ورودی معتبر همیشه پذیرفته میشود و zero خام EOF framed ندارد. این گزینه محلی
است و negotiation ندارد. Finish درخواست جدید ایجاد نمیکند. handler پایان carrier متعلق به node، با مرگ منطقی همان carrier
برمیگردد. source-owner drain، lineهای قرضی و carrierهای وابسته را پیش از teardown
chain/pool میبندد و quiescence ارسال timer و association جدید را متوقف میکند.
node در تمام اتصال، حتی none/zero، فعال میماند.
آمار فقطخواندنی
عملیات ثبتشدهٔ stats در API، شیء خالی JSON یعنی {} با حداکثر ۶۴ بایت شامل
فاصلهها میپذیرد و reset یا mutation ندارد. دسترسی به permission معمول APIServer
برای node/operation نیاز دارد؛ راهنمای API را ببینید.
پاسخ JSON با schema ثابت، تنظیمات مؤثر امنیت/options/protocol، counter، termination، packet encoding، شمار تقریبی application/carrier فعال، رکوردهای موفق TX/RX، بایتهای برنامه و wire، wrapها، دلیلهای ثابت close/drop و پیامهای verbose سرکوبشده را دارد. تمام counterهای uint64 رشتهٔ دهدهی JSON هستند و دقت کامل دارند. بایت برنامه پوشش packetaddr را شامل نمیشود و دادهٔ موفق TX و دادهٔ تحویلشدهٔ RX را میشمارد؛ wire بایتهای ارسالی TX/دریافتی RX شامل handshake و framing است. EOF framed یک رکورد با صفر بایت برنامه است. snapshot از counterهای atomic مستقل خوانده میشود و همزمان با ترافیک تقریبی است؛ line زنده پیمایش نمیشود. کلاینت backend مقصد، auth cache یا جدول replay ندارد. ورودی غیرخالی/نامعتبر، deadline گذشته یا بودجهٔ ناکافی نتیجه/حافظه، خطای API برمیگرداند. بودجهٔ کار شامل ۴٬۶۰۸ بایت scratch ثابت برای render/metadata بهاضافهٔ charge واقعی تخصیص result pool است.
build و محدودیتها
INCLUDE_VMESS_CLIENT در buildهای استاندارد فعال است. کلاینت و سرور از codec داخلی
VmessCommon و provider مستقیم OpenSSL با نسخه حداقل 3.3 برای streaming SHAKE
استفاده میکنند؛ انتخاب backend عمومی crypto مستقل است. build فقط کلاینت به
VmessServer، AuthenticationClient یا UserController نیاز ندارد.
chain گسترشیافته از buffer معمولی استفاده میکند و خروجی تازه padding کامل onward pool را حفظ میکند.
header قدیمی، AlterID، AES-CFB، AES-CTR، Mux.Cool، XUDP، فرمان reverse، تعویض خودکار حساب/امنیت، reconnect/replay و transport جدید پشتیبانی نمیشوند.
framing مربوط به Mux در WaterWall اختصاصی است و سازگاری خارجی VMess Mux.Cool فراهم نمیکند.
فرادادهٔ نود
مقادیر برگرفته از instance/node.c:
| ویژگی | مقدار |
|---|---|
| node flags | kNodeFlagBlocksSplice |
can_have_prev | true |
can_have_next | true |
layer_group | kNodeLayer4 |
layer_group_prev_node | kNodeLayer4 |
layer_group_next_node | kNodeLayer4 |
required_padding_left | 0 بایت |