Skip to main content

Socks5Client

Socks5Client is a stream middle tunnel that speaks the client side of the SOCKS5 protocol. It connects to the next transport as a SOCKS proxy connection, negotiates the SOCKS5 method, optionally authenticates with username/password, and then sends either a CONNECT or UDP ASSOCIATE request for the configured final target.

The target configured on Socks5Client is the final destination that should appear inside the SOCKS5 request. The address of the SOCKS proxy itself belongs to the next connector, usually TcpConnector for TCP-only use or TcpUdpConnector when UDP relay traffic is needed.

Typical placement​

TCP proxying:

TcpListener -> Socks5Client -> TcpConnector

TCP and UDP proxying through the same SOCKS server:

TcpUdpListener -> Socks5Client -> TcpUdpConnector

In the second topology, the combined connector is expected to reach the SOCKS proxy over TCP for the control connection and UDP for the relay address returned by UDP ASSOCIATE.

Basic example​

This example preserves the incoming destination from line->dest_ctx and asks the SOCKS proxy to connect to that same destination.

{
"name": "socks-client",
"type": "Socks5Client",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "tcp",
"user": "alice",
"pass": "secret",
"domain-strategy": "do-not-resolve-domains",
"verbose": false
},
"next": "proxy-transport"
}

proxy-transport should be the node that connects to the SOCKS5 server, for example a TcpConnector configured with the proxy server address and port.

Dynamic protocol example​

Use dest_context->protocol when the incoming line may already be marked as TCP or UDP and the SOCKS client should follow that protocol.

{
"name": "socks-client",
"type": "Socks5Client",
"settings": {
"address": "dest_context->address",
"port": "dest_context->port",
"protocol": "dest_context->protocol"
},
"next": "proxy-transport"
}

If the incoming destination context is exactly TCP, Socks5Client sends CONNECT. If it is exactly UDP, it negotiates UDP ASSOCIATE. If the protocol flags are missing, ambiguous, or not TCP/UDP, it logs a warning and falls back to TCP.

Fixed target example​

{
"name": "socks-client",
"type": "Socks5Client",
"settings": {
"address": "example.com",
"port": 443,
"protocol": "tcp",
"domain-strategy": "resolve-domains-and-prefer-ipv4"
},
"next": "proxy-transport"
}

With local domain resolution enabled, the internal DomainResolver resolves example.com before the SOCKS request is sent. If an IPv4 answer is available, the SOCKS request carries the IPv4 address. Otherwise it falls back according to the selected strategy.

Required fields​

FieldTypeDescription
settingsobjectRequired and must not be empty.
settings.address / settings.target-address / settings.targetstringFinal SOCKS destination address. Use a literal IP, a domain name of 1 to 255 bytes, dest_context->address, or line->dest_ctx->address. Longer domain names are rejected during node creation.
settings.portnumber or stringFinal SOCKS destination port. Use a number from 1 to 65535, dest_context->port, or line->dest_ctx->port.
nextstringRequired. Points to the transport that reaches the SOCKS proxy.

Optional fields​

FieldDefaultDescription
settings.protocol / settings.prototcpSOCKS command source. Supports tcp, connect, udp, udp-associate, dest_context->protocol, and line->dest_ctx->protocol.
settings.user / settings.usernameunsetSOCKS5 username. Must be provided together with pass/password.
settings.pass / settings.passwordunsetSOCKS5 password. Must be provided together with user/username.
settings.domain-strategydo-not-resolve-domainsControls whether domain targets are resolved locally before the SOCKS request is encoded.
settings.verbosefalseEnables extra debug logging.

Credentials must be non-empty and no longer than 255 bytes each. If only one side of the username/password pair is configured, node creation fails.

Domain strategy​

Socks5Client prepares the configured target before the SOCKS protocol code runs and keeps the selected protocol for the real client line.

When domain-strategy enables local resolution, the client creates an internal DomainResolver whose prepare hook applies the configured target before DNS resolution. Resolution applies to fixed domain targets and to targets copied from dest_context->address. Literal IP addresses are left unchanged.

Supported values:

ValueBehavior
do-not-resolve-domainsKeep domain names as SOCKS5 domain addresses. The SOCKS server side resolves them.
resolve-domains-with-core-settingsUse the DNS preference configured in core.json.
resolve-domains-and-accept-dns-returned-orderResolve locally and use the first usable DNS result.
resolve-domains-and-prefer-ipv4Prefer IPv4, fall back to IPv6.
resolve-domains-and-prefer-ipv6Prefer IPv6, fall back to IPv4.
resolve-domains-and-use-only-ipv4Use only IPv4 answers; close the line if none are available.
resolve-domains-and-use-only-ipv6Use only IPv6 answers; close the line if none are available.

Initialization direction​

Application lines enter Socks5Client through upstream Init. Downstream Init is disabled for every line role, including UDP application, control, and relay lines; receiving it logs a fatal diagnostic and terminates the process through abortProgramNow(1). Downstream Est and payload callbacks remain supported for proxy replies and relay data.

TCP behavior​

For TCP mode, the lifecycle is:

  1. Upstream Init initializes Socks5Client line state and forwards Init to the next transport.
  2. The next transport establishes the proxy connection; its downstream Est is forwarded immediately, before SOCKS negotiation.
  3. Socks5Client sends the SOCKS5 method greeting once. Repeated transport Est notifications continue downstream toward the application without repeating the greeting.
  4. If credentials are configured and the server selects username/password auth, it sends the username/password request.
  5. It sends a SOCKS5 CONNECT command for the configured target.
  6. A successful command reply opens the application-data gate; it does not emit another Est.
  7. The pending upstream FIFO drains without waiting for Resume; nested input follows older bytes.

