Skip to main content

TrojanServer

TrojanServer is the server-side Trojan protocol tunnel for WaterWall. It parses a Trojan byte stream from the previous node, authenticates the standard SHA224 password hash, extracts the requested TCP or UDP destination, and forwards accepted traffic to its configured next node.

The node supports local password allowlists and AuthenticationClient backed users. It also supports optional fallback for unauthenticated invalid probes.

This current node replaces the older two-node setup that used separate TrojanAuthServer and TrojanSocksServer nodes.

Typical Placement

Normal Trojan server:

TcpListener -> TlsServer -> TrojanServer -> TcpUdpConnector

TCP-only outbound:

TcpListener -> TlsServer -> TrojanServer -> TcpConnector

With routing after authentication:

TcpListener -> TlsServer -> TrojanServer -> Router

TrojanServer does not terminate TLS by itself. For public Trojan deployments, place TlsServer before it. Without TLS, the password hash and destination metadata are visible on the wire.

Minimal Local-Password Example

[
{
"name": "tls-in",
"type": "TlsServer",
"settings": {
"cert-file": "/etc/waterwall/fullchain.pem",
"key-file": "/etc/waterwall/privkey.pem"
},
"next": "trojan-server"
},
{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"password": "secret-password",
"connect": true,
"udp": true,
"verbose": false
},
"next": "outbound"
},
{
"name": "outbound",
"type": "TcpUdpConnector",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port"
}
}
]

Multiple Local Users

{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"users": [
"secret-password",
{
"username": "alice",
"password": "another-secret"
}
],
"connect": true,
"udp": true
},
"next": "outbound"
}

In local allowlist mode, TrojanServer stores the matched raw password on the line so Router can match password rules. If the matching object has username, that username is also stored on the line for username-aware routing.

AuthenticationClient Mode

{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"auth-client-node-name": "auth-client",
"connect": true,
"udp": true,
"verbose": false
},
"next": "outbound"
}

In this mode, the configured AuthenticationClient is the authority. Store each user's plaintext Trojan password in the user object's password field; AuthenticationClient prepares the SHA224 lookup data internally. The user object's name is an account name, not the Trojan password itself.

When auth-client-node-name is configured, local password settings are rejected. TrojanServer internally inserts a UserController before the configured outbound node, so do not place a manual UserController directly after it.

Fallback Example

{
"name": "trojan-server",
"type": "TrojanServer",
"settings": {
"password": "secret-password",
"fallback-node-name": "fallback-service",
"fallback-intentional-delay-ms": 7,
"fallback-intentional-delay-jitter-ms": 1,
"connect": true,
"udp": true
},
"next": "outbound"
}

Fallback is for active-probing resistance. Invalid unauthenticated traffic can be handed to another configured node instead of being closed immediately.

Required Fields

Top-level fields:

FieldTypeDescription
namestringUser-chosen node name. Must be unique inside the config file.
typestringMust be exactly "TrojanServer".
settingsobjectNon-empty Trojan server settings.
nextstringRequired. Accepted Trojan traffic is forwarded to this node.

Choose exactly one authentication mode.

Local Authentication Settings

Use local authentication by omitting auth-client-node-name and configuring at least one raw password.

FieldTypeDescription
passwordstringOne raw Trojan password.
passstringAlias for password. Use either password or pass, not both.
passwordsarray of stringsMultiple raw Trojan passwords.
usersarrayPassword strings or objects with password or pass. Objects can include username.
clientsarrayAlias for users. Use either users or clients, not both.

Password strings must be non-empty. Duplicate local passwords are fatal configuration errors, even when the duplicate entries use different usernames.

Database Authentication Settings

FieldTypeDescription
auth-client-node-namestringName of an existing AuthenticationClient node in the same config file.

The referenced node must exist, must be an AuthenticationClient, and must not point back to the TrojanServer node itself. In database mode, do not configure password, pass, passwords, users, or clients.

Optional Settings

FieldDefaultDescription
connecttrueEnables Trojan TCP CONNECT, command 0x01.
udptrueEnables Trojan UDP ASSOCIATE, command 0x03.
verbosefalseEnables focused authentication diagnostics.
fallback-node-namenot setFallback branch for unauthenticated invalid probes. Aliases: fallback-node, fallback.
fallback-intentional-delay-ms7Delay for upstream payloads sent to fallback. Set 0 to disable. Must not be negative.
fallback-intentional-delay-jitter-ms1Random jitter around delayed fallback payload scheduling. Ignored when delay is 0. Must not be negative.
sweep-interval-ms1000Optional setting forwarded to the internally created UserController in database mode.

At least one of connect or udp must be enabled.

Request Format

Accepted 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
body: TCP stream bytes or Trojan UDP packets

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

