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

VlessClient

VlessClient پیاده‌سازی سمت client پروتکل plain VLESS v0 در WaterWall است. request header شامل UUID و مقصد نهایی را می‌نویسد و سپس داده TCP stream یا UDP datagramها را از نود stream-facing بعدی عبور می‌دهد.

این نود فقط بخش پایه VLESS را پیاده‌سازی می‌کند؛ TLS، REALITY، XTLS Vision، flow addon، mux، XUDP، WebSocket، gRPC و wrapperهای transport باید با نودهای دیگری ساخته شوند.

جایگاه رایج

VLESS client برای TCP:

TcpListener -> VlessClient -> TlsClient -> TcpConnector

VLESS client برای UDP:

UdpListener -> VlessClient -> TlsClient -> TcpConnector

مقصد پویا از proxy یا router:

Socks5Server -> VlessClient -> TlsClient -> TcpConnector
SniffRouter -> VlessClient -> TlsClient -> TcpConnector

VlessClient خودش TLS ایجاد نمی‌کند. در deployment معمول VLESS، TlsClient را بعد از آن قرار دهید و TcpConnector نهایی را به address و port سرور VLESS وصل کنید. مقصد واقعی سرویس در request header مربوط به VLESS نوشته می‌شود.

نمونه TCP

[
{
"name": "vless-client",
"type": "VlessClient",
"settings": {
"uuid": "5783a3e7-e373-51cd-8642-c83782b807c5",
"address": "example.com",
"port": 443,
"protocol": "tcp",
"domain-strategy": "do-not-resolve-domains",
"verbose": false
},
"next": "tls-client"
},
{
"name": "tls-client",
"type": "TlsClient",
"settings": {
"sni": "vless.example.net",
"verify": true
},
"next": "server-out"
},
{
"name": "server-out",
"type": "TcpConnector",
"settings": {
"address": "vless.example.net",
"port": 443,
"nodelay": true
}
}
]

نمونه Dynamic Destination

{
"name": "vless-client",
"type": "VlessClient",
"settings": {
"id": "5783a3e7-e373-51cd-8642-c83782b807c5",
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "dest_context->protocol"
},
"next": "tls-client"
}

id و user-id aliasهای قابل قبول برای uuid هستند.

نمونه UDP

{
"name": "vless-client-udp",
"type": "VlessClient",
"settings": {
"uuid": "5783a3e7-e373-51cd-8642-c83782b807c5",
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "udp"
},
"next": "tls-client"
}

در حالت UDP، نود قبلی همچنان با UDP کار می‌کند. VlessClient برای آن line یک TCP carrier line داخلی می‌سازد، درخواست VLESS UDP را روی carrier می‌فرستد و هر UDP payload را پس از یک length دو بایتی big-endian قرار می‌دهد.

فیلدهای لازم

فیلدنوعتوضیح
namestringنام یکتای نود داخل config.
typestringباید دقیقاً "VlessClient" باشد.
settingsobjectتنظیمات غیرخالی VLESS client.
nextstringنود stream-facing که byteهای مربوط به VLESS را حمل می‌کند؛ در استفاده معمول لازم است.

فیلدهای لازم داخل settings:

فیلدنام‌های قابل قبولتوضیح
UUIDuuid, id, user-iduser ID در VLESS؛ می‌تواند به فرمت dashed در RFC4122 یا hex فشرده ۳۲ کاراکتری باشد و به‌شکل ۱۶ byte خام ارسال می‌شود.
target addresstarget-address, address, targethost یا IP مقصد نهایی در درخواست VLESS، یا "dest_context->address".
target portportport مقصد نهایی، یا "dest_context->port"؛ مقدار عددی باید در بازه 1..65535 باشد.

UUID با byte order شبکه در RFC4122 سریال می‌شود؛ نه به‌شکل ASCII و نه با memory layout مربوط به GUID پلتفرم.

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

فیلدپیش‌فرضتوضیح
protocol"tcp"انتخاب VLESS command. نام proto هم پذیرفته می‌شود.
verbosefalseجزئیات بیشتری از tunnel را log می‌کند.
domain-strategy"do-not-resolve-domains"مشخص می‌کند domain مقصد پیش از نوشتن درخواست به‌صورت محلی resolve شود یا نه.

مقدارهای پشتیبانی‌شده برای protocol:

مقداررفتار
"tcp" یا "connect"VLESS TCP command 0x01 ارسال می‌شود.
"udp" یا "udp-associate"VLESS UDP command 0x02 ارسال می‌شود و UDP datagramها روی TCP carrier داخلی حمل می‌شوند.
"dest_context->protocol" یا "line->dest_ctx->protocol"protocol از destination context ورودی گرفته می‌شود. TCP دقیق command 0x01 و UDP دقیق command 0x02 را انتخاب می‌کند.

اگر با انتخاب dest_context->protocol، protocol ورودی وجود نداشته باشد، مبهم باشد یا TCP/UDP نباشد، client یک warning در log می‌نویسد و TCP را انتخاب می‌کند.

Domain Strategy

domain-strategy تعیین می‌کند آدرس نهایی در درخواست VLESS چگونه نوشته شود.

