Stupid Network Tricks Using socat

My dad is a do-it-yourselfer, so he has a lot of tools. “The right tool makes all the difference,” he would often say. Sometimes I think he was just trying to justify purchasing some rarely-used tool. But he also wasn’t wrong. This is how I feel about socat. It is specialized yet has a general purpose at the same time. It’s not a tool I directly use daily — but when I need it, it’s incredibly useful.

Origins of socat

The name “socat” comes from mashing together “socket” and “cat”. “Socket” refers to networking, and “cat” refers to the classic Unix command for “concatenating and printing files.”

Think of socat as the Swiss Army™ knife of sockets and streams. If you’ve ever used netcat, it’s a similar idea but much more flexible. The man page for socat summarizes it succinctly as “Multipurpose relay (SOcket CAT)”. And indeed it has many purposes. But if you try to figure them out by reading the man page alone, you’ll probably give up.

On the surface, the syntax for invoking socat is very simple: socat ADDRESS1 ADDRESS2, where each of those ADDRESS arguments consist of PROTOCOL:flag1,flag2,.... Socat then continuously relays the contents each stream to the other. The complexity lies in figuring out which address type to use and which combination of flags to go with it. It helps to have some examples of real-world use, so here are a few ways I’ve found socat useful.

Port Forwarding

One of the most useful features of most SSH clients is tunneling, which allows you to forward network traffic for a specific port over an encrypted connection. But sometimes encryption is not needed — for example, if you’re forwarding stuff within your local network or within the same host.

Port forwarding is about where socat’s feature list starts.

socat TCP-LISTEN:3000 TCP:remote-host:4000

This is very much like the “local port forwarding” feature of SSH.
Socat will listen for an incoming TCP connection on port 3000 and then relay the stream to remote-host on port 4000. But there is currently a problem. Socat will only handle one connection and then quit. But wait! There are flags for that.

socat TCP-LISTEN:3000,fork TCP:remote-host:4000

With fork socat will now fork itself for each incoming connection, handling connections in a child process while the parent continues to listen.

Voila! Port forwarding without the overhead of encryption or SSH server setup.

Remote Port Forwarding

Notice that you can bridge inbound or outbound connections by mixing and matching the TCP and TCP-LISTEN addresses. This seems like an odd thing to do, until you find yourself trying to access a resource that is behind a firewall. If you can establish an outbound connection from behind the firewall, then you can set up a “remote port forward” using a couple instances of socat.

Say your resource is running inside the firewall on a host called “db-host” and port 5432. On the machine outside the firewall, bond two listeners:

socat TCP-LISTEN:3000,fork TCP-LISTEN:4000,fork

Then on some machine inside the firewall, bond two outbound connections:

socat TCP:db:5432 TCP:external-host:3000

Now when you connect to external-host on port 4000, you’ll find yourself connected to whatever’s running on db-host port 5432!

Stripping SSL

Socat is not limited to plain TCP streams. It can work with pretty much any protocol you can think of, including SSL. This can be very useful if you need to connect systems that don’t agree about encryption: one side requires an unencrypted connection, while the other side requires an encrypted connection.

Or, in my case, I was using a client library that supported SSL connections, but our development server was using a self-signed certificate. Additionally, the client library did not provide an easy way to either trust the certificate or disable the verification.

Sidenote: a better solution would be to sign internal certificates with a private root certificate authority, and add that certificate to the system’s trusted certificate store. But sometimes you’re not in control of these things.

Since socat relays the contents of streams, it can easily listen on an unencrypted port and handle the SSL communication with a remote host. And, if necessary, certificate verification can be disabled.

socat TCP-LISTEN:8080 OPENSSL-CONNECT:some-remote-host:443,verify=0

Or when used in the opposite direction, strip SSL before handing it off to another service.

socat OPENSSL-LISTEN:4000,cert=my.crt TCP-CONNECT:other-host:5000

Note that in this last case if you want socat to act as an SSL server you’ll need to generate an x509 certificate first.

Simple Web Server

Getting to the “cat” part of socat’s name, it can also read and write files. For example, you can run a dead-simple web server that only serves one file. Say you wanted to serve a proxy auto-config file. You could use a file: URL scheme, but some clients may ignore the file if it doesn’t have the right MIME type. You can craft a file (I’ll call it pac.response) that contains the full HTTP response:

HTTP/1.1 200 OK
Content-type: application/x-ns-proxy-autoconfig
Connection: close

function FindProxyForURL(url, host) {
  // Configure your proxy here
}

Then have socat return the file contents whenever a client connects.

socat -U TCP-LISTEN:4000,fork,reuseaddr OPEN:'pac.response'

This command introduces socat command-line switch that is important for this use case. We need to use the -U switch for a couple reasons. For some context, there is a similar -u switch that puts socat into unidirectional mode (which is more like how netcat operates), meaning that it will only use the first stream for reading and the second stream for writing. We want to only read from the file and only write to the network stream. But it would not work if we wrote it like this:

socat -u OPEN:'pac.response' TCP-LISTEN:4000,fork,reuseaddr

This is because socat always processes the addresses in order from left to right. So the above command would read the file to the end and serve it to the first client that connected. Subsequent client connections would get nothing because the file position would be stuck at the end of the file (socat does not open the file again or return to the start for each connection).

So what we have to do is use the -U flag, which is unidirectional in reverse order. The TCP-LISTEN socket needs to be specified first, because then socat will open the file anew for each connection.

Transparent Connection Queue

I once worked with a system whose server had a very low limit on concurrent connections. But during development, the number of short-lived client requests would easily overwhelm this limit. When the limit was reached, new connections were simply refused by the server.

To get around this, I used a command like the following to set up a transparent connection queue.

socat TCP-LISTEN:4000,fork,reuseaddr,max-children=4,backlog=20 TCP:remote-host:5000

This is basically port forwarding but with some limitations on concurrent connections. Only four connections are allowed through at a time, and socat will queue up to 20 connections before starting to refuse new connections. So to clients who try to connect and end up queued, it just looks like the request took longer.

A Lot More Uses

Socat has a lot more functionality than I was able to cover here, and I’m constantly finding new uses for it. Let me know what else you use socat for.

Conversation

Join the conversation

Your email address will not be published. Required fields are marked *