Trojan UDP packet format:

destination:  ATYP + address + port
length: 2 bytes big-endian
separator: CRLF
payload: length bytes

Authentication Flow

Trojan clients send hex(SHA224(password)) as 56 ASCII hex bytes. TrojanServer decodes that value and authenticates it against the selected mode.

In local mode, the server precomputes SHA224 for each configured raw password. In database mode, it uses the AuthenticationClient SHA224 lookup API and records the returned user handle on the line after the request is parsed.

The complete 56-byte password hash must be present in the first upstream payload callback. If the first payload does not contain the full hash, the line is treated as an invalid unauthenticated probe. With fallback configured, those bytes go to fallback; otherwise the line is closed. Later request fields, including the CRLF, command, destination, and early body bytes, may still arrive across later payload callbacks after the complete password hash is present.

There is no unauthenticated Trojan mode.

TCP CONNECT Runtime Behavior

For command 0x01, the server:

  1. authenticates the password hash
  2. parses the requested destination
  3. writes that destination into line->routing_context.dest_ctx
  4. records local or database credentials on the line for downstream routing
  5. initializes the configured next node
  6. forwards any TCP body bytes that arrived in the same payload as the request header
  7. forwards downstream Est only after the selected outbound path establishes

The configured next node is usually TcpConnector, TcpUdpConnector, Router, or another node that understands the destination context.

UDP ASSOCIATE Runtime Behavior

For command 0x03, the initial request address is only the association metadata. The real remote destination is carried by each Trojan UDP packet.

For each unique UDP packet destination, TrojanServer creates or reuses one internal backend UDP line. That backend line gets the packet destination in line->routing_context.dest_ctx, carries the authenticated user credentials, and forwards the raw UDP payload to the configured next node.

Replies from backend UDP lines are wrapped back into Trojan UDP packet framing and sent downstream to the original Trojan client stream. One Trojan UDP association can therefore talk to multiple remote UDP endpoints.

Malformed UDP packet headers, zero-length domain names, missing ports, bad CRLF separators, or UDP payloads larger than 8192 bytes close the affected Trojan line. Pending downstream reply data is capped at 1 MiB.

Fallback Behavior

Fallback is used only before successful password authentication. After authentication succeeds, malformed Trojan protocol data closes the line instead of being replayed to fallback.

Fallback may be selected for:

  • invalid password prefix bytes
  • a password hash split across the first upstream payload
  • invalid password CRLF before authentication
  • invalid password hex before authentication
  • failed password lookup
  • AuthenticationClient not being ready
  • an incomplete unauthenticated prefix that grows beyond the initial-buffer limit

When fallback is selected:

  • the fallback branch receives Init immediately
  • saved initial bytes and later upstream payloads are forwarded to fallback
  • upstream fallback payloads can be delayed by fallback-intentional-delay-ms plus jitter
  • an upstream Finish waits until delayed fallback payloads have been delivered
  • downstream fallback responses are not intentionally delayed

The default fallback delay is intentionally small. Tune delay and jitter against the public behavior the fallback is supposed to resemble; they are mitigations, not proof of timing indistinguishability.

Finish Behavior

TrojanServer destroys its own line state before propagating real Finish callbacks.

The client stream line is created by the previous adapter and is not destroyed by TrojanServer. Internal UDP backend lines are created by TrojanServer; it owns those lines and destroys them safely when the client stream closes or when a backend UDP line closes.

Padding

TrojanServer may prepend the largest possible Trojan UDP reply header:

required_padding_left = 263

That covers ATYP + domain length + 255-byte domain + port + payload length + CRLF.

Node Metadata

PropertyValue
Node flagkNodeFlagChainHead
Previous nodeAllowed, required in normal use
Next nodeRequired
Layer groupkNodeLayerAnything
required_padding_left263
Line stateauthentication state, phase, read stream, pending queues, fallback state, UDP backend-line map

Unsupported Features

This implementation does not create or manage:

  • TLS termination
  • WebSocket, HTTP, gRPC, or other transport wrappers
  • legacy TrojanAuthServer plus TrojanSocksServer composition
  • non-standard Trojan extensions

Use separate WaterWall nodes such as TlsServer, HttpServer, MuxServer, TcpUdpConnector, or Router when you need those layers.

Common Mistakes

  • Do not expose TrojanServer directly on a public TCP listener without TlsServer unless you intentionally want plain Trojan on the wire.
  • Do not configure local passwords together with auth-client-node-name.
  • Do not set the next node to a manual UserController; database mode inserts its own internal UserController.
  • Do not use the old TrojanAuthServer and TrojanSocksServer node names for new configurations.
  • Do not expect the initial UDP ASSOCIATE address to choose the final UDP target; each Trojan UDP packet carries its own target address.