مقداررفتار
"do-not-resolve-domains"نام domain بدون تغییر در درخواست می‌ماند تا سمت VLESS server آن را resolve کند.
"resolve-domains-and-accept-dns-returned-order"resolve محلی انجام می‌شود و نخستین نتیجه قابل استفاده انتخاب می‌شود.
"resolve-domains-and-prefer-ipv4"پس از resolve محلی، IPv4 در اولویت است و در صورت نیاز IPv6 استفاده می‌شود.
"resolve-domains-and-prefer-ipv6"پس از resolve محلی، IPv6 در اولویت است و در صورت نیاز IPv4 استفاده می‌شود.
"resolve-domains-and-use-only-ipv4"فقط IPv4 قبول می‌شود. اگر DNS پاسخ IPv4 ندهد line بسته می‌شود.
"resolve-domains-and-use-only-ipv6"فقط IPv6 قبول می‌شود. اگر DNS پاسخ IPv6 ندهد line بسته می‌شود.
"resolve-domains-with-core-settings"resolve با تنظیمات dns در core.json انجام می‌شود.

با فعال بودن resolve محلی، VlessClient یک DomainResolver داخلی می‌سازد. prepare hook آن پیش از DNS lookup مقصد تنظیم‌شده را اعمال می‌کند. اگر مقصد نهایی VLESS نام domain باشد resolve انجام می‌شود؛ چه مقصد از JSON آمده باشد و چه از "dest_context->address". آدرس‌های IP بدون تغییر می‌مانند.

قالب Request و Response

request header:

version:      00
uuid: 16 raw bytes
addons len: 00
command: 01 TCP or 02 UDP
destination: port first, then address

destination format:

port:          2 bytes big-endian
address type: 01 IPv4, 02 domain, 03 IPv6
address body: IPv4 4 bytes, domain length + bytes, or IPv6 16 bytes

response header:

00 00

برای سازگاری بیشتر، client response addon غیرخالی را می‌پذیرد و یک warning در log ثبت می‌کند. byteهای addon نادیده گرفته می‌شوند و سپس body تحویل داده می‌شود؛ خود addon پردازش نخواهد شد.

رفتار TCP در زمان اجرا

در upstream Init، مقصد پیش از راه‌اندازی هسته protocol آماده می‌شود؛ یا از طریق prepare hook مربوط به resolver داخلی، یا در صورت غیرفعال بودن DNS محلی مستقیماً در init خود client. سپس VlessClient state مربوط به line را مقداردهی می‌کند و Init را به نود بعدی می‌فرستد.

وقتی transport downstream برقرار شود، VlessClient:

  1. downstream Est را به نود قبلی می‌فرستد
  2. VLESS request header را upstream می‌فرستد
  3. payloadهای upstream موجود در صف را بلافاصله پس از header ارسال می‌کند

برای ارسال byteهای application منتظر response header مربوط به VLESS نمی‌ماند؛ بااین‌حال body برگشتی تا زمان خواندن و اعتبارسنجی response header نگه داشته می‌شود.

رفتار UDP در زمان اجرا

در حالت UDP، application line از carrier line جداست:

Lineهدف
UDP app lineline قابل مشاهده برای نود UDP-facing قبلی.
TCP carrier lineline داخلی ساخته‌شده توسط VlessClient و ارسال‌شده به نود stream-facing بعدی.

carrier line یک VLESS UDP request برای یک destination ثابت می‌فرستد. بعد از آن:

  • هر UDP payload در جهت upstream به‌شکل uint16_be length + payload نوشته می‌شود
  • length هر VLESS UDP frame برگشتی خوانده می‌شود و payload خام UDP به app line برمی‌گردد
  • UDP payload با length برابر 0 یا بزرگ‌تر از 65535 در همان سمت دور ریخته می‌شود

client هنگام انتظار برای carrier/request حداکثر 1 MiB داده upstream را در صف نگه می‌دارد. byteهای پاسخ downstream نیز تا کامل شدن response header یا UDP frameها همین محدودیت 1 MiB را دارند.

رفتار Finish

VlessClient پیش از فرستادن Finish واقعی، line state خود را از بین می‌برد. برای lineهای معمولی بیرونی نیز lineDestroy() را فراخوانی نمی‌کند.

در حالت UDP، مالک carrier line داخلی‌ای است که خودش ساخته است. بسته شدن app line یا carrier line باعث بسته شدن سمت مقابل و از بین رفتن امن carrier line می‌شود.

Padding

VlessClient ممکن است prefix دو بایتی length در VLESS UDP را به ابتدای payload اضافه کند، بنابراین مقدار زیر را اعلام می‌کند:

required_padding_left = 2

متادیتای نود

ویژگیمقدار
Node flagkNodeFlagNone
previous nodeمجاز، و در استفاده عادی لازم
next nodeمجاز، و در استفاده عادی لازم
layer groupkNodeLayerAnything
required_padding_left2
line statetarget context، phase، read stream، pending queue، لینک‌های UDP app/carrier

قابلیت‌های پشتیبانی‌نشده

این پیاده‌سازی موارد زیر را پشتیبانی نمی‌کند:

  • ساخت TLS یا REALITY داخل node VLESS
  • XTLS Vision یا flow addons
  • VLESS mux
  • reverse
  • XUDP
  • WebSocket، HTTP، gRPC یا transport wrapperهای دیگر

برای این لایه‌ها از نودهای جدا مثل TlsClient، HttpClient، MuxClient یا TcpConnector استفاده کنید.

اشتباه‌های رایج

  • انتظار نداشته باشید VlessClient خودش به VLESS server وصل شود؛ بعد از آن stream transport بگذارید.
  • در deployment عمومی VLESS، TlsClient را حذف نکنید مگر اینکه عمداً plain VLESS را روی wire بخواهید.
  • address سرور VLESS را با target address نهایی اشتباه نگیرید. TcpConnector به server وصل می‌شود؛ VLESS request header مقصد نهایی را حمل می‌کند.
  • UDP mode را با next node غیر stream-facing استفاده نکنید.
  • به featureهای پشتیبانی‌نشده مثل Vision، flow، mux یا XUDP تکیه نکنید.