Many web browsers support persistent connections in the form of
WebSockets. However, there is a protocol overhead in using these
which means it is incompatible with existing TCP/IP daemons expecting
a standard stream.
WsStent in essence, provides a proxy to bridge between these protocols
and tunnel a connection to a standard network daemon. An overview of
its features are as follows:
- SSL sockets for https and wss connections.
- Simple web server to provide application payload.
- Exceptionally safe due to libstent at its core.
- Non-blocking sockets for platforms unsuited for multi-threading.
- C89, C99, C11, C17, C2x standards support.
- Minimal dependencies (OpenSSL and POSIX).
- Simple echo mode to help debugging and setting up.
The ability of ANSI C to interact with raw memory is extremely important,
however this can also lead to a wide range of memory errors which can be
difficult to identify. The libstent library solves
a number of these risks in a fairly innovative way and this project is
entirely bound to it. As such, the code may look considerably different
to idiomatic C. In the debug profile, the following benefits are provided:
- Eliminated dangling pointers and use-after-free.
- Memory leaks are fully tracked.
- Type-safe vectors used rather than raw arrays.
- Safe string alternative is used in place of NULL terminated strings.
- All structures and variables are zero initialized.
- Malloc, calloc, realloc resulting in raw memory are not used directly.
- Non-blocking sockets rather than a potential for race conditions or other threading issues.
The build system is intentionally kept very minimal. In general the
following command should compile the wsstent binary, as well as creating
a test certificate (and key) for SSL:
Various convenience rules are added to the Makefile:
$ make test_echo # Run the simple echo server on localhost
$ make test # Run a test TCP proxy server (ws:8080 -> tcp:8000)
$ make test_server # Run a simple TCP server via netcat (port 8000)
If you run the compiled wsstent binary with no arguments, you will be
greeted with the usage instructions.
Note: You will notice that in the default build, these usage
instructions are followed with many notifications detailing information about
leaked memory. This demonstrates the leak checking done by
libstent with the purposefully raw
exit(0) of the
application. This will be the only instance where the program should leak.
Generally there are two modes, the simple echo server and the TCP
proxy. The echo server returns a copy of any message sent to it whereas
the TCP server provides the main functionality of the proxy, forwarding
communication between the browser and the TCP server.
Note: The echo server is intended only for debugging and as part
of setting up your server. In particular, wsstent will cleanly exit when
the message BYE is sent to it (to help with testing).
It is useful to first run the echo server to make sure things are
working. To do this, run:
$ wsstent --port 8080 --webroot www --cert cert.pem --key key.pem
Because we have not specified the --tunnelport and --tunnelhost
arguments, it will fall back to the echo server running on port 8080
and serving files from the www directory.
If you now navigate to https://localhost:8080, you should see the test
page so you can use it to confirm that this part is working.
With that in place, the next step is to run it in TCP proxy mode. This
is the following command:
$ wsstent --port 8080 --webroot www --cert cert.pem --key key.pem \
--tunnelhost localhost --tunnelport 8000
This will listen on port 8080 and forward traffic to TCP port 8000
If you attempt to connect using the test page, it should immediately
disconnect. This is because it could not connect to the TCP end
point. Lets run a quick test server via netcat.
$ nc -l 8000
With both wsstent and netcat running, you should now be able to connect
via the test page and send data through to the netcat instance. Messages
typed in netcat should also send back through to the test page and
display in the log.
Perhaps have a look through the code to get a feel
for how libstent works. For example,
Client.c deals with
the sending and receiving of buffers from the WebSocket client.
This demonstrates the usage of the type-safe vector container rather
than needing to deal with raw memory. There should actually be no
memory unsafe code in this file. Also in this file you will notice
that some variables get set to NULL once they are destroyed. Do take
this opportunity to comment out these NULL assignments to see how
libstent alerts you to dangling pointers.
There are some limitations to the software that you may be interested
to know about. Though these should not affect its general purpose.
- Supported HTTP standards are intentionally kept to the bare minimum required to fulfil the task.
- Usage of raw C APIs (SSL, POSIX/BSD Sockets). Other safe languages such as Rust create safe bindings around them and encapsulate the unsafe code in these. The equivalent in this project to these *-sys bindings are unsafe wrappers i.e. SslSocket.c.
- Because TCP streams don't always arrive as individual messages (due to i.e. Nagle's Algorithm), messages sent from WebSocket may be split up into smaller messages or combined into larger ones. Typically the TCP daemon endpoint will be handling this correctly anyway with its own higher level protocol.