TrojanClient
TrojanClient is the client-side Trojan protocol tunnel for WaterWall. It writes the standard Trojan password hash,
request command, and destination header, then forwards application traffic through the next stream-facing node.
The node supports Trojan TCP CONNECT and Trojan UDP UDP ASSOCIATE. It does not create TLS or open the remote server
connection by itself.
Typical Placement
TCP Trojan client:
TcpListener -> TrojanClient -> TlsClient -> TcpConnector
UDP Trojan client:
UdpListener -> TrojanClient -> TlsClient -> TcpConnector
Dynamic destination from a proxy or router:
Socks5Server -> TrojanClient -> TlsClient -> TcpConnector
SniffRouter -> TrojanClient -> TlsClient -> TcpConnector
TcpConnector normally connects to the Trojan server address and port. The real target service is encoded inside the
Trojan request or inside each Trojan UDP packet.
TCP Example
[
{
"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
}
}
]
This sends a Trojan request shaped as:
hex(SHA224("secret-password")) CRLF
command 01
destination example.com:443
CRLF
Dynamic-Destination Example
{
"name": "trojan-client",
"type": "TrojanClient",
"settings": {
"sha224": "7e240de74fb1ed08fa08d38063f6a6a91462a815c15d3f6abf1d7e0b",
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "dest_context->protocol"
},
"next": "tls-client"
}
Use sha224 when the config should contain the already-computed 56-character SHA224 hexadecimal digest instead of the
raw Trojan password.
UDP Example
{
"name": "trojan-client-udp",
"type": "TrojanClient",
"settings": {
"password": "secret-password",
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "udp"
},
"next": "tls-client"
}
In UDP mode, the previous side remains UDP-facing. TrojanClient creates an internal TCP carrier line, sends a Trojan
UDP ASSOCIATE request over that carrier, and wraps each local UDP payload into Trojan UDP packet framing.
Required Fields
Top-level fields:
| Field | Type | Description |
|---|---|---|
name | string | User-chosen node name. Must be unique inside the config file. |
type | string | Must be exactly "TrojanClient". |
settings | object | Non-empty Trojan client settings. |
next | string | Stream-facing node that carries Trojan bytes. Required in normal use. |
Required settings fields:
| Field | Accepted names | Description |
|---|---|---|
| password material | password, pass, sha224, password-sha224, password_sha224 | Use either a raw password or a precomputed SHA224 hex digest, not both. |
| target address | target-address, address, target | Final target host or IP, or "dest_context->address" / "line->dest_ctx->address". |
| target port | port | Final target port, or "dest_context->port" / "line->dest_ctx->port". Constant ports must be in 1..65535. |
password and pass are raw Trojan passwords. TrojanClient calculates SHA224(password) and sends the lowercase hex
form. The sha224 aliases must already be exactly 56 hexadecimal characters; the node normalizes uppercase hex to
lowercase.
Optional Settings
| Field | Default | Description |
|---|---|---|
protocol | "tcp" | Selects Trojan command. Also accepts proto. |
verbose | false | Enables extra tunnel logging. |
domain-strategy | "do-not-resolve-domains" | Controls whether domain targets are resolved locally before the Trojan request is written. |
Supported protocol values:
| Value | Behavior |
|---|---|
"tcp" or "connect" | Send Trojan command 0x01 for TCP CONNECT. |
"udp" or "udp-associate" | Send Trojan command 0x03 for UDP ASSOCIATE and carry UDP datagrams over an internal TCP carrier line. |
"dest_context->protocol" or "line->dest_ctx->protocol" | Use the incoming destination context protocol. Exact TCP selects 0x01; exact UDP selects 0x03. |
If dest_context->protocol is selected but the incoming protocol is missing, ambiguous, or not TCP/UDP, the client logs
a warning and falls back to TCP.
Domain Strategy
domain-strategy controls the final target address encoded in the Trojan request and in UDP packet headers.
| Value | Behavior |
|---|---|
"do-not-resolve-domains" | Keep domain targets as domains. The Trojan server side resolves the target. |
"resolve-domains-and-accept-dns-returned-order" | Resolve locally and use the first usable DNS result. |
"resolve-domains-and-prefer-ipv4" | Resolve locally, prefer IPv4, and fall back to IPv6. |
"resolve-domains-and-prefer-ipv6" | Resolve locally, prefer IPv6, and fall back to IPv4. |
"resolve-domains-and-use-only-ipv4" | Resolve locally and require an IPv4 result. If none exists, close the line. |
"resolve-domains-and-use-only-ipv6" | Resolve locally and require an IPv6 result. If none exists, close the line. |
"resolve-domains-with-core-settings" | Resolve locally using the dns settings from core.json. |
When local resolution is enabled, TrojanClient creates an internal DomainResolver whose prepare hook applies the
configured target before DNS resolution. Resolution applies when the final Trojan target address is a domain, including
both fixed JSON targets and destination-context targets. Literal IP addresses are left unchanged.
Request Format
Initial Trojan request:
password: 56 ASCII hex bytes, hex(SHA224(password))
separator: CRLF
command: 01 CONNECT or 03 UDP ASSOCIATE
destination: ATYP + address + port
separator: CRLF
Destination format:
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
For UDP mode, the initial UDP ASSOCIATE request uses 0.0.0.0:0 as the associate address. The real remote destination
is carried by each subsequent Trojan UDP packet.
Trojan UDP packet format:
destination: ATYP + address + port
length: 2 bytes big-endian
separator: CRLF
payload: length bytes
TCP Runtime Behavior
On upstream Init, target preparation applies the configured target before the protocol core starts, either through the
internal resolver prepare hook or directly during client init when local DNS resolution is disabled. TrojanClient then
forwards the same line to the next node.
When the downstream transport establishes, TrojanClient:
- sends the Trojan request header upstream
- marks the Trojan stream as established
- forwards downstream
Estto the previous node - flushes queued upstream application payloads
- forwards any downstream bytes that arrived before establishment
Trojan has no success response header, so the client does not wait for one.
UDP Runtime Behavior
In UDP mode, there are two lines:
| Line | Purpose |
|---|---|
| UDP app line | The line visible to the previous UDP-facing node. |
| TCP carrier line | An internal line created by TrojanClient and sent to the next stream-facing node. |
After the carrier establishes:
- the carrier sends the Trojan password and
UDP ASSOCIATErequest - the app line receives downstream
Est - queued local UDP payloads are wrapped and sent through the carrier
- downstream Trojan UDP frames are parsed, stripped, and returned to the app line as raw UDP payloads
Local UDP payloads larger than 8192 bytes are dropped without closing the whole association. Inbound Trojan UDP frames larger than 8192 bytes, malformed address headers, bad CRLF separators, or input buffering above 1 MiB close the affected line.
The client queues up to 1 MiB of upstream application data while waiting for the transport/request path to establish.
Finish Behavior
TrojanClient destroys its own line state before propagating real Finish callbacks. It does not call lineDestroy() on
normal external lines.
In UDP mode, it owns the internal TCP carrier line it created. If the app line or carrier line closes, it closes the paired side and destroys the owned carrier line safely.
Padding
TrojanClient may prepend the largest possible Trojan UDP packet header:
required_padding_left = 263
That covers ATYP + domain length + 255-byte domain + port + payload length + CRLF.
Node Metadata
| Property | Value |
|---|---|
| Node flag | kNodeFlagNone |
| Previous node | Allowed, required in normal use |
| Next node | Allowed, required in normal use |
| Layer group | kNodeLayerAnything |
required_padding_left | 263 |
| Line state | target context, phase, read stream, pending queue, UDP app/carrier links |
Unsupported Features
This implementation does not create or manage:
- TLS
- WebSocket, HTTP, gRPC, or other transport wrappers
- server-side fallback
- non-standard Trojan extensions
Use separate WaterWall nodes such as TlsClient, HttpClient, MuxClient, or TcpConnector when you need those layers.
Common Mistakes
- Do not expect
TrojanClientto connect to the Trojan server by itself; putTlsClientand a connector after it. - Do not omit
TlsClientfor public Trojan deployments unless you intentionally want plain Trojan on the wire. - Do not confuse the Trojan server address with the final target address.
- Do not use UDP mode with a next node that is not stream-facing.