TrojanClient
TrojanClient پیادهسازی سمت client پروتکل Trojan در WaterWall است. hash مربوط به password، command درخواست و header مقصد
را با فرمت استاندارد Trojan مینویسد و سپس ترافیک application را از نود stream-facing بعدی عبور میدهد.
هر دو حالت TCP CONNECT و UDP ASSOCIATE در Trojan پشتیبانی میشوند. خود نود TLS ایجاد نمیکند و اتصال به server راه دور
را هم باز نمیکند.
جایگاه رایج
Trojan client برای TCP:
TcpListener -> TrojanClient -> TlsClient -> TcpConnector
Trojan client برای UDP:
UdpListener -> TrojanClient -> TlsClient -> TcpConnector
مقصد پویا از proxy یا router:
Socks5Server -> TrojanClient -> TlsClient -> TcpConnector
SniffRouter -> TrojanClient -> TlsClient -> TcpConnector
TcpConnector معمولاً به address و port سرور Trojan وصل میشود. مقصد واقعی سرویس در درخواست Trojan یا در هر Trojan UDP
packet قرار میگیرد.
نمونه TCP
[
{
"name": "trojan-client",
"type": "TrojanClient",
"settings": {
"password": "secret-password",
"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": "trojan.example.net",
"verify": true
},
"next": "server-out"
},
{
"name": "server-out",
"type": "TcpConnector",
"settings": {
"address": "trojan.example.net",
"port": 443,
"nodelay": true
}
}
]
این request به این شکل ارسال میشود:
hex(SHA224("secret-password")) CRLF
command 01
destination example.com:443
CRLF
نمونه Dynamic Destination
{
"name": "trojan-client",
"type": "TrojanClient",
"settings": {
"sha224": "7e240de74fb1ed08fa08d38063f6a6a91462a815c15d3f6abf1d7e0b",
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "dest_context->protocol"
},
"next": "tls-client"
}
اگر نمیخواهید password خام در config نوشته شود، از sha224 استفاده کنید. مقدار آن باید digest شانزدهی ۵۶ کاراکتری
SHA224 باشد.
نمونه UDP
{
"name": "trojan-client-udp",
"type": "TrojanClient",
"settings": {
"password": "secret-password",
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "udp"
},
"next": "tls-client"
}
در حالت UDP، نود قبلی همچنان با UDP کار میکند. TrojanClient یک TCP carrier line داخلی میسازد، درخواست Trojan
UDP ASSOCIATE را روی آن میفرستد و هر UDP payload محلی را با framing مخصوص Trojan UDP حمل میکند.
فیلدهای اجباری
فیلدهای top-level:
| Field | Type | توضیح |
|---|---|---|
name | string | نام دلخواه نود؛ باید داخل config یکتا باشد. |
type | string | باید دقیقاً "TrojanClient" باشد. |
settings | object | تنظیمات Trojan client؛ نباید خالی باشد. |
next | string | نود stream-facing که byteهای پروتکل Trojan را حمل میکند؛ در استفاده معمول لازم است. |
فیلدهای اجباری داخل settings:
| Field | نامهای قابل قبول | توضیح |
|---|---|---|
| password material | password, pass, sha224, password-sha224, password_sha224 | یا password خام بدهید یا مقدار hex آماده SHA224؛ استفاده همزمان از هر دو مجاز نیست. |
| target address | target-address, address, target | مقصد نهایی host/IP، یا "dest_context->address" / "line->dest_ctx->address". |
| target port | port | پورت مقصد نهایی، یا "dest_context->port" / "line->dest_ctx->port". پورت ثابت باید در بازه 1..65535 باشد. |
password و pass مقدار خام password در Trojan هستند. TrojanClient مقدار SHA224(password) را محاسبه و بهشکل hex
با حروف کوچک ارسال میکند. مقدار aliasهای sha224 باید دقیقاً ۵۶ کاراکتر hexadecimal باشد؛ حروف بزرگ بهطور خودکار به
حروف کوچک تبدیل میشوند.
تنظیمات اختیاری
| Field | Default | توضیح |
|---|---|---|
protocol | "tcp" | Trojan command را انتخاب میکند. alias آن proto است. |
verbose | false | جزئیات بیشتری از tunnel را log میکند. |
domain-strategy | "do-not-resolve-domains" | مشخص میکند domain مقصد پیش از نوشتن درخواست بهصورت محلی resolve شود یا نه. |
مقادیر قابل قبول protocol:
| Value | رفتار |
|---|---|
"tcp" یا "connect" | Trojan command 0x01 برای TCP CONNECT ارسال میشود. |
"udp" یا "udp-associate" | Trojan command 0x03 برای UDP ASSOCIATE ارسال میشود و UDP datagramها روی carrier داخلی TCP حمل میشوند. |
"dest_context->protocol" یا "line->dest_ctx->protocol" | protocol از destination context ورودی خوانده میشود. TCP دقیق 0x01 و UDP دقیق 0x03 را انتخاب میکند. |
اگر با انتخاب dest_context->protocol، protocol ورودی وجود نداشته باشد، مبهم باشد یا TCP/UDP نباشد، client یک warning در
log مینویسد و TCP را انتخاب میکند.
Domain Strategy
domain-strategy تعیین میکند آدرس نهایی در درخواست Trojan و headerهای UDP packet چگونه نوشته شود.
| Value | رفتار |
|---|---|
"do-not-resolve-domains" | نام domain بدون تغییر میماند تا سمت Trojan server آن را resolve کند. |
"resolve-domains-and-accept-dns-returned-order" | resolve محلی انجام میشود و نخستین پاسخ قابل استفاده DNS انتخاب میشود. |
"resolve-domains-and-prefer-ipv4" | پس از resolve محلی، IPv4 در اولویت است و در صورت نیاز IPv6 استفاده میشود. |
"resolve-domains-and-prefer-ipv6" | پس از resolve محلی، IPv6 در اولویت است و در صورت نیاز IPv4 استفاده میشود. |
"resolve-domains-and-use-only-ipv4" | فقط IPv4 پذیرفته میشود؛ نبودن پاسخ IPv4 باعث بسته شدن line است. |
"resolve-domains-and-use-only-ipv6" | فقط IPv6 پذیرفته میشود؛ نبودن پاسخ IPv6 باعث بسته شدن line است. |
"resolve-domains-with-core-settings" | resolve محلی با تنظیمات dns در core.json انجام میشود. |
با فعال بودن resolve محلی، TrojanClient یک DomainResolver داخلی میسازد. prepare hook آن پیش از DNS lookup، مقصد
تنظیمشده را اعمال میکند. اگر مقصد نهایی Trojan نام domain باشد resolve انجام میشود؛ چه مقصد از JSON آمده باشد و چه از
destination context. آدرسهای IP بدون تغییر میمانند.
فرمت Request
درخواست اولیه Trojan:
password: 56 ASCII hex bytes, hex(SHA224(password))
separator: CRLF
command: 01 CONNECT or 03 UDP ASSOCIATE
destination: ATYP + address + port
separator: CRLF
فرمت destination:
ATYP 01: IPv4 address, 4 bytes, then 2-byte big-endian port
ATYP 03: domain length, domain bytes, then 2-byte big-endian port
ATYP 04: IPv6 address, 16 bytes, then 2-byte big-endian port
در حالت UDP، درخواست اولیه UDP ASSOCIATE از 0.0.0.0:0 بهعنوان associate address استفاده میکند. مقصد واقعی راه دور
داخل هر Trojan UDP packet بعدی حمل میشود.
فرمت Trojan UDP packet:
destination: ATYP + address + port
length: 2 bytes big-endian
separator: CRLF
payload: length bytes
رفتار TCP در زمان اجرا
در upstream Init، مقصد پیش از راهاندازی هسته protocol آماده میشود؛ یا از طریق prepare hook مربوط به resolver داخلی،
یا در صورت غیرفعال بودن DNS محلی مستقیماً در init خود client. سپس TrojanClient همان line را به نود بعدی میفرستد.
وقتی transport سمت downstream established شد، TrojanClient:
- Trojan request header را upstream ارسال میکند
- Trojan stream را established علامت میزند
- downstream
Estرا به نود قبلی میفرستد - payloadهای upstream موجود در صف را ارسال میکند
- byteهای downstream را که پیش از برقراری مسیر رسیدهاند تحویل میدهد
Trojan response header موفقیت ندارد، بنابراین client منتظر چنین پاسخی نمیماند.
رفتار UDP در زمان اجرا
در حالت UDP دو line وجود دارد:
| Line | کاربرد |
|---|---|
| UDP app line | line قابل مشاهده برای نود UDP-facing قبلی. |
| TCP carrier line | line داخلی که TrojanClient میسازد و به نود stream-facing بعدی میفرستد. |
بعد از established شدن carrier:
- carrier، Trojan password و request مربوط به
UDP ASSOCIATEرا ارسال میکند - app line یک downstream
Estدریافت میکند - UDP payloadهای محلی موجود در صف با framing مناسب از carrier عبور میکنند
- Trojan UDP frameهای downstream تجزیه میشوند، header آنها حذف میشود و payload خام UDP به app line برمیگردد
payloadهای محلی UDP بزرگتر از ۸۱۹۲ بایت دور ریخته میشوند، بیآنکه کل association بسته شود. در مقابل، Trojan UDP frame ورودی بزرگتر از ۸۱۹۲ بایت، header خراب، CRLF نامعتبر یا عبور حجم buffer ورودی از 1 MiB باعث بسته شدن line مربوطه است.
client هنگام انتظار برای برقرار شدن مسیر transport/request، حداکثر 1 MiB از داده upstream application را در صف نگه میدارد.
رفتار Finish
TrojanClient پیش از فرستادن Finish واقعی، line state خود را از بین میبرد. برای lineهای معمولی بیرونی هم
lineDestroy() را فراخوانی نمیکند.
در حالت UDP، مالک TCP carrier line داخلیای است که خودش ساخته است. بسته شدن app line یا carrier line باعث بسته شدن سمت مقابل و از بین رفتن امن carrier line میشود.
Padding
TrojanClient ممکن است بزرگترین header ممکن برای Trojan UDP packet را به ابتدای payload اضافه کند:
required_padding_left = 263
این مقدار ATYP + domain length + 255-byte domain + port + payload length + CRLF را پوشش میدهد.
Metadata نود
| Property | Value |
|---|---|
| Node flag | kNodeFlagNone |
| Previous node | مجاز، در استفاده عادی required |
| Next node | مجاز، در استفاده عادی required |
| Layer group | kNodeLayerAnything |
required_padding_left | 263 |
| Line state | target context، phase، read stream، pending queue، لینکهای UDP app/carrier |
قابلیتهای پشتیبانینشده
این پیادهسازی موارد زیر را ایجاد یا مدیریت نمیکند:
- TLS
- WebSocket، HTTP، gRPC یا transport wrapperهای دیگر
- fallback سمت server
- extensionهای غیر استاندارد Trojan
وقتی به این layerها نیاز دارید، از نودهای جداگانه WaterWall مثل TlsClient، HttpClient، MuxClient یا
TcpConnector استفاده کنید.
اشتباههای رایج
- انتظار نداشته باشید
TrojanClientخودش به سرور Trojan وصل شود؛ بعد از آنTlsClientو connector بگذارید. - در deployment عمومی Trojan،
TlsClientرا حذف نکنید مگر اینکه عمداً Trojan را بهصورت plain روی wire بخواهید. - آدرس سرور Trojan را با آدرس مقصد نهایی اشتباه نگیرید.
- حالت UDP را با نود بعدی غیر stream-facing استفاده نکنید.