Skip to content

SOCKS5: Fails to check upstream connection prior to reporting success to client. #485

@0typos

Description

@0typos

Describe the bug

SOCKS5 CONNECT responses are not synchronized with the upstream connection outcome.

This can impact multiple proxy clients/tools (for example nc, nmap -sT, and other SOCKS-aware scanners using proxychains), where closed/unreachable targets may appear open or behave as if the connection is established.

This can result in the wstunnel becoming unresponsive and unable to any additional connections to the socks port.

The minimal deterministic reproduction below uses nc only:

  • direct/local nc to a closed port fails immediately (expected)
  • proxychains + nc to the same closed port reports success and/or hangs (unexpected)

To Reproduce

  1. Start server:
    ./target/release/wstunnel server ws://127.0.0.1:18080 --log-lvl=DEBUG
  2. Start client exposing local SOCKS5:
    ./target/release/wstunnel client -R socks5://127.0.0.1:18888 ws://127.0.0.1:18080 --log-lvl=DEBUG
  3. Prepare proxychains config:
    cat >/tmp/proxychains-wstunnel.conf <<'EOF'
    strict_chain
    proxy_dns
    quiet_mode
    tcp_read_time_out 5000
    tcp_connect_time_out 5000
    
    [ProxyList]
    socks5 127.0.0.1 18888
    EOF
  4. Pick a known closed local port (example: 19998) and verify direct nc fails:
    nc -vz 127.0.0.1 19998
  5. Probe the same closed port through SOCKS proxy:
    proxychains4 -f /tmp/proxychains-wstunnel.conf nc -vz 127.0.0.1 19998
  6. Closed port through the proxy appears open.

Expected behavior

  • Direct/local nc to a closed port should fail.
  • Proxied proxychains + nc to the same closed port should also fail.
  • SOCKS5 success reply (REP=0x00) should only be sent after upstream connect succeeds.
  • Closed/unreachable endpoints should return SOCKS5 failure promptly.

Your wstunnel setup

Commit Hash Of Build: 9d0ec3f

Server

