Build Your Own Socat
This challenge is to build your own version of the Unix command line tool socat
. It’s name is a combination of the words SOcket CAT as it is in effect a version of cat
that works over socks too.
Socat
is a command line utility that establishes two bidirectional byte streams and transfers data between them. Because the streams can be constructed from a large set of different types of data sinks and sources. That can include:
- Files
- Pipes
- Devices (serial line, pseudo-terminal, etc)
- Sockets (UNIX, IP4, IP6 - raw, UDP, TCP)
- SSL sockets
- Proxy CONNECT connections
Basically anything Unix like operating systems treat as a file.
The Challenge - Building Socat
In this coding challenge we’re going to build a clone of the socat
tool that can establish bidirectional streams between files, sockets and pipes.
We can learn more about socat
using the man socat
command:
Socat is a command line based utility that establishes two bidirectional byte
streams and transfers data between them. Because the streams can be constructed
from a large set of different types of data sinks and sources (see address types),
and because lots of address options may be applied to the streams, socat can be
used for many different purposes.
There are lot of options for the socat
tool, so do refer back to the man page as you need to throughout the coding challenge.
Step Zero
As I have a background working in C and C-like programming languages, Coding Challenges is zero indexed. As always in step zero your goal is to setup your programming environment of choice, sit, stand or lie in wherever you code best, accompanied by your beverage and/or fuel of choice!
Step 1
In this step your goal is to support forwarding a local port to a remote host and port. With socat
the command line to do that looks like this:
% socat - TCP4:eu.httpbin.org:80
This is going to open a TCP connection to the defined host and port.
We can check it’s worked by sending a hand-crafted HTTP request like so:
% socat - TCP4:eu.httpbin.org:80
GET /get HTTP/1.1
Host: eu.httpbin.org
Accept: */*
Be sure to send two newlines after the Accept
line. If it’s working you’ll see a response like this:
% socat - TCP4:eu.httpbin.org:80
GET /get HTTP/1.1
Host: eu.httpbin.org
Accept: */*
HTTP/1.1 200 OK
Date: Wed, 04 Dec 2024 15:14:35 GMT
Content-Type: application/json
Content-Length: 225
Connection: keep-alive
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "eu.httpbin.org",
"X-Amzn-Trace-Id": "Root=1-675071db-6bd70b51268fe7614cf2c9ff"
},
"origin": "90.242.53.77",
"url": "http://eu.httpbin.org/get"
}
OK, we won’t normally need to hand-craft HTTP requests, but sometimes it’s fun to understand how these things work. If you want to really dig into HTTP, check out RFC9110.
Step 2
In this step your goal is to support writing to a file. The command line for this looks like:
socat -u STDIO FILE:test.txt,create
To test it we can write to the file and check the file then contains what we want:
% socat -u STDIO FILE:test.txt,create
Hello Coding Challenges
% cat test.txt
Hello Coding Challenges
Note you’ll have to send an end of file to socat
to terminate the program. If you’re on a Unix-like operating system you can do that by entering: CTRL-D
. We’ve used cat to check the file contains the content we entered. By the way building your own cat is another Coding Challenge!
Step 3
In this step your goal is to extend your socat
clone to have enough features to support creating a simple network message collector. To do that you need to support the following command line:
% socat -u TCP4-LISTEN:3333,reuseaddr,fork OPEN:./test.log,creat,append
This will set up socat
to listen for TCP connections on port 3333, and fork a new instance for every incoming connection. Using this we can use socat
as a simple log aggregator, sending logs from one or more servers to a central server.
To check it’s working run your socat
and then use netcat
to send mock log files to it like so:
% for i in {1..10} ; do echo "Hello, Coding Challenges Log Message $i" | nc localhost 3333 -c; done
Your log file should then contain:
Hello, Coding Challenges Log Message 1
Hello, Coding Challenges Log Message 2
Hello, Coding Challenges Log Message 3
Hello, Coding Challenges Log Message 4
Hello, Coding Challenges Log Message 5
Hello, Coding Challenges Log Message 6
Hello, Coding Challenges Log Message 7
Hello, Coding Challenges Log Message 8
Hello, Coding Challenges Log Message 9
Hello, Coding Challenges Log Message 10
By the way, build your own netcat is also a coding challenge!
Step 4
In this step your goal is to support the exec option so we use it to follow a log file on a server and send it to the message collector. Once you’ve done that you should be able to start the message collector as before:
% socat -u TCP4-LISTEN:3333,reuseaddr,fork OPEN:./test.log,create,append
Start socat tailing the ‘log’ file we’re using to test:
% socat -u EXEC:'tail -f test.txt' TCP4:localhost:3333
And in another terminal simulate log messages being written to the file, i.e.:
% echo "Coding Challenges Log Message" > test.txt
% echo "Another Coding Challenges Log Message" > test.txt
Once that works, congratulations you’ve built quite a useful tool and learned about forking processes, reading and writing files as well as TCP connections in your programming language of choice!
Going Further
There are many other options for socat
. Have a read of the manual and see what else it can do. If you’ve never used UDP before that might be an interesting place to start.
Help Others by Sharing Your Solutions!
If you think your solution is an example other developers can learn from please share it, put it on GitHub, GitLab or elsewhere. Then let me know - ping me a message on the Discord Server, via Twitter or LinkedIn or just post about it there and tag me. Alternately please add a link to it in the Coding Challenges Shared Solutions Github repo.
Get The Challenges By Email
If you would like to recieve the coding challenges by email, you can subscribe to the weekly newsletter on SubStack here: