6.2 KiB
SecLink Proxy -- Linux Serial Bridge
Linux-hosted proxy that bridges an 86Box emulated serial port to a remote telnet BBS. Part of the DVX GUI project.
The 86Box side communicates using the SecLink protocol (HDLC packet framing, CRC-16, DH key exchange, XTEA-CTR encryption). The BBS side is plain telnet over TCP. All crypto is transparent to the BBS -- it sees a normal telnet client.
Architecture
86Box (DOS terminal) Remote BBS
| |
emulated modem telnet
| |
TCP:2323 TCP:23
| |
+--- secproxy ----------------------------------+
secLink <-> plaintext
(encrypted, reliable)
The proxy accepts a single TCP connection from 86Box, performs the SecLink handshake (Diffie-Hellman key exchange), then connects to the BBS. All traffic between 86Box and the proxy is encrypted via XTEA-CTR on channel 0. Traffic between the proxy and the BBS is unencrypted telnet with IAC negotiation handling.
Usage
secproxy [listen_port] [bbs_host] [bbs_port]
| Argument | Default | Description |
|---|---|---|
listen_port |
2323 | TCP port for 86Box connection |
bbs_host |
10.1.0.244 | BBS hostname or IP |
bbs_port |
2023 | BBS TCP port |
Examples:
secproxy # all defaults
secproxy 5000 # listen on port 5000
secproxy 2323 bbs.example.com 23 # different BBS
secproxy --help # show usage
Startup Sequence
- Listen on the configured TCP port.
- Wait for 86Box to connect (polls with Ctrl+C support).
- Map the TCP socket to COM0 via the socket shim.
- Seed the RNG from
/dev/urandom. - Open SecLink and perform the DH handshake (blocks until the DOS side completes its handshake).
- Wait for the terminal to send ENTER (so the user can confirm the connection is working before BBS output starts).
- Connect to the remote BBS.
- Enter the proxy loop.
Proxy Loop
The main loop uses poll() with a 10ms timeout to multiplex between
the two TCP connections:
- 86Box -> BBS:
secLinkPoll()reads from the 86Box socket via the socket shim, decrypts incoming packets, and the receive callback writes plaintext to the BBS socket. - BBS -> 86Box:
read()from the BBS socket, then the telnet filter strips IAC sequences, thensecLinkSend()encrypts and sends to 86Box via the socket shim. If the send window is full, the loop retries with ACK processing until the data goes through. - Maintenance:
secLinkPoll()also handles packet-layer retransmit timers on every iteration.
The proxy exits cleanly on Ctrl+C (SIGINT), SIGTERM, or when either side disconnects.
Telnet Negotiation
The proxy handles RFC 854 telnet IAC sequences from the BBS so they do not corrupt the terminal display. A state machine parser strips IAC sequences from the data stream, persisting state across TCP segment boundaries.
Accepted options:
- ECHO (option 1) -- server echoes characters
- SGA (option 3) -- suppress go-ahead for character-at-a-time mode
- TTYPE (option 24) -- terminal type negotiation
- NAWS (option 31) -- window size negotiation
All other options are refused. Subnegotiations (SB...SE) are consumed silently.
Socket Shim
The proxy reuses the same packet, security, and secLink source code as
the DOS build. A socket shim (sockShim.h / sockShim.c) provides
rs232-compatible functions backed by TCP sockets instead of UART
hardware:
| rs232 function | Socket shim behavior |
|---|---|
rs232Open() |
Validates socket assigned; ignores serial params |
rs232Close() |
Marks port closed (socket lifecycle is caller's) |
rs232Read() |
Non-blocking recv() with MSG_DONTWAIT |
rs232Write() |
Blocking send() loop with MSG_NOSIGNAL |
The shim maps COM port indices (0-3) to socket file descriptors via
sockShimSetFd(), which must be called before opening the SecLink
layer. Up to 4 ports are supported.
The Makefile uses -include sockShim.h when compiling the packet and
secLink layers, which defines RS232_H to prevent the real rs232.h
from being included.
DJGPP Stubs
DOS-specific headers required by the security library are replaced by
minimal stubs in stubs/:
| Stub | Replaces DJGPP header | Contents |
|---|---|---|
stubs/pc.h |
<pc.h> |
No-op definitions |
stubs/go32.h |
<go32.h> |
No-op definitions |
stubs/sys/farptr.h |
<sys/farptr.h> |
No-op definitions |
The security library's hardware entropy function returns zeros on
Linux, which is harmless since the proxy seeds the RNG from
/dev/urandom before the handshake.
86Box Configuration
Configure the 86Box serial port to connect to the proxy:
- In 86Box settings, set a COM port to TCP client mode pointing at the proxy's listen port (default 2323).
- Enable "No telnet negotiation" to send raw bytes.
- The DOS terminal application running inside 86Box uses SecLink over this serial port.
Building
make # builds ../bin/secproxy
make clean # removes objects and binary
Objects are placed in ../obj/proxy/, the binary in ../bin/.
Requires only a standard Linux C toolchain (gcc, libc). No external dependencies beyond the project's own packet, security, and secLink source files, which are compiled directly from their source directories.
Files
proxy/
proxy.c main proxy program
sockShim.h rs232-compatible socket API (header)
sockShim.c socket shim implementation
Makefile Linux native build
stubs/
pc.h stub for DJGPP <pc.h>
go32.h stub for DJGPP <go32.h>
sys/
farptr.h stub for DJGPP <sys/farptr.h>