Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

proxy and socks5

Two ways to reach a peer through something else. Both are layers, both dial the proxy as their transport and carry the real destination themselves, and both are transparent once the tunnel is open: after the handshake the bytes are the target’s.

$ tocat - proxy:proxy.corp:8080:api.example.com:443
$ tocat - socks5:127.0.0.1:1080:api.example.com:443

The address is proxyhost:port:targethost:port. The first two components are the transport, which is what a scheme’s address has always meant here, and everything after is the target.

OptionDescription
target=HOST:PORTThe destination, if you would rather not write it in the address
proxy-auth=USER:PASSCredentials, inline
proxy-auth-file=PATHCredentials from a file, first line
proxy-auth-env=NAMECredentials from an environment variable
resolve-locallysocks5 only. Resolve the target here instead of at the proxy

Naming more than one credential source is an error rather than a precedence rule. Both layers are client only: a listening endpoint has nobody to ask for a tunnel, and saying so is a parse error rather than a surprise at connect time.

Which one

proxy is HTTP CONNECT: a request, a status line, and then the connection is yours. It works through anything that fronts an HTTP proxy, which in a corporate network is usually what exists.

socks5 sends the target as a name unless you set resolve-locally, so the proxy does the lookup. That is the reason to prefer it where both are available: a host only the proxy’s network can resolve still works, and no lookup for it happens on your side. It also carries the reply codes, so a refusal arrives as “not allowed by ruleset” rather than as a dropped connection.

SOCKS4a is not supported. Everything that speaks it speaks SOCKS5.

Stacking TLS over a tunnel

This is the arrangement the layer model exists for, and the one that has to get the name right: above the tunnel you are talking to the target, so the certificate is checked against the target and not against the proxy.

There is no single scheme for it, because sugar spells one layer. Write the stack:

[sink]
type = "tcp"
addr = "127.0.0.1:1080"

[[sink.layers]]
type = "socks5"
target = "api.example.com:443"

[[sink.layers]]
type = "tls"

Layers are bottom first, so this reads as: dial the proxy, ask it for a route to api.example.com:443, then handshake TLS with api.example.com through it.

Two things that look like bugs

curl -x will not work against a tocat relay. -x tells curl the address is an HTTP proxy, so it sends CONNECT and waits for a 200. A relay answers nothing: it carries bytes to an endpoint that is already decided. If your sink is api.example.com:443, point curl at the relay directly and let it speak the protocol the far side expects.

A name based virtual host needs the name in the request. A relay does not rewrite anything, so a request arriving with Host: 127.0.0.1:12345 reaches the target with that header and gets whatever the target does with an unknown host, often a 404. Send the name you mean:

$ curl -H 'Host: api.example.com' http://127.0.0.1:12345

What is not here yet

proxy offers only basic authentication; digest and NTLM are not supported. socks5 does not do UDP association or BIND, so it tunnels outbound TCP and nothing else. Neither layer can accept a connection, so tocat cannot be a proxy, only use one.