TCP Pause/Resume notifications pass through immediately, including duplicates and notifications during negotiation. The greeting and ready application/reply output do not wait for Resume. A receive Pause reaches the next adapter even during negotiation, so its issuer must arrange Resume or close.

After successful TCP negotiation, all retained input/output has drained and the local source hold is released, the client destroys its line state and permanently bypasses itself. Later callbacks pass directly between neighbors. Already-running callbacks check the active mask before using state that a nested exchange could retire.

If upstream payload arrives before the SOCKS request succeeds, it is queued. Retained upstream payload is limited to 2 MiB and 1,024 buffers.

The 4,096-byte bound applies only to incomplete reply metadata; a completed reply may arrive with a larger application body. Nested reply processing is serialized, with response bytes kept in FIFO order. Necessary retained response input shares a 2 MiB / 1,024-buffer downstream budget and drains after older reply input, including through Pause; ready TCP forwarding stays synchronous. Empty proxy TCP input is ignored, and unused TCP tails on an established UDP control line are discarded. Ordering queues allocate backing storage on first use; UDP application and relay lines do not allocate handshake stream storage.

If the proxy rejects all methods, authentication fails, the command reply is invalid, or the command fails, the tunnel closes the line.

UDP behavior​

For UDP mode, the application line represents the user-facing UDP flow, while Socks5Client creates two internal lines:

Internal linePurpose
UDP control lineA TCP line that connects to the SOCKS proxy and sends UDP ASSOCIATE. The request target is 0.0.0.0:0.
UDP relay lineA UDP line that connects to the relay address returned by the SOCKS proxy.

The relay line uses the BND.ADDR and BND.PORT in the successful UDP ASSOCIATE reply. It does not reuse the TCP control port or overwrite that negotiated endpoint with the next connector's static SOCKS-proxy address and port. A server can therefore return a dedicated ephemeral relay port; firewall and NAT policy must allow the returned endpoint, not merely the TCP control port.

This negotiated authority survives a Router or sniffer clearing observed application-protocol metadata. The outbound UDP adapter consumes it exactly once, then clears the internal marker so it cannot affect later unrelated routing.

The control transport Est maps once to the application line. Datagram delivery still waits for both the successful control association and UDP relay readiness, under the 2 MiB / 1,024-buffer pending limit.

When application payload goes upstream, Socks5Client prepends a SOCKS5 UDP request header containing the configured final target and sends the datagram through the UDP relay line. When a relay datagram comes back downstream, it validates and strips the SOCKS5 UDP header before forwarding only the payload toward the previous node.

SOCKS5 UDP fragmentation is not reassembled. Datagrams with FRAG != 0 are ignored conservatively.

Common mistakes​

  • Do not put the SOCKS proxy address in settings.address; that field is the final target inside the SOCKS request.
  • Do not use UDP mode with a TCP-only next connector. UDP mode needs a next side that can create the TCP control line and the UDP relay line.
  • Do not expect domain-strategy to change literal IP targets. It only affects domain targets.
  • Do not omit port when using dest_context->address; address and port are selected independently.

Splice Support​

Socks5Client supports splice-backed TCP payloads. During method, authentication and command negotiation, complete proxy-reply deliveries are materialized with sbufEnsureOrdinary() before parser or nested-input admission. After negotiation, TCP payloads and necessary opaque FIFO entries can retain their original buffers. The incomplete-handshake and application-backlog limits remain separate.

UDP application, TCP control and UDP relay lines request ordinary reads in both directions for their lifetimes. UDP target preparation sets the application preference before local DNS can retain input. Preferences are advisory and do not propagate between associated lines; every UDP datagram is still materialized before application retention, header decoding or framing. Ordinary input passes through the helper unchanged. TCP CONNECT lines do not request ordinary reads.

The internal DomainResolver remains splice-capable. Actual activation requires an eligible finalized chain and misc.splice; a TLS or other blocking node still disables splice for the entire payload chain.

Node Metadata​

The padding requirement lets UDP mode prepend SOCKS5 datagram headers without breaking Waterwall buffer-padding assumptions. If an incoming buffer still lacks enough left capacity, the tunnel allocates a replacement buffer and copies the payload before adding the header.

Source-backed metadata:

PropertyValue
node flagskNodeFlagSupportsSplice
can_have_prevtrue
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayer4
layer_group_next_nodekNodeLayer4
required_padding_left262 bytes

Transport Est does not mean SOCKS negotiation has succeeded. Application payload can be accepted after Init; the real protocol backlog is bounded at 2 MiB and 1,024 buffers. TCP greeting and ready application/reply output do not wait for Resume. The first retained application input pauses its source; the source resumes when the older FIFO drains. This local protocol hold is independent of transparently relayed transport pressure. TCP receive Pause also reaches the next adapter during negotiation; its issuer must arrange Resume or close. For UDP, control and relay write pressure combine; one Resume cannot clear the other's outstanding Pause. UDP application backlog release and the owned control greeting continue to honor that boundary pressure, and application receive pressure is mapped to the relay line after its Init. UDP application, control and relay lines remain active for framing, association ownership and pressure mapping.