Tunnel is a small SSL server (35K stripped) which decrypts and sends all plaintext bytes to the configured destination.
Tunnel has:
- CyaSSL for SSL support.
- libevent for fast event-driven (non-blocking) I/O.
- pthreads for separate event loops on each core.
- syslog for logging.
- configuration via a simple .ini file.
- Shared instance reference counts for memory management.
Tunnel uses a modern event-based server architecture known as an "event pump", or "readiness change notification loop". This is implemented in a cross-platform manner using libevent.
In contrast with Tunnel, most classic server architectures use the fork() or worker-thread-per-client model. Every new socket connection is handed off to a thread that is dedicated to that particular client for the life of the connection.
This classic design is common because it allows for the use of blocking I/O, which is easier and less error-prone than non-blocking I/O, and because it minimizes the number of sockets that any given worker thread needs to poll for readiness (using select() or poll()). Sometimes polling does use non-blocking I/O, with one thread handling many socket connections (but still handing off buffered bytes to worker threads for processing).
Tunnel uses only a single thread (per CPU core -- more on that below). This event loop handles all non-blocking socket I/O, processing bytes as soon as they're ready without the need for performance-killing context switches or mutex contention.
This is possible thanks to newer features available in modern Unix kernels: epoll() on Linux, kqueue on BSD, and /dev/poll on Solaris. These features allow the kernel to notify the program when a socket is ready, instead of having the program poll() the large list of connected clients repeatedly. This provides substantial performance gains over select() and poll(): several hundred times faster socket I/O, and several thousand times faster on busy servers.
The library libevent abstracts the underlying OS-specific kernel feature into a cross-platform API, so Tunnel should run on any platform supported by CyaSSL, pthreads, libevent, and syslog. (Note, Windows will need to use a wrapper library for pthreads and syslog, and libevent will fall back to select().)
The one drawback of most event pump servers (like thttpd, lighttpd, snap-server, etc.) is that there is only a single event pump loop, meaning, on a multi-core CPU, only one core gets utilitized at a time. Tunnel fixes this by having multiple event-pump threads -- optimally, one thread per core. When a new connection comes in, it is handed off to an event-pump thread which multiplexes the new client (along with many others) for the life of the connection.
# Install build-essential, pthreads, and libevent:
sudo apt-get install build-essential checkinstall \
libpthread-stubs0 libpthread-stubs0-dev libevent-dev
# Install CyaSSL (due to Launchpad Bug #624840):
cd ./third-party/cyassl-2.9.4/
./configure
make
sudo make install # Puts it into /usr/local/ by default.
cd ../../
# Build Tunnel:
cd ./src # into ./tunnel/src/
make
cd .. # back to ./tunnel
# Edit tunnel.ini to taste. Comments within.
gedit ./tunnel.ini # Yeah, that's right. Gedit. Punk.
# Finally, run it:
./tunnel
Daemonization is not done yet (it's on the task list). For now it can be run using your favorite daemonizer, such as 'screen'.
If you need a plaintext server to test against there is a trivial HTTP server (in Python) in ./tools/.
cd ./tools/
./simple_http_server.sh
Entry | Description |
---|---|
archive | Old files to delete soon. Currently has the original design notes. |
src | Tunnel source code. (The meat. Or tofu, if that's how you roll.) |
third-party | Library source from other distributors. |
tools | Tools and utilities for development and testing |
*.pem | SSL certs. (These are the demo files from WolfSSL.) |
tunnel.ini | The configuration file. Edit this before running ./tunnel. |
tunnel | ELF 64-bit LSB executable, x86-64. (For your convenience.) |
Tunnel is not ready for prime time. It's only had a few hours of work put into it. At this writing, it's only been barely tested with Firefox and Chrome.
For a commercial-grade solution, check out STunnel.
- Use libevent logging callback to pipe debug messages to syslog
- Unit tests (incl. real network testing on a server)
- daemonize(), incl. PID file
- SysV init scripts
- Set loglevel using the config file (tunnel.ini)
- Full doxygen coverage
- Static analysis / valgrind
- Stress/performance testing
- Packaging / signed binaries