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 قرار میدهد.
فیلدهای لازم
| فیلد | نوع | توضیح |
|---|---|---|
name | string | نام یکتای نود داخل config. |
type | string | باید دقیقاً "VlessClient" باشد. |
settings | object | تنظیمات غیرخالی VLESS client. |
next | string | نود stream-facing که byteهای مربوط به VLESS را حمل میکند؛ در استفاده معمول لازم است. |
فیلدهای لازم داخل settings:
| فیلد | نامهای قابل قبول | توضیح |
|---|---|---|
| UUID | uuid, id, user-id | user ID در VLESS؛ میتواند به فرمت dashed در RFC4122 یا hex فشرده ۳۲ کاراکتری باشد و بهشکل ۱۶ byte خام ارسال میشود. |
| target address | target-address, address, target | host یا IP مقصد نهایی در درخواست VLESS، یا "dest_context->address". |
| target port | port | port مقصد نهایی، یا "dest_context->port"؛ مقدار عددی باید در بازه 1..65535 باشد. |
UUID با byte order شبکه در RFC4122 سریال میشود؛ نه بهشکل ASCII و نه با memory layout مربوط به GUID پلتفرم.
تنظیمات اختیاری
| فیلد | پیشفرض | توضیح |
|---|---|---|
protocol | "tcp" | انتخاب VLESS command. نام proto هم پذیرفته میشود. |
verbose | false | جزئیات بیشتری از 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:
- downstream
Estرا به نود قبلی میفرستد - VLESS request header را upstream میفرستد
- payloadهای upstream موجود در صف را بلافاصله پس از header ارسال میکند
برای ارسال byteهای application منتظر response header مربوط به VLESS نمیماند؛ بااینحال body برگشتی تا زمان خواندن و اعتبارسنجی response header نگه داشته میشود.
رفتار UDP در زمان اجرا
در حالت UDP، application line از carrier line جداست:
| Line | هدف |
|---|---|
| UDP app line | line قابل مشاهده برای نود UDP-facing قبلی. |
| TCP carrier line | line داخلی ساختهشده توسط 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 flag | kNodeFlagNone |
| previous node | مجاز، و در استفاده عادی لازم |
| next node | مجاز، و در استفاده عادی لازم |
| layer group | kNodeLayerAnything |
required_padding_left | 2 |
| line state | target 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 تکیه نکنید.