./target/release/wstunnel server ws://127.0.0.1:18080 --log-lvl=DEBUG
2026-02-07T03:15:55.137822Z  INFO wstunnel: Starting wstunnel server v10.5.2 with config WsServerConfig { socket_so_mark: SoMark, bind: 127.0.0.1:18080, websocket_ping_frequency: Some(30s), timeout_connect: 10s, websocket_mask_frame: false, restriction_config: None, tls: false, remote_server_idle_timeout: 180s, mTLS: false }
2026-02-07T03:15:55.137887Z DEBUG wstunnel: Restriction rules: RestrictionsRules {
    restrictions: [
        RestrictionConfig {
            name: "Allow All",
            match: [
                Any,
            ],
            allow: [
                Tunnel(
                    AllowTunnelConfig {
                        protocol: [],
                        port: [],
                        host: Regex(
                            "^.*$",
                        ),
                        cidr: [
                            0.0.0.0/0,
                            ::/0,
                        ],
                    },
                ),
                ReverseTunnel(
                    AllowReverseTunnelConfig {
                        protocol: [],
                        port: [],
                        port_mapping: {},
                        cidr: [
                            0.0.0.0/0,
                            ::/0,
                        ],
                        unix_path: Regex(
                            "^.*$",
                        ),
                    },
                ),
            ],
        },
    ],
}
2026-02-07T03:15:55.137987Z  INFO wstunnel::tunnel::server::server: Starting wstunnel server listening on 127.0.0.1:18080
2026-02-07T03:16:06.548640Z  INFO cnx{peer="127.0.0.1:65165"}: wstunnel::tunnel::server::server: Accepting connection
2026-02-07T03:16:06.548865Z  INFO cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::tunnel::server::server: Tunnel accepted due to matched restriction: Allow All
2026-02-07T03:16:06.548895Z  INFO cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::protocols::socks5::tcp_server: Starting SOCKS5 server listening cnx on 127.0.0.1:18888 with credentials None
2026-02-07T03:17:00.742817Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::server: incoming connection from peer 127.0.0.1:65168 @ 127.0.0.1:18888
2026-02-07T03:17:00.742859Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::server: Handshake headers: [version: 5, methods len: 1]
2026-02-07T03:17:00.742870Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::server: methods supported sent by the client: [0]
2026-02-07T03:17:00.742875Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::server: Reply with method 0
2026-02-07T03:17:00.742957Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::server: Request: [version: 5, command: 1, rev: 0, address_type: 1]
2026-02-07T03:17:00.742965Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::util::target_addr: Address type `IPv4`
2026-02-07T03:17:00.742972Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::server: Request target is 127.0.0.1:19998
2026-02-07T03:17:00.742978Z DEBUG cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: fast_socks5::server: Domain won't be resolved because `dns_resolve`'s config has been turned off.
2026-02-07T03:17:00.743024Z  INFO cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::tunnel::server::server: connected to ReverseSocks5 { timeout: Some(30s), credentials: None } 127.0.0.1:19998
2026-02-07T03:17:00.743138Z  INFO cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::tunnel::transport::io: Closing local => remote tunnel
2026-02-07T03:17:00.743167Z  INFO cnx{peer="127.0.0.1:65165"}:tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::tunnel::transport::io: Closing local <= remote tunnel
2026-02-07T03:17:00.743638Z  INFO cnx{peer="127.0.0.1:65170"}: wstunnel::tunnel::server::server: Accepting connection
2026-02-07T03:17:00.743761Z  INFO cnx{peer="127.0.0.1:65170"}:tunnel{id="019c361a-52c7-7f03-9e44-33d5591795da" remote="127.0.0.1:18888"}: wstunnel::tunnel::server::server: Tunnel accepted due to matched restriction: Allow All

Client

