socat (Go)
A Go implementation of socat: a
multipurpose relay for moving data between files, sockets, processes, and
other endpoints.

The command line and address syntax follow classic socat except for the
documented differences below. The project supports Linux, macOS, and Windows.
DTLS is 1.3-only. WebSocket, QUIC, and HTTP/2 and HTTP/3 CONNECT are
Go-specific.
Build
Go 1.27 or newer is required.
make build
Without make:
go build -o socat ./cmd/socat
go build -o filan ./cmd/filan
go build -o procan ./cmd/procan
Usage
socat [options] <address> <address>
socat -V | -h | -hh | -hhh
Each address has the form TYPE:parameters,option=value,.... Use - for
standard input and output.
socat -h lists available address types.
socat -hh lists supported options.
socat -hhh also lists aliases and termios names.
Common flags include -d, -v, -x, -b, -t, -T, -u/-U,
-4/-6/-0, and --statistics. On Linux and macOS, -ly and -lm
send logs to syslog.
The command output is the authoritative feature list for the current
platform.
Examples
Connect standard input and output to a TCP service:
printf 'GET / HTTP/1.0\r\nHost: 127.0.0.1\r\n\r\n' |
./socat - TCP4:127.0.0.1:80
Publish a Unix socket over TCP:
./socat TCP4-LISTEN:5432,reuseaddr,fork \
UNIX-CONNECT:/var/run/postgresql/.s.PGSQL.5432
Expose a TCP service through a Unix socket:
./socat UNIX-LISTEN:/tmp/app.sock,fork,unlink-early,mode=600 \
TCP4:127.0.0.1:8080
Run an interactive program on a pseudo-terminal:
./socat -,pty,cfmakeraw EXEC:'python3 -i',setsid,stderr
Address types
The following groups summarize the implemented address families. Run
socat -h for the exact names and aliases available on your platform.
| Group |
Address types |
| Standard streams and descriptors |
STDIO, STDIN, STDOUT, STDERR, FD; ACCEPT-FD on Linux and macOS |
| Files and local I/O |
OPEN, CREATE, GOPEN, PIPE, FIFO, ECHO, SOCKETPAIR, TEXT, STALL, PTY |
| IP networking |
TCP connect/listen, UDP connect/listen/send/receive/datagram, raw IP; generic SOCKET on Linux and macOS |
| Local networking |
Unix stream/datagram sockets on Linux and macOS; Linux abstract sockets |
| Processes |
EXEC, SYSTEM, SHELL |
| Encryption and proxies |
TLS, DTLS 1.3, HTTP CONNECT, SOCKS4/4A/5, SOCKS5 BIND |
| Go extensions |
WebSocket (WS/WSS), QUIC, HTTP/2 and HTTP/3 CONNECT |
| Linux networking |
SCTP, VSOCK, TUN/TAP, AF_PACKET INTERFACE, POSIX message queues |
Common forms include:
TCP4:host:port TCP4-LISTEN:port
UDP4:host:port UDP4-RECVFROM:port
UNIX-CONNECT:path UNIX-LISTEN:path
TLS:host:port TLS-LISTEN:port
DTLS-CLIENT:host:port DTLS-SERVER:port
WS:host:port WSS-LISTEN:port
QUIC:host:port QUIC-LISTEN:port
EXEC:command SYSTEM:shell-command
OPENSSL-* and SSL-* remain aliases for the corresponding TLS and DTLS
addresses. QUIC is a byte stream over one bidirectional QUIC stream; it is
not HTTP/3. On Linux and macOS, SOCKET-* takes a packed sockaddr, so you
can connect or listen on families other than TCP and UDP.
UDP-LISTEN,fork keeps a session per peer.
Options
Address options are written after the address parameters:
TCP4-LISTEN:8080,reuseaddr,fork,bind=127.0.0.1
Major option groups include:
- listener and socket configuration, timeouts, retry, and peer filters;
- file modes, ownership, locking, seeking, truncation, and unlink behavior;
- process descriptors, PTYs, signals, working directories, and terminal flags;
- IPv4/IPv6, multicast, ancillary data, TUN, and interface settings;
- TLS certificates, verification, protocol versions, ciphers, and SNI;
- proxy, SOCKS, WebSocket, QUIC, and namespace settings;
- transfer conversions such as
cr, crnl, ignoreeof, and readbytes.
Options are advertised only where they are implemented. Unsupported
platform/address combinations are rejected rather than silently ignored.
Use socat -hh instead of this README as the complete option reference.
TLS and QUIC
TLS listeners require a certificate. verify=1 is the default; clients use
the system trust store unless cafile= or capath= is supplied.
# Create a test certificate with an ECDSA P-256 key.
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-sha256 -days 365 -nodes -keyout server.key -out server.crt \
-subj "/CN=server.example" -addext "subjectAltName=DNS:server.example"
# Publish a local TCP service over TLS.
./socat TLS-LISTEN:8443,reuseaddr,fork,cert=server.crt,key=server.key,verify=0 \
TCP4:127.0.0.1:8080
# Connect to it and verify the server certificate.
./socat TCP4-LISTEN:8080,reuseaddr,fork,bind=127.0.0.1 \
TLS:server.example:8443,cafile=server.crt,verify=1
verify=0 disables certificate trust and name checks. On a listener it also
disables client-certificate verification.
QUIC uses TLS 1.3 and otherwise accepts the same certificate and verification
options:
./socat QUIC-LISTEN:4433,reuseaddr,fork,cert=server.crt,key=server.key,verify=0 \
TCP4:127.0.0.1:8080
The default QUIC ALPN is socat. Use alpn= when both endpoints require a
different value.
DTLS 1.3
DTLS encrypts UDP without making delivery reliable or ordered.
OPENSSL-DTLS-CLIENT / OPENSSL-DTLS-SERVER (aliases DTLS-CLIENT /
DTLS-SERVER) accept the usual certificate, bind, retry and peer-filter
options. Both ends verify peers by default, so the server requires a
trusted client certificate.
./socat -T 30 DTLS-SERVER:4433,cert=server.pem,key=server.key,cafile=ca.pem,fork PIPE
./socat -T 30 - DTLS-CLIENT:localhost:4433,cert=client.pem,key=client.key,cafile=ca.pem
dtls-mtu sets the maximum UDP payload (default 1200; range 256–65507).
Byte-stream peers work with the default 8192-byte transfer buffer: writes
split into fitting records and short reads retain tails, separately per
direction. Datagram and unknown peers stay strict: oversized writes fail
and small reads truncate. Size -b and the peer's records accordingly.
- CID and RFC 9853 path validation are negotiated by default. New addresses
must pass the server's peer filters.
dtls-migration=0 disables both.
alpn=protocol optionally selects one application protocol.
- MTU confirmation and upward discovery default on for dedicated clients
with migration enabled, successful unfragmented-send setup and negotiated
CID/RRC. Search starts after the final handshake flight is acknowledged
and stays within
dtls-mtu (default 1200). dtls-unfragmented-probes=0
disables discovery; listeners ignore this option. ICMP Packet Too Big
messages are not used. Datagram writes are never retried. Byte-stream
chunks are split again and retried only after a definite too-large rejection
before transmission, with zero bytes written and a smaller size limit.
Timeouts, partial writes and ambiguous errors are not retried.
handshake-timeout caps negotiation at 30 seconds by default; zero removes
that deadline, but protocol retry limits remain. so-rcvtimeo / rcvtimeo
adds a handshake receive-wait limit (zero or omission disables it).
Received fragments, ACKs and retransmissions restart that wait. Expiry ends
the connection attempt, subject to retry / forever.
- After negotiation, receive timeouts remain retryable. Use
-T to bound an
idle transfer; close alerts can be lost. Use ordinary EXEC, since
EXEC,nofork cannot inherit a plaintext DTLS descriptor.
- Only DTLS 1.3 is negotiated, regardless of a lower
min-version;
max-version below 1.3 is rejected. cipher / ciphers keeps its TLS 1.2
meaning and does not select DTLS 1.3 suites.
Go supplies cryptographic and certificate-policy updates; new algorithms
still require DTLS wire integration. Include the adapted Pion
MIT license, quic-go
MIT license, and
attribution when redistributing that code.
Intentional differences from classic socat
Compatibility is checked against the latest classic socat release and current
master from the official repository. Public
address and option spellings are audited automatically. The
scorecard tracks the classic test.sh suite.
fork sessions use goroutines rather than worker processes.
- On macOS,
UDP-LISTEN,fork,shut-down keeps connected child sockets because
shutdown() requires one. Concurrent peers can therefore have datagrams
delivered to another child and dropped; the default shut-null path uses
the shared-socket peer dispatcher instead. Windows rejects
UDP-LISTEN,fork,shut-down because fork sessions share the listen socket.
- Unknown options, malformed values, and unsupported combinations fail
explicitly instead of becoming no-ops.
-s is accepted as a compatibility no-op; error handling is unchanged and
does not continue after otherwise non-fatal errors.
- DNS overrides use a per-address resolver and never mutate process-global
resolver state.
ai-v4mapped is off unless requested, matching classic runtime behavior
rather than the man-page default. Go dials mapped results as IPv4.
handshake-timeout separately limits TLS, DTLS, WebSocket, proxy, SOCKS, and
QUIC negotiation.
- WebSocket, QUIC, and HTTP/2 and HTTP/3 CONNECT are Go-specific extensions.
- DTLS endpoints split byte-stream input into records that fit
dtls-mtu and
retain record tails for byte-stream output. Datagram input stays strict.
This is an endpoint policy; classic's documented interface asks users to
size DTLS transfers with -b.
- TLS listeners fail immediately when
cert= is missing.
- TLS peer names use
VerifyHostname; empty commonname= skips only the name
check, and capath= loads every parseable certificate file rather than only
OpenSSL hash-named entries.
- Child descriptor remapping happens in the child, so a failed
EXEC cannot
leave the parent process partially remapped.
- Omitted
setpgid, setpgid=0, and setpgid=1 all create a new process
group as documented.
end-close[=<bool>] and the close alias accept omission, 0, and 1 as
documented. Classic C rejects explicit =0 and =1 despite the man page.
- Lock files and unlink-on-close paths are removed only if they still refer to
the object created by this process.
- Boolean unlink options honor
=0; they do not delete merely because the
option was present.
ACCEPT-FD supports fork, range, sourceport, lowport, and tcpwrap
matching classic runtime behavior despite the narrower man page. It is
Linux/macOS only.
TUN,retrieve-vlan is rejected instead of succeeding without restoring VLAN
tags; retrieve-vlan is supported on Linux INTERFACE addresses.
- Windows rejects simultaneously enabled
binary and text modes instead of
relying on an unspecified conversion order.
- Windows uses the Winsock provider's listen queue size and rejects explicit
backlog= because Winsock cannot change it after Go creates the listener.
- Generic ioctl request and integer values outside their 32-bit range are
rejected instead of wrapping into a different operation.
- Multicast membership rejects unresolved interface names instead of falling
back to an unintended interface.
- On macOS, SIGILL follows the Go runtime's fatal-signal behavior rather than
the classic caught-signal exit code.
Unsupported names are omitted from help and rejected if used. They are not
silently emulated with a different protocol.
| Feature |
Status |
| DCCP and UDP-Lite |
Not supported. Both were removed from modern Linux kernels and have no native macOS or Windows equivalent. |
| GNU readline address |
Not implemented. |
| DTLS 1.0/1.2 |
Rejected. DTLS endpoints support only DTLS 1.3, with AES-GCM or ChaCha20-Poly1305 and RSA, ECDSA, Ed25519, or ML-DSA certificates. |
| DSA, SSLv3, and weak TLS ciphers |
Rejected; use current TLS versions and RSA, ECDSA, Ed25519, or ML-DSA keys. |
| OpenSSL engines, FIPS mode, EGD, pseudo-random mode, custom DH parameters, and fragment controls |
Enabling these features is rejected where Go's TLS stack has no equivalent. |
Process-wide setuid, setgid, chroot, and substuser options |
Not implemented because changing credentials or root from a goroutine would affect every session. They require process isolation. |
| Process-global libc resolver flags |
Not implemented. res-nsaddr and res-usevc are supported per address. |
| Read-only, obsolete, or structurally unsafe socket options |
Rejected rather than advertised as setters. This includes get-only socket state and options that require structures the classic integer syntax cannot represent safely. |
ipv6-recverr |
Not advertised because the documented interface is ip-recverr (IP_RECVERR). IPV6_RECVERR is not documented in socat.yo. |
udp-ignore-peerport |
Not implemented because classic documents the name but does not expose or implement it. UDP datagram receive behavior follows the working classic implementation. |
Go's TLS defaults also intentionally keep TLS compression disabled. The
accepted openssl-compress=none spelling can be used by compatible command
lines; enabling compression is rejected.
Environment
The main classic environment inputs are supported, including
SOCAT_DEFAULT_LISTEN_IP, SOCAT_PREFERRED_RESOLVE_IP,
SOCAT_MAIN_WAIT, SOCAT_TRANSFER_WAIT, and SOCAT_FORK_WAIT.
Child processes receive SOCAT_* connection metadata. TLS metadata is exposed
as SOCAT_TLS_*, with SOCAT_OPENSSL_* aliases for compatible scripts.
Testing
make check # platform policy, lint, security, unit, and e2e tests
make test # formatting and unit tests
make e2e # local end-to-end tests
make test-netns-docker # privileged Linux namespace and raw-IP tests
make classic-parity # native Go vs official release and reviewed master
make update-scorecard # Linux: refresh committed privileged-Docker results
make check does not contact repo.or.cz. make classic-parity syncs the
official repository into a gitignored working directory.
Focused raw-IP suites live in internal/xio/privileged (Linux and macOS)
and e2e/privileged (macOS). They need the privileged build tag and
fail without root. Ordinary go test excludes them.
sudo "$(command -v go)" test -race -count=1 -tags=privileged ./internal/xio/privileged
go build ./cmd/socat
sudo env SOCAT="$PWD/socat" "$(command -v go)" test -count=1 -tags=e2e,privileged ./e2e/privileged
Classic test.sh results: testdata/scorecard/README.md.
On Linux with Docker, make update-scorecard refreshes the privileged
baselines.
Examples and benchmarks
- Docker Compose lab: TLS, QUIC, WSS, HTTP, and
SOCKS examples (
make lab).
- Benchmarks: optional loopback comparisons with
classic socat (
make bench).
go run ./scripts/fuzzall: local parser and protocol fuzz campaigns.
Repository layout
cmd/ socat, filan, and procan commands
internal/parse/ address and option parsing
internal/xio/ endpoint implementations
internal/relay/ transfer engine
e2e/ end-to-end tests
examples/lab/ container examples
testdata/scorecard/ classic compatibility results
testdata/bench/ benchmark snapshots
License
MIT. See LICENSE.
This is an independent reimplementation and does not copy classic socat's C
sources.