Socks5Server
Socks5Server is the server side of SOCKS5 inside a Waterwall chain. It accepts SOCKS5 control traffic from the
previous node, performs method negotiation, authenticates through an existing AuthenticationClient node or explicit
no-auth, and then maps SOCKS5 requests into Waterwall traffic.
It supports:
- SOCKS5 method negotiation
- username/password authentication through
AuthenticationClient CONNECTUDP ASSOCIATE- authenticated UDP relay associations
It rejects BIND.
Typical placement
TCP-only SOCKS5 server:
TcpListener -> Socks5Server -> TcpConnector
SOCKS5 server with UDP support:
TcpListener -> Socks5Server -> TcpConnector
UdpListener -> Socks5Server -> UdpConnector
The TCP listener accepts the SOCKS5 control connection. The UDP listener receives SOCKS5 UDP datagrams after the client has created a valid UDP association over the TCP control connection.
Do not manually place UserController after an authenticated Socks5Server. Authenticated mode creates and inserts an
internal UserController before the configured next node.
Authenticated example
{
"name": "socks-server",
"type": "Socks5Server",
"settings": {
"auth-client-node-name": "auth-client",
"connect": true,
"udp": true,
"ipv4": "0.0.0.0",
"sweep-interval-ms": 1000,
"verbose": false
},
"next": "outbound"
}
auth-client must be an existing node of type AuthenticationClient in the same configuration. Socks5Server uses
that node for lookups; it does not create the authentication client.
No-auth example
{
"name": "socks-server",
"type": "Socks5Server",
"settings": {
"no-auth": true,
"connect": true,
"udp": false
},
"next": "outbound"
}
Exactly one authentication mode must be selected: either auth-client-node-name or no-auth: true.
Required fields
| Field | Type | Description |
|---|---|---|
settings | object | Required and must not be empty. |
settings.auth-client-node-name | string | Required unless no-auth is explicitly true. Must point to an existing AuthenticationClient node and must not point back to the same Socks5Server. |
settings.no-auth | boolean | Required only when no authentication client is configured. Must be true to enable no-auth mode. |
settings.ipv4 / settings.udp-ipv4 | string | Required when udp is enabled. IPv4 address returned in the UDP ASSOCIATE reply. |
next | string | Required. Points to the real outbound path. |
At least one of connect or udp must be enabled.
Optional fields
| Field | Default | Description |
|---|---|---|
settings.connect | true | Enables SOCKS5 CONNECT. |
settings.udp | false | Enables SOCKS5 UDP ASSOCIATE and UDP datagram handling. |
settings.verbose | false | Enables extra logging, including authentication rejection reasons. |
settings.sweep-interval-ms | 1000 | Forwarded to the internal UserController in authenticated mode. |
The old inline authentication settings username, password, users, and accounts are no longer accepted. If any of
those keys are present, node creation fails. Use AuthenticationServer plus AuthenticationClient, or use explicit
no-auth: true.
Authentication model
When authenticated mode is enabled, the SOCKS5 client must offer username/password authentication. Socks5Server
receives the SOCKS username and password, builds one lookup string in this exact form:
username:password
It then calls the configured AuthenticationClient and checks that combined string as the user's password.
For AuthenticationServer users intended for SOCKS5, store the SOCKS credential pair in the user object's password
field using the exact username:password form. The user object's name is not used for the SOCKS5 wire username; it can
remain operator-facing metadata.
After successful authentication:
- the returned
user_handle_tis stored inSocks5Serverline state - the handle is added to the Waterwall line with
lineAddUser() - the raw SOCKS username/password are also copied to the line as a credential marker for downstream username/password routing rules
- the internal
UserControllerenforces the user's live connection, IP, traffic, expiry, and enabled-state limits before the line reaches the configurednextnode
In no-auth mode, the line receives an empty anonymous handle marker.
SOCKS usernames and passwords must be non-empty in authenticated mode. Embedded NUL bytes are rejected because the authentication lookup key is passed as a C string.
CONNECT behavior
After method negotiation and authentication, a CONNECT request is handled as a normal Waterwall stream path:
- The requested SOCKS destination is copied into
line->dest_ctx. - The destination protocol is set to TCP.
- Upstream
Initis sent to the next node. - Upstream and downstream payload may be queued while the next side establishes.
- When downstream
Estarrives from the next side,Socks5Serversends a SOCKS5 success reply to the client. - It then emits downstream
Esttoward the previous node and flushes queued payload.
If connect is disabled, CONNECT requests are rejected with command not supported.
UDP ASSOCIATE behavior
UDP ASSOCIATE creates an association that is owned by the TCP control line. It does not open a normal upstream stream
from the control line.
The UDP associate reply uses:
- the configured
settings.ipv4orsettings.udp-ipv4as the reply address - the local TCP listener port that accepted the control connection as the reply port
For example, if the TCP control connection was accepted on local port 443 and ipv4 is 0.0.0.0, the reply tells the
client to send SOCKS UDP datagrams to 0.0.0.0:443.
The request's UDP peer hint is used when present. If the hint contains a concrete IP and port, the association is keyed to that source. If the hint uses an any-address, the TCP control connection source IP is used.
UDP security model
The UDP listener is not an open proxy. A UDP datagram is accepted only when:
- the datagram sender matches a registered association
- the association was created by a successfully negotiated TCP control line
- the association entry still exists
When the TCP control line closes, its UDP association is unregistered immediately. Later UDP datagrams for that association are rejected.
Associations are stored in a 64-shard registry on the Socks5Server tunnel state. Registry entries store copied
metadata only:
- a generation token
- the owner worker id
- the authenticated
user_handle_t - copied raw username/password strings, when authenticated mode is used
They do not store a usable line_t *. The generation token prevents an older closing control line from deleting a newer
association that reused the same key.
UDP payload behavior
When a SOCKS UDP datagram arrives:
- The server looks up the matching association.
- It validates the SOCKS5 UDP header.
- If
FRAG != 0, it ignores the datagram conservatively. - It parses the requested remote destination from the header.
- It creates or reuses an internal backend UDP line for that destination.
- It strips the SOCKS5 UDP header and sends only the payload upstream through that backend line.
When a reply comes back from the next node:
- The payload is wrapped in a SOCKS5 UDP response header.
- The response header source address is the remote destination represented by the backend UDP line.
- The wrapped datagram is sent downstream toward the UDP listener/client side.
The internal backend UDP lines are normal Waterwall lines created behind the client-facing UDP side. This keeps the client-facing association line separate from per-remote outbound destinations and preserves normal line lifecycle rules.
Finish behavior
The implementation follows Waterwall's close ordering:
- local line state is destroyed before real
Finishpropagation - the control line unregisters its UDP association before it can die
- internal backend UDP lines detach from the client line before closing
- final SOCKS5 failure replies, when needed before establishment, are sent through a close path that guards against re-entrant callbacks
Node metadata
| Property | Value |
|---|---|
| Node flag | kNodeFlagChainHead |
| Previous node | Allowed |
| Next node | Required |
| Layer group | kNodeLayerAnything |
required_padding_left | Worst-case SOCKS5 UDP header: 4 + 1 + 255 + 2 bytes |
The padding requirement lets UDP replies prepend SOCKS5 datagram headers while preserving Waterwall buffer-padding assumptions. If a reply buffer lacks enough left capacity, the tunnel allocates a replacement buffer and copies the payload before adding the header.
Common mistakes
- Do not configure
username,password,users, oraccounts; those legacy keys are rejected. - Do not configure both
auth-client-node-nameandno-auth: true. - Do not enable UDP without
ipv4orudp-ipv4. - Do not manually place
UserControllerimmediately after an authenticatedSocks5Server; it is inserted internally. - Do not expect SOCKS5
BINDor UDP fragmentation reassembly.