Skip to main content

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:

FieldTypeDescription
namestringUser-chosen node name. Must be unique inside the config file.
typestringMust be exactly "TrojanClient".
settingsobjectNon-empty Trojan client settings.
nextstringStream-facing node that carries Trojan bytes. Required in normal use.

Required settings fields:

FieldAccepted namesDescription
password materialpassword, pass, sha224, password-sha224, password_sha224Use either a raw password or a precomputed SHA224 hex digest, not both.
target addresstarget-address, address, targetFinal target host or IP, or "dest_context->address" / "line->dest_ctx->address".
target portportFinal 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

FieldDefaultDescription
protocol"tcp"Selects Trojan command. Also accepts proto.
verbosefalseEnables 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:

ValueBehavior
"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.

ValueBehavior
"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:

  1. sends the Trojan request header upstream
  2. marks the Trojan stream as established
  3. forwards downstream Est to the previous node
  4. flushes queued upstream application payloads
  5. 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:

LinePurpose
UDP app lineThe line visible to the previous UDP-facing node.
TCP carrier lineAn 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 ASSOCIATE request
  • 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

PropertyValue
Node flagkNodeFlagNone
Previous nodeAllowed, required in normal use
Next nodeAllowed, required in normal use
Layer groupkNodeLayerAnything
required_padding_left263
Line statetarget 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 TrojanClient to connect to the Trojan server by itself; put TlsClient and a connector after it.
  • Do not omit TlsClient for 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.