./target/release/wstunnel client -R socks5://127.0.0.1:18888 ws://127.0.0.1:18080 --log-lvl=DEBUG
2026-02-07T03:16:06.548182Z  INFO wstunnel: Starting wstunnel client v10.5.2
2026-02-07T03:16:06.548267Z  INFO wstunnel::protocols::tcp::server: Opening TCP connection to 127.0.0.1:18080
2026-02-07T03:16:06.548309Z DEBUG wstunnel::protocols::tcp::server: Connecting to 127.0.0.1:18080
2026-02-07T03:16:06.548482Z DEBUG wstunnel::protocols::tcp::server: Connected to tcp endpoint 127.0.0.1:18080, aborted all other connection attempts
2026-02-07T03:16:06.548550Z DEBUG tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::tunnel::transport::websocket: with HTTP upgrade request Request { method: GET, uri: /v1/events, version: HTTP/1.1, headers: {"host": "127.0.0.1:18080", "upgrade": "websocket", "connection": "upgrade", "sec-websocket-key": "RnDQn67rYpudaI+t2xP8PA==", "sec-websocket-version": "13", "sec-websocket-protocol": "v1, authorization.bearer.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjAxOWMzNjE5LTdmMTQtNzI0MC05MjEyLTNjMjA1NDNkN2U3OSIsInAiOnsiUmV2ZXJzZVNvY2tzNSI6eyJ0aW1lb3V0Ijp7InNlY3MiOjMwLCJuYW5vcyI6MH0sImNyZWRlbnRpYWxzIjpudWxsfX0sInIiOiIxMjcuMC4wLjEiLCJycCI6MTg4ODh9.TWuBtVyMnULgMfLTXWthWNTbfwT2ZV5JQCajp2qDE4s"}, body: Empty }
2026-02-07T03:17:00.743229Z DEBUG tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::tunnel::client::client: Server response: Parts { status: 101, version: HTTP/1.1, headers: {"connection": "upgrade", "upgrade": "websocket", "sec-websocket-accept": "Lajh7apPgvJqsIBZi55BndnNOQk=", "cookie": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIsInAiOnsiVGNwIjp7InByb3h5X3Byb3RvY29sIjpmYWxzZX19LCJyIjoiMTI3LjAuMC4xIiwicnAiOjE5OTk4fQ.t_SQooaArNcATzxti-90sRqGj03Fz4qeo-ReZtTbkbU", "sec-websocket-protocol": "v1", "date": "Sat, 07 Feb 2026 03:17:00 GMT"} }
2026-02-07T03:17:00.743307Z  INFO tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::protocols::tcp::server: Opening TCP connection to 127.0.0.1:19998
2026-02-07T03:17:00.743339Z DEBUG wstunnel::protocols::tcp::server: Connecting to 127.0.0.1:19998
2026-02-07T03:17:00.743421Z DEBUG tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::protocols::tcp::server: Cannot connect to tcp endpoint 127.0.0.1:19998 reason Connection refused (os error 61)
2026-02-07T03:17:00.743443Z ERROR tunnel{id="019c3619-7f14-7240-9212-3c20543d7e79" remote="127.0.0.1:18888"}: wstunnel::tunnel::client::client: Cannot connect to Some(RemoteAddr { protocol: Tcp { proxy_protocol: false }, host: Ipv4(127.0.0.1), port: 19998 }): Cannot connect to tcp endpoint 127.0.0.1:19998 reason Some(Os { code: 61, kind: ConnectionRefused, message: "Connection refused" })
2026-02-07T03:17:00.743505Z  INFO wstunnel::protocols::tcp::server: Opening TCP connection to 127.0.0.1:18080
2026-02-07T03:17:00.743532Z DEBUG wstunnel::protocols::tcp::server: Connecting to 127.0.0.1:18080
2026-02-07T03:17:00.743617Z DEBUG wstunnel::protocols::tcp::server: Connected to tcp endpoint 127.0.0.1:18080, aborted all other connection attempts
2026-02-07T03:17:00.743656Z DEBUG tunnel{id="019c361a-52c7-7f03-9e44-33d5591795da" remote="127.0.0.1:18888"}: wstunnel::tunnel::transport::websocket: with HTTP upgrade request Request { method: GET, uri: /v1/events, version: HTTP/1.1, headers: {"host": "127.0.0.1:18080", "upgrade": "websocket", "connection": "upgrade", "sec-websocket-key": "JomwJ6cEvVzsn7o25R2cpw==", "sec-websocket-version": "13", "sec-websocket-protocol": "v1, authorization.bearer.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjAxOWMzNjFhLTUyYzctN2YwMy05ZTQ0LTMzZDU1OTE3OTVkYSIsInAiOnsiUmV2ZXJzZVNvY2tzNSI6eyJ0aW1lb3V0Ijp7InNlY3MiOjMwLCJuYW5vcyI6MH0sImNyZWRlbnRpYWxzIjpudWxsfX0sInIiOiIxMjcuMC4wLjEiLCJycCI6MTg4ODh9.anawOVjhnlEvxWRnhbBtb-60aKqHjm00H02yKmjx0sg"}, body: Empty }

Non-proxied

nc -vz 127.0.0.1 19998
localhost [127.0.0.1] 19998: Connection refused

Proxychains + Netcat

proxychains4 -f /tmp/proxychains-wstunnel.conf nc -vz 127.0.0.1 19998
[proxychains] config file found: /tmp/proxychains-wstunnel.conf
[proxychains] preloading /opt/homebrew/Cellar/proxychains-ng/4.17/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.17
127.0.0.1 [127.0.0.1] 19998 open

Desktop (please complete the following information):

  • Linux: Fedora 43
  • Mac: Tahoe 26.2

Additional context
This issue was drafted with assistance from ChatGPT 5.3 Codex; I have manually reviewed and tested before